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: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
This commit is contained in:
		
							parent
							
								
									2e8c04f757
								
							
						
					
					
						commit
						b34050fadb
					
				| @ -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; | ||||
| 		/* If the command is valid, move to the new address */ | ||||
| 		if (parse_xy(esc, &priv->addr.x, &priv->addr.y)) | ||||
| 			charlcd_gotoxy(lcd); | ||||
| 
 | ||||
| 		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; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		charlcd_gotoxy(lcd); | ||||
| 		/* Regardless of its validity, mark as processed */ | ||||
| 		processed = 1; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user