Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b34050fa authored by Miguel Ojeda's avatar Miguel Ojeda
Browse files

auxdisplay: charlcd: Fix and clean up handling of x/y commands



The current version is not parsing multiple x/y commands as the code
originally intended. On top of that, kstrtoul() expects
NULL-terminated strings. Finally, the code does two passes over
the string.

Some explanations about the supported syntax are added as well.

Cc: Willy Tarreau <w@1wt.eu>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Andy Shevchenko <andy.shevchenko@gmail.com>
Cc: Robert Abel <rabel@robertabel.eu>
Signed-off-by: default avatarMiguel Ojeda <miguel.ojeda.sandonis@gmail.com>
parent 2e8c04f7
Loading
Loading
Loading
Loading
+78 −17
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
 */

#include <linux/atomic.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
@@ -293,6 +294,79 @@ static int charlcd_init_display(struct charlcd *lcd)
	return 0;
}

/*
 * Parses an unsigned integer from a string, until a non-digit character
 * is found. The empty string is not accepted. No overflow checks are done.
 *
 * Returns whether the parsing was successful. Only in that case
 * the output parameters are written to.
 *
 * TODO: If the kernel adds an inplace version of kstrtoul(), this function
 * could be easily replaced by that.
 */
static bool parse_n(const char *s, unsigned long *res, const char **next_s)
{
	if (!isdigit(*s))
		return false;

	*res = 0;
	while (isdigit(*s)) {
		*res = *res * 10 + (*s - '0');
		++s;
	}

	*next_s = s;
	return true;
}

/*
 * Parses a movement command of the form "(.*);", where the group can be
 * any number of subcommands of the form "(x|y)[0-9]+".
 *
 * Returns whether the command is valid. The position arguments are
 * only written if the parsing was successful.
 *
 * For instance:
 *   - ";"          returns (<original x>, <original y>).
 *   - "x1;"        returns (1, <original y>).
 *   - "y2x1;"      returns (1, 2).
 *   - "x12y34x56;" returns (56, 34).
 *   - ""           fails.
 *   - "x"          fails.
 *   - "x;"         fails.
 *   - "x1"         fails.
 *   - "xy12;"      fails.
 *   - "x12yy12;"   fails.
 *   - "xx"         fails.
 */
static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)
{
	unsigned long new_x = *x;
	unsigned long new_y = *y;

	for (;;) {
		if (!*s)
			return false;

		if (*s == ';')
			break;

		if (*s == 'x') {
			if (!parse_n(s + 1, &new_x, &s))
				return false;
		} else if (*s == 'y') {
			if (!parse_n(s + 1, &new_y, &s))
				return false;
		} else {
			return false;
		}
	}

	*x = new_x;
	*y = new_y;
	return true;
}

/*
 * These are the file operation function for user access to /dev/lcd
 * This function can also be called from inside the kernel, by
@@ -471,24 +545,11 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
	}
	case 'x':	/* gotoxy : LxXXX[yYYY]; */
	case 'y':	/* gotoxy : LyYYY[xXXX]; */
		if (!strchr(esc, ';'))
			break;

		while (*esc) {
			if (*esc == 'x') {
				esc++;
				if (kstrtoul(esc, 10, &priv->addr.x) < 0)
					break;
			} else if (*esc == 'y') {
				esc++;
				if (kstrtoul(esc, 10, &priv->addr.y) < 0)
					break;
			} else {
				break;
			}
		}

		/* If the command is valid, move to the new address */
		if (parse_xy(esc, &priv->addr.x, &priv->addr.y))
			charlcd_gotoxy(lcd);

		/* Regardless of its validity, mark as processed */
		processed = 1;
		break;
	}