mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-11-04 02:11:25 +01:00 
			
		
		
		
	Report Coverity log: Destination buffer too small (STRING_OVERFLOW) string_overflow: You might overrun the 1024 byte destination string lastcommand by writing 1025 bytes from console_buffer Signed-off-by: Peng Fan <van.freenix@gmail.com> Cc: Heiko Schocher <hs@denx.de> Cc: Simon Glass <sjg@chromium.org> Cc: Tom Rini <trini@konsulko.com> Reviewed-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			340 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			340 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * (C) Copyright 2000
 | 
						|
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 | 
						|
 *
 | 
						|
 * Add to readline cmdline-editing by
 | 
						|
 * (C) Copyright 2005
 | 
						|
 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier:	GPL-2.0+
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <bootretry.h>
 | 
						|
#include <cli.h>
 | 
						|
#include <console.h>
 | 
						|
#include <linux/ctype.h>
 | 
						|
 | 
						|
#define DEBUG_PARSER	0	/* set to 1 to debug */
 | 
						|
 | 
						|
#define debug_parser(fmt, args...)		\
 | 
						|
	debug_cond(DEBUG_PARSER, fmt, ##args)
 | 
						|
 | 
						|
 | 
						|
int cli_simple_parse_line(char *line, char *argv[])
 | 
						|
{
 | 
						|
	int nargs = 0;
 | 
						|
 | 
						|
	debug_parser("%s: \"%s\"\n", __func__, line);
 | 
						|
	while (nargs < CONFIG_SYS_MAXARGS) {
 | 
						|
		/* skip any white space */
 | 
						|
		while (isblank(*line))
 | 
						|
			++line;
 | 
						|
 | 
						|
		if (*line == '\0') {	/* end of line, no more args	*/
 | 
						|
			argv[nargs] = NULL;
 | 
						|
			debug_parser("%s: nargs=%d\n", __func__, nargs);
 | 
						|
			return nargs;
 | 
						|
		}
 | 
						|
 | 
						|
		argv[nargs++] = line;	/* begin of argument string	*/
 | 
						|
 | 
						|
		/* find end of string */
 | 
						|
		while (*line && !isblank(*line))
 | 
						|
			++line;
 | 
						|
 | 
						|
		if (*line == '\0') {	/* end of line, no more args	*/
 | 
						|
			argv[nargs] = NULL;
 | 
						|
			debug_parser("parse_line: nargs=%d\n", nargs);
 | 
						|
			return nargs;
 | 
						|
		}
 | 
						|
 | 
						|
		*line++ = '\0';		/* terminate current arg	 */
 | 
						|
	}
 | 
						|
 | 
						|
	printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
 | 
						|
 | 
						|
	debug_parser("%s: nargs=%d\n", __func__, nargs);
 | 
						|
	return nargs;
 | 
						|
}
 | 
						|
 | 
						|
void cli_simple_process_macros(const char *input, char *output)
 | 
						|
{
 | 
						|
	char c, prev;
 | 
						|
	const char *varname_start = NULL;
 | 
						|
	int inputcnt = strlen(input);
 | 
						|
	int outputcnt = CONFIG_SYS_CBSIZE;
 | 
						|
	int state = 0;		/* 0 = waiting for '$'  */
 | 
						|
 | 
						|
	/* 1 = waiting for '(' or '{' */
 | 
						|
	/* 2 = waiting for ')' or '}' */
 | 
						|
	/* 3 = waiting for '''  */
 | 
						|
	char __maybe_unused *output_start = output;
 | 
						|
 | 
						|
	debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
 | 
						|
		     input);
 | 
						|
 | 
						|
	prev = '\0';		/* previous character   */
 | 
						|
 | 
						|
	while (inputcnt && outputcnt) {
 | 
						|
		c = *input++;
 | 
						|
		inputcnt--;
 | 
						|
 | 
						|
		if (state != 3) {
 | 
						|
			/* remove one level of escape characters */
 | 
						|
			if ((c == '\\') && (prev != '\\')) {
 | 
						|
				if (inputcnt-- == 0)
 | 
						|
					break;
 | 
						|
				prev = c;
 | 
						|
				c = *input++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		switch (state) {
 | 
						|
		case 0:	/* Waiting for (unescaped) $    */
 | 
						|
			if ((c == '\'') && (prev != '\\')) {
 | 
						|
				state = 3;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			if ((c == '$') && (prev != '\\')) {
 | 
						|
				state++;
 | 
						|
			} else {
 | 
						|
				*(output++) = c;
 | 
						|
				outputcnt--;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case 1:	/* Waiting for (        */
 | 
						|
			if (c == '(' || c == '{') {
 | 
						|
				state++;
 | 
						|
				varname_start = input;
 | 
						|
			} else {
 | 
						|
				state = 0;
 | 
						|
				*(output++) = '$';
 | 
						|
				outputcnt--;
 | 
						|
 | 
						|
				if (outputcnt) {
 | 
						|
					*(output++) = c;
 | 
						|
					outputcnt--;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case 2:	/* Waiting for )        */
 | 
						|
			if (c == ')' || c == '}') {
 | 
						|
				int i;
 | 
						|
				char envname[CONFIG_SYS_CBSIZE], *envval;
 | 
						|
				/* Varname # of chars */
 | 
						|
				int envcnt = input - varname_start - 1;
 | 
						|
 | 
						|
				/* Get the varname */
 | 
						|
				for (i = 0; i < envcnt; i++)
 | 
						|
					envname[i] = varname_start[i];
 | 
						|
				envname[i] = 0;
 | 
						|
 | 
						|
				/* Get its value */
 | 
						|
				envval = getenv(envname);
 | 
						|
 | 
						|
				/* Copy into the line if it exists */
 | 
						|
				if (envval != NULL)
 | 
						|
					while ((*envval) && outputcnt) {
 | 
						|
						*(output++) = *(envval++);
 | 
						|
						outputcnt--;
 | 
						|
					}
 | 
						|
				/* Look for another '$' */
 | 
						|
				state = 0;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case 3:	/* Waiting for '        */
 | 
						|
			if ((c == '\'') && (prev != '\\')) {
 | 
						|
				state = 0;
 | 
						|
			} else {
 | 
						|
				*(output++) = c;
 | 
						|
				outputcnt--;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		prev = c;
 | 
						|
	}
 | 
						|
 | 
						|
	if (outputcnt)
 | 
						|
		*output = 0;
 | 
						|
	else
 | 
						|
		*(output - 1) = 0;
 | 
						|
 | 
						|
	debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
 | 
						|
		     strlen(output_start), output_start);
 | 
						|
}
 | 
						|
 | 
						|
 /*
 | 
						|
 * WARNING:
 | 
						|
 *
 | 
						|
 * We must create a temporary copy of the command since the command we get
 | 
						|
 * may be the result from getenv(), which returns a pointer directly to
 | 
						|
 * the environment data, which may change magicly when the command we run
 | 
						|
 * creates or modifies environment variables (like "bootp" does).
 | 
						|
 */
 | 
						|
int cli_simple_run_command(const char *cmd, int flag)
 | 
						|
{
 | 
						|
	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
 | 
						|
	char *token;			/* start of token in cmdbuf	*/
 | 
						|
	char *sep;			/* end of token (separator) in cmdbuf */
 | 
						|
	char finaltoken[CONFIG_SYS_CBSIZE];
 | 
						|
	char *str = cmdbuf;
 | 
						|
	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
 | 
						|
	int argc, inquotes;
 | 
						|
	int repeatable = 1;
 | 
						|
	int rc = 0;
 | 
						|
 | 
						|
	debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
 | 
						|
	if (DEBUG_PARSER) {
 | 
						|
		/* use puts - string may be loooong */
 | 
						|
		puts(cmd ? cmd : "NULL");
 | 
						|
		puts("\"\n");
 | 
						|
	}
 | 
						|
	clear_ctrlc();		/* forget any previous Control C */
 | 
						|
 | 
						|
	if (!cmd || !*cmd)
 | 
						|
		return -1;	/* empty command */
 | 
						|
 | 
						|
	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
 | 
						|
		puts("## Command too long!\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	strcpy(cmdbuf, cmd);
 | 
						|
 | 
						|
	/* Process separators and check for invalid
 | 
						|
	 * repeatable commands
 | 
						|
	 */
 | 
						|
 | 
						|
	debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
 | 
						|
	while (*str) {
 | 
						|
		/*
 | 
						|
		 * Find separator, or string end
 | 
						|
		 * Allow simple escape of ';' by writing "\;"
 | 
						|
		 */
 | 
						|
		for (inquotes = 0, sep = str; *sep; sep++) {
 | 
						|
			if ((*sep == '\'') &&
 | 
						|
			    (*(sep - 1) != '\\'))
 | 
						|
				inquotes = !inquotes;
 | 
						|
 | 
						|
			if (!inquotes &&
 | 
						|
			    (*sep == ';') &&	/* separator		*/
 | 
						|
			    (sep != str) &&	/* past string start	*/
 | 
						|
			    (*(sep - 1) != '\\'))	/* and NOT escaped */
 | 
						|
				break;
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Limit the token to data between separators
 | 
						|
		 */
 | 
						|
		token = str;
 | 
						|
		if (*sep) {
 | 
						|
			str = sep + 1;	/* start of command for next pass */
 | 
						|
			*sep = '\0';
 | 
						|
		} else {
 | 
						|
			str = sep;	/* no more commands for next pass */
 | 
						|
		}
 | 
						|
		debug_parser("token: \"%s\"\n", token);
 | 
						|
 | 
						|
		/* find macros in this token and replace them */
 | 
						|
		cli_simple_process_macros(token, finaltoken);
 | 
						|
 | 
						|
		/* Extract arguments */
 | 
						|
		argc = cli_simple_parse_line(finaltoken, argv);
 | 
						|
		if (argc == 0) {
 | 
						|
			rc = -1;	/* no command at all */
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (cmd_process(flag, argc, argv, &repeatable, NULL))
 | 
						|
			rc = -1;
 | 
						|
 | 
						|
		/* Did the user stop this? */
 | 
						|
		if (had_ctrlc())
 | 
						|
			return -1;	/* if stopped then not repeatable */
 | 
						|
	}
 | 
						|
 | 
						|
	return rc ? rc : repeatable;
 | 
						|
}
 | 
						|
 | 
						|
void cli_simple_loop(void)
 | 
						|
{
 | 
						|
	static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
 | 
						|
 | 
						|
	int len;
 | 
						|
	int flag;
 | 
						|
	int rc = 1;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		if (rc >= 0) {
 | 
						|
			/* Saw enough of a valid command to
 | 
						|
			 * restart the timeout.
 | 
						|
			 */
 | 
						|
			bootretry_reset_cmd_timeout();
 | 
						|
		}
 | 
						|
		len = cli_readline(CONFIG_SYS_PROMPT);
 | 
						|
 | 
						|
		flag = 0;	/* assume no special flags for now */
 | 
						|
		if (len > 0)
 | 
						|
			strlcpy(lastcommand, console_buffer,
 | 
						|
				CONFIG_SYS_CBSIZE + 1);
 | 
						|
		else if (len == 0)
 | 
						|
			flag |= CMD_FLAG_REPEAT;
 | 
						|
#ifdef CONFIG_BOOT_RETRY_TIME
 | 
						|
		else if (len == -2) {
 | 
						|
			/* -2 means timed out, retry autoboot
 | 
						|
			 */
 | 
						|
			puts("\nTimed out waiting for command\n");
 | 
						|
# ifdef CONFIG_RESET_TO_RETRY
 | 
						|
			/* Reinit board to run initialization code again */
 | 
						|
			do_reset(NULL, 0, 0, NULL);
 | 
						|
# else
 | 
						|
			return;		/* retry autoboot */
 | 
						|
# endif
 | 
						|
		}
 | 
						|
#endif
 | 
						|
 | 
						|
		if (len == -1)
 | 
						|
			puts("<INTERRUPT>\n");
 | 
						|
		else
 | 
						|
			rc = run_command_repeatable(lastcommand, flag);
 | 
						|
 | 
						|
		if (rc <= 0) {
 | 
						|
			/* invalid command or not repeatable, forget it */
 | 
						|
			lastcommand[0] = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int cli_simple_run_command_list(char *cmd, int flag)
 | 
						|
{
 | 
						|
	char *line, *next;
 | 
						|
	int rcode = 0;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Break into individual lines, and execute each line; terminate on
 | 
						|
	 * error.
 | 
						|
	 */
 | 
						|
	next = cmd;
 | 
						|
	line = cmd;
 | 
						|
	while (*next) {
 | 
						|
		if (*next == '\n') {
 | 
						|
			*next = '\0';
 | 
						|
			/* run only non-empty commands */
 | 
						|
			if (*line) {
 | 
						|
				debug("** exec: \"%s\"\n", line);
 | 
						|
				if (cli_simple_run_command(line, 0) < 0) {
 | 
						|
					rcode = 1;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			line = next + 1;
 | 
						|
		}
 | 
						|
		++next;
 | 
						|
	}
 | 
						|
	if (rcode == 0 && *line)
 | 
						|
		rcode = (cli_simple_run_command(line, 0) < 0);
 | 
						|
 | 
						|
	return rcode;
 | 
						|
}
 |