Skip to content

Commit b34050f

Browse files
committed
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 <[email protected]> Cc: Geert Uytterhoeven <[email protected]> Cc: Andy Shevchenko <[email protected]> Cc: Robert Abel <[email protected]> Signed-off-by: Miguel Ojeda <[email protected]>
1 parent 2e8c04f commit b34050f

File tree

1 file changed

+78
-17
lines changed

1 file changed

+78
-17
lines changed

drivers/auxdisplay/charlcd.c

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212

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

297+
/*
298+
* Parses an unsigned integer from a string, until a non-digit character
299+
* is found. The empty string is not accepted. No overflow checks are done.
300+
*
301+
* Returns whether the parsing was successful. Only in that case
302+
* the output parameters are written to.
303+
*
304+
* TODO: If the kernel adds an inplace version of kstrtoul(), this function
305+
* could be easily replaced by that.
306+
*/
307+
static bool parse_n(const char *s, unsigned long *res, const char **next_s)
308+
{
309+
if (!isdigit(*s))
310+
return false;
311+
312+
*res = 0;
313+
while (isdigit(*s)) {
314+
*res = *res * 10 + (*s - '0');
315+
++s;
316+
}
317+
318+
*next_s = s;
319+
return true;
320+
}
321+
322+
/*
323+
* Parses a movement command of the form "(.*);", where the group can be
324+
* any number of subcommands of the form "(x|y)[0-9]+".
325+
*
326+
* Returns whether the command is valid. The position arguments are
327+
* only written if the parsing was successful.
328+
*
329+
* For instance:
330+
* - ";" returns (<original x>, <original y>).
331+
* - "x1;" returns (1, <original y>).
332+
* - "y2x1;" returns (1, 2).
333+
* - "x12y34x56;" returns (56, 34).
334+
* - "" fails.
335+
* - "x" fails.
336+
* - "x;" fails.
337+
* - "x1" fails.
338+
* - "xy12;" fails.
339+
* - "x12yy12;" fails.
340+
* - "xx" fails.
341+
*/
342+
static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)
343+
{
344+
unsigned long new_x = *x;
345+
unsigned long new_y = *y;
346+
347+
for (;;) {
348+
if (!*s)
349+
return false;
350+
351+
if (*s == ';')
352+
break;
353+
354+
if (*s == 'x') {
355+
if (!parse_n(s + 1, &new_x, &s))
356+
return false;
357+
} else if (*s == 'y') {
358+
if (!parse_n(s + 1, &new_y, &s))
359+
return false;
360+
} else {
361+
return false;
362+
}
363+
}
364+
365+
*x = new_x;
366+
*y = new_y;
367+
return true;
368+
}
369+
296370
/*
297371
* These are the file operation function for user access to /dev/lcd
298372
* 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)
471545
}
472546
case 'x': /* gotoxy : LxXXX[yYYY]; */
473547
case 'y': /* gotoxy : LyYYY[xXXX]; */
474-
if (!strchr(esc, ';'))
475-
break;
476-
477-
while (*esc) {
478-
if (*esc == 'x') {
479-
esc++;
480-
if (kstrtoul(esc, 10, &priv->addr.x) < 0)
481-
break;
482-
} else if (*esc == 'y') {
483-
esc++;
484-
if (kstrtoul(esc, 10, &priv->addr.y) < 0)
485-
break;
486-
} else {
487-
break;
488-
}
489-
}
548+
/* If the command is valid, move to the new address */
549+
if (parse_xy(esc, &priv->addr.x, &priv->addr.y))
550+
charlcd_gotoxy(lcd);
490551

491-
charlcd_gotoxy(lcd);
552+
/* Regardless of its validity, mark as processed */
492553
processed = 1;
493554
break;
494555
}

0 commit comments

Comments
 (0)