Home | History | Annotate | Download | only in common
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * (C) Copyright 2000
      4  * Wolfgang Denk, DENX Software Engineering, wd (at) denx.de.
      5  *
      6  * Add to readline cmdline-editing by
      7  * (C) Copyright 2005
      8  * JinHua Luo, GuangDong Linux Center, <luo.jinhua (at) gd-linux.com>
      9  */
     10 
     11 #include <common.h>
     12 #include <bootretry.h>
     13 #include <cli.h>
     14 #include <console.h>
     15 #include <linux/ctype.h>
     16 
     17 #define DEBUG_PARSER	0	/* set to 1 to debug */
     18 
     19 #define debug_parser(fmt, args...)		\
     20 	debug_cond(DEBUG_PARSER, fmt, ##args)
     21 
     22 
     23 int cli_simple_parse_line(char *line, char *argv[])
     24 {
     25 	int nargs = 0;
     26 
     27 	debug_parser("%s: \"%s\"\n", __func__, line);
     28 	while (nargs < CONFIG_SYS_MAXARGS) {
     29 		/* skip any white space */
     30 		while (isblank(*line))
     31 			++line;
     32 
     33 		if (*line == '\0') {	/* end of line, no more args	*/
     34 			argv[nargs] = NULL;
     35 			debug_parser("%s: nargs=%d\n", __func__, nargs);
     36 			return nargs;
     37 		}
     38 
     39 		argv[nargs++] = line;	/* begin of argument string	*/
     40 
     41 		/* find end of string */
     42 		while (*line && !isblank(*line))
     43 			++line;
     44 
     45 		if (*line == '\0') {	/* end of line, no more args	*/
     46 			argv[nargs] = NULL;
     47 			debug_parser("parse_line: nargs=%d\n", nargs);
     48 			return nargs;
     49 		}
     50 
     51 		*line++ = '\0';		/* terminate current arg	 */
     52 	}
     53 
     54 	printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
     55 
     56 	debug_parser("%s: nargs=%d\n", __func__, nargs);
     57 	return nargs;
     58 }
     59 
     60 void cli_simple_process_macros(const char *input, char *output)
     61 {
     62 	char c, prev;
     63 	const char *varname_start = NULL;
     64 	int inputcnt = strlen(input);
     65 	int outputcnt = CONFIG_SYS_CBSIZE;
     66 	int state = 0;		/* 0 = waiting for '$'  */
     67 
     68 	/* 1 = waiting for '(' or '{' */
     69 	/* 2 = waiting for ')' or '}' */
     70 	/* 3 = waiting for '''  */
     71 	char __maybe_unused *output_start = output;
     72 
     73 	debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
     74 		     input);
     75 
     76 	prev = '\0';		/* previous character   */
     77 
     78 	while (inputcnt && outputcnt) {
     79 		c = *input++;
     80 		inputcnt--;
     81 
     82 		if (state != 3) {
     83 			/* remove one level of escape characters */
     84 			if ((c == '\\') && (prev != '\\')) {
     85 				if (inputcnt-- == 0)
     86 					break;
     87 				prev = c;
     88 				c = *input++;
     89 			}
     90 		}
     91 
     92 		switch (state) {
     93 		case 0:	/* Waiting for (unescaped) $    */
     94 			if ((c == '\'') && (prev != '\\')) {
     95 				state = 3;
     96 				break;
     97 			}
     98 			if ((c == '$') && (prev != '\\')) {
     99 				state++;
    100 			} else {
    101 				*(output++) = c;
    102 				outputcnt--;
    103 			}
    104 			break;
    105 		case 1:	/* Waiting for (        */
    106 			if (c == '(' || c == '{') {
    107 				state++;
    108 				varname_start = input;
    109 			} else {
    110 				state = 0;
    111 				*(output++) = '$';
    112 				outputcnt--;
    113 
    114 				if (outputcnt) {
    115 					*(output++) = c;
    116 					outputcnt--;
    117 				}
    118 			}
    119 			break;
    120 		case 2:	/* Waiting for )        */
    121 			if (c == ')' || c == '}') {
    122 				int i;
    123 				char envname[CONFIG_SYS_CBSIZE], *envval;
    124 				/* Varname # of chars */
    125 				int envcnt = input - varname_start - 1;
    126 
    127 				/* Get the varname */
    128 				for (i = 0; i < envcnt; i++)
    129 					envname[i] = varname_start[i];
    130 				envname[i] = 0;
    131 
    132 				/* Get its value */
    133 				envval = env_get(envname);
    134 
    135 				/* Copy into the line if it exists */
    136 				if (envval != NULL)
    137 					while ((*envval) && outputcnt) {
    138 						*(output++) = *(envval++);
    139 						outputcnt--;
    140 					}
    141 				/* Look for another '$' */
    142 				state = 0;
    143 			}
    144 			break;
    145 		case 3:	/* Waiting for '        */
    146 			if ((c == '\'') && (prev != '\\')) {
    147 				state = 0;
    148 			} else {
    149 				*(output++) = c;
    150 				outputcnt--;
    151 			}
    152 			break;
    153 		}
    154 		prev = c;
    155 	}
    156 
    157 	if (outputcnt)
    158 		*output = 0;
    159 	else
    160 		*(output - 1) = 0;
    161 
    162 	debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
    163 		     strlen(output_start), output_start);
    164 }
    165 
    166  /*
    167  * WARNING:
    168  *
    169  * We must create a temporary copy of the command since the command we get
    170  * may be the result from env_get(), which returns a pointer directly to
    171  * the environment data, which may change magicly when the command we run
    172  * creates or modifies environment variables (like "bootp" does).
    173  */
    174 int cli_simple_run_command(const char *cmd, int flag)
    175 {
    176 	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
    177 	char *token;			/* start of token in cmdbuf	*/
    178 	char *sep;			/* end of token (separator) in cmdbuf */
    179 	char finaltoken[CONFIG_SYS_CBSIZE];
    180 	char *str = cmdbuf;
    181 	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
    182 	int argc, inquotes;
    183 	int repeatable = 1;
    184 	int rc = 0;
    185 
    186 	debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
    187 	if (DEBUG_PARSER) {
    188 		/* use puts - string may be loooong */
    189 		puts(cmd ? cmd : "NULL");
    190 		puts("\"\n");
    191 	}
    192 	clear_ctrlc();		/* forget any previous Control C */
    193 
    194 	if (!cmd || !*cmd)
    195 		return -1;	/* empty command */
    196 
    197 	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
    198 		puts("## Command too long!\n");
    199 		return -1;
    200 	}
    201 
    202 	strcpy(cmdbuf, cmd);
    203 
    204 	/* Process separators and check for invalid
    205 	 * repeatable commands
    206 	 */
    207 
    208 	debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
    209 	while (*str) {
    210 		/*
    211 		 * Find separator, or string end
    212 		 * Allow simple escape of ';' by writing "\;"
    213 		 */
    214 		for (inquotes = 0, sep = str; *sep; sep++) {
    215 			if ((*sep == '\'') &&
    216 			    (*(sep - 1) != '\\'))
    217 				inquotes = !inquotes;
    218 
    219 			if (!inquotes &&
    220 			    (*sep == ';') &&	/* separator		*/
    221 			    (sep != str) &&	/* past string start	*/
    222 			    (*(sep - 1) != '\\'))	/* and NOT escaped */
    223 				break;
    224 		}
    225 
    226 		/*
    227 		 * Limit the token to data between separators
    228 		 */
    229 		token = str;
    230 		if (*sep) {
    231 			str = sep + 1;	/* start of command for next pass */
    232 			*sep = '\0';
    233 		} else {
    234 			str = sep;	/* no more commands for next pass */
    235 		}
    236 		debug_parser("token: \"%s\"\n", token);
    237 
    238 		/* find macros in this token and replace them */
    239 		cli_simple_process_macros(token, finaltoken);
    240 
    241 		/* Extract arguments */
    242 		argc = cli_simple_parse_line(finaltoken, argv);
    243 		if (argc == 0) {
    244 			rc = -1;	/* no command at all */
    245 			continue;
    246 		}
    247 
    248 		if (cmd_process(flag, argc, argv, &repeatable, NULL))
    249 			rc = -1;
    250 
    251 		/* Did the user stop this? */
    252 		if (had_ctrlc())
    253 			return -1;	/* if stopped then not repeatable */
    254 	}
    255 
    256 	return rc ? rc : repeatable;
    257 }
    258 
    259 void cli_simple_loop(void)
    260 {
    261 	static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
    262 
    263 	int len;
    264 	int flag;
    265 	int rc = 1;
    266 
    267 	for (;;) {
    268 		if (rc >= 0) {
    269 			/* Saw enough of a valid command to
    270 			 * restart the timeout.
    271 			 */
    272 			bootretry_reset_cmd_timeout();
    273 		}
    274 		len = cli_readline(CONFIG_SYS_PROMPT);
    275 
    276 		flag = 0;	/* assume no special flags for now */
    277 		if (len > 0)
    278 			strlcpy(lastcommand, console_buffer,
    279 				CONFIG_SYS_CBSIZE + 1);
    280 		else if (len == 0)
    281 			flag |= CMD_FLAG_REPEAT;
    282 #ifdef CONFIG_BOOT_RETRY_TIME
    283 		else if (len == -2) {
    284 			/* -2 means timed out, retry autoboot
    285 			 */
    286 			puts("\nTimed out waiting for command\n");
    287 # ifdef CONFIG_RESET_TO_RETRY
    288 			/* Reinit board to run initialization code again */
    289 			do_reset(NULL, 0, 0, NULL);
    290 # else
    291 			return;		/* retry autoboot */
    292 # endif
    293 		}
    294 #endif
    295 
    296 		if (len == -1)
    297 			puts("<INTERRUPT>\n");
    298 		else
    299 			rc = run_command_repeatable(lastcommand, flag);
    300 
    301 		if (rc <= 0) {
    302 			/* invalid command or not repeatable, forget it */
    303 			lastcommand[0] = 0;
    304 		}
    305 	}
    306 }
    307 
    308 int cli_simple_run_command_list(char *cmd, int flag)
    309 {
    310 	char *line, *next;
    311 	int rcode = 0;
    312 
    313 	/*
    314 	 * Break into individual lines, and execute each line; terminate on
    315 	 * error.
    316 	 */
    317 	next = cmd;
    318 	line = cmd;
    319 	while (*next) {
    320 		if (*next == '\n') {
    321 			*next = '\0';
    322 			/* run only non-empty commands */
    323 			if (*line) {
    324 				debug("** exec: \"%s\"\n", line);
    325 				if (cli_simple_run_command(line, 0) < 0) {
    326 					rcode = 1;
    327 					break;
    328 				}
    329 			}
    330 			line = next + 1;
    331 		}
    332 		++next;
    333 	}
    334 	if (rcode == 0 && *line)
    335 		rcode = (cli_simple_run_command(line, 0) < 0);
    336 
    337 	return rcode;
    338 }
    339