xref: /openbmc/u-boot/common/cli_simple.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
26493ccc7SSimon Glass /*
36493ccc7SSimon Glass  * (C) Copyright 2000
46493ccc7SSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
56493ccc7SSimon Glass  *
66493ccc7SSimon Glass  * Add to readline cmdline-editing by
76493ccc7SSimon Glass  * (C) Copyright 2005
86493ccc7SSimon Glass  * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
96493ccc7SSimon Glass  */
106493ccc7SSimon Glass 
116493ccc7SSimon Glass #include <common.h>
120098e179SSimon Glass #include <bootretry.h>
136493ccc7SSimon Glass #include <cli.h>
1424b852a7SSimon Glass #include <console.h>
156493ccc7SSimon Glass #include <linux/ctype.h>
166493ccc7SSimon Glass 
176493ccc7SSimon Glass #define DEBUG_PARSER	0	/* set to 1 to debug */
186493ccc7SSimon Glass 
196493ccc7SSimon Glass #define debug_parser(fmt, args...)		\
206493ccc7SSimon Glass 	debug_cond(DEBUG_PARSER, fmt, ##args)
216493ccc7SSimon Glass 
226493ccc7SSimon Glass 
cli_simple_parse_line(char * line,char * argv[])23e1bf824dSSimon Glass int cli_simple_parse_line(char *line, char *argv[])
246493ccc7SSimon Glass {
256493ccc7SSimon Glass 	int nargs = 0;
266493ccc7SSimon Glass 
276493ccc7SSimon Glass 	debug_parser("%s: \"%s\"\n", __func__, line);
286493ccc7SSimon Glass 	while (nargs < CONFIG_SYS_MAXARGS) {
296493ccc7SSimon Glass 		/* skip any white space */
306493ccc7SSimon Glass 		while (isblank(*line))
316493ccc7SSimon Glass 			++line;
326493ccc7SSimon Glass 
336493ccc7SSimon Glass 		if (*line == '\0') {	/* end of line, no more args	*/
346493ccc7SSimon Glass 			argv[nargs] = NULL;
356493ccc7SSimon Glass 			debug_parser("%s: nargs=%d\n", __func__, nargs);
366493ccc7SSimon Glass 			return nargs;
376493ccc7SSimon Glass 		}
386493ccc7SSimon Glass 
396493ccc7SSimon Glass 		argv[nargs++] = line;	/* begin of argument string	*/
406493ccc7SSimon Glass 
416493ccc7SSimon Glass 		/* find end of string */
426493ccc7SSimon Glass 		while (*line && !isblank(*line))
436493ccc7SSimon Glass 			++line;
446493ccc7SSimon Glass 
456493ccc7SSimon Glass 		if (*line == '\0') {	/* end of line, no more args	*/
466493ccc7SSimon Glass 			argv[nargs] = NULL;
476493ccc7SSimon Glass 			debug_parser("parse_line: nargs=%d\n", nargs);
486493ccc7SSimon Glass 			return nargs;
496493ccc7SSimon Glass 		}
506493ccc7SSimon Glass 
516493ccc7SSimon Glass 		*line++ = '\0';		/* terminate current arg	 */
526493ccc7SSimon Glass 	}
536493ccc7SSimon Glass 
546493ccc7SSimon Glass 	printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
556493ccc7SSimon Glass 
566493ccc7SSimon Glass 	debug_parser("%s: nargs=%d\n", __func__, nargs);
576493ccc7SSimon Glass 	return nargs;
586493ccc7SSimon Glass }
596493ccc7SSimon Glass 
cli_simple_process_macros(const char * input,char * output)60a06be2d0SHans de Goede void cli_simple_process_macros(const char *input, char *output)
616493ccc7SSimon Glass {
626493ccc7SSimon Glass 	char c, prev;
636493ccc7SSimon Glass 	const char *varname_start = NULL;
646493ccc7SSimon Glass 	int inputcnt = strlen(input);
656493ccc7SSimon Glass 	int outputcnt = CONFIG_SYS_CBSIZE;
666493ccc7SSimon Glass 	int state = 0;		/* 0 = waiting for '$'  */
676493ccc7SSimon Glass 
686493ccc7SSimon Glass 	/* 1 = waiting for '(' or '{' */
696493ccc7SSimon Glass 	/* 2 = waiting for ')' or '}' */
706493ccc7SSimon Glass 	/* 3 = waiting for '''  */
7180402f34SHeiko Schocher 	char __maybe_unused *output_start = output;
726493ccc7SSimon Glass 
736493ccc7SSimon Glass 	debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
746493ccc7SSimon Glass 		     input);
756493ccc7SSimon Glass 
766493ccc7SSimon Glass 	prev = '\0';		/* previous character   */
776493ccc7SSimon Glass 
786493ccc7SSimon Glass 	while (inputcnt && outputcnt) {
796493ccc7SSimon Glass 		c = *input++;
806493ccc7SSimon Glass 		inputcnt--;
816493ccc7SSimon Glass 
826493ccc7SSimon Glass 		if (state != 3) {
836493ccc7SSimon Glass 			/* remove one level of escape characters */
846493ccc7SSimon Glass 			if ((c == '\\') && (prev != '\\')) {
856493ccc7SSimon Glass 				if (inputcnt-- == 0)
866493ccc7SSimon Glass 					break;
876493ccc7SSimon Glass 				prev = c;
886493ccc7SSimon Glass 				c = *input++;
896493ccc7SSimon Glass 			}
906493ccc7SSimon Glass 		}
916493ccc7SSimon Glass 
926493ccc7SSimon Glass 		switch (state) {
936493ccc7SSimon Glass 		case 0:	/* Waiting for (unescaped) $    */
946493ccc7SSimon Glass 			if ((c == '\'') && (prev != '\\')) {
956493ccc7SSimon Glass 				state = 3;
966493ccc7SSimon Glass 				break;
976493ccc7SSimon Glass 			}
986493ccc7SSimon Glass 			if ((c == '$') && (prev != '\\')) {
996493ccc7SSimon Glass 				state++;
1006493ccc7SSimon Glass 			} else {
1016493ccc7SSimon Glass 				*(output++) = c;
1026493ccc7SSimon Glass 				outputcnt--;
1036493ccc7SSimon Glass 			}
1046493ccc7SSimon Glass 			break;
1056493ccc7SSimon Glass 		case 1:	/* Waiting for (        */
1066493ccc7SSimon Glass 			if (c == '(' || c == '{') {
1076493ccc7SSimon Glass 				state++;
1086493ccc7SSimon Glass 				varname_start = input;
1096493ccc7SSimon Glass 			} else {
1106493ccc7SSimon Glass 				state = 0;
1116493ccc7SSimon Glass 				*(output++) = '$';
1126493ccc7SSimon Glass 				outputcnt--;
1136493ccc7SSimon Glass 
1146493ccc7SSimon Glass 				if (outputcnt) {
1156493ccc7SSimon Glass 					*(output++) = c;
1166493ccc7SSimon Glass 					outputcnt--;
1176493ccc7SSimon Glass 				}
1186493ccc7SSimon Glass 			}
1196493ccc7SSimon Glass 			break;
1206493ccc7SSimon Glass 		case 2:	/* Waiting for )        */
1216493ccc7SSimon Glass 			if (c == ')' || c == '}') {
1226493ccc7SSimon Glass 				int i;
1236493ccc7SSimon Glass 				char envname[CONFIG_SYS_CBSIZE], *envval;
1246493ccc7SSimon Glass 				/* Varname # of chars */
1256493ccc7SSimon Glass 				int envcnt = input - varname_start - 1;
1266493ccc7SSimon Glass 
1276493ccc7SSimon Glass 				/* Get the varname */
1286493ccc7SSimon Glass 				for (i = 0; i < envcnt; i++)
1296493ccc7SSimon Glass 					envname[i] = varname_start[i];
1306493ccc7SSimon Glass 				envname[i] = 0;
1316493ccc7SSimon Glass 
1326493ccc7SSimon Glass 				/* Get its value */
13300caae6dSSimon Glass 				envval = env_get(envname);
1346493ccc7SSimon Glass 
1356493ccc7SSimon Glass 				/* Copy into the line if it exists */
1366493ccc7SSimon Glass 				if (envval != NULL)
1376493ccc7SSimon Glass 					while ((*envval) && outputcnt) {
1386493ccc7SSimon Glass 						*(output++) = *(envval++);
1396493ccc7SSimon Glass 						outputcnt--;
1406493ccc7SSimon Glass 					}
1416493ccc7SSimon Glass 				/* Look for another '$' */
1426493ccc7SSimon Glass 				state = 0;
1436493ccc7SSimon Glass 			}
1446493ccc7SSimon Glass 			break;
1456493ccc7SSimon Glass 		case 3:	/* Waiting for '        */
1466493ccc7SSimon Glass 			if ((c == '\'') && (prev != '\\')) {
1476493ccc7SSimon Glass 				state = 0;
1486493ccc7SSimon Glass 			} else {
1496493ccc7SSimon Glass 				*(output++) = c;
1506493ccc7SSimon Glass 				outputcnt--;
1516493ccc7SSimon Glass 			}
1526493ccc7SSimon Glass 			break;
1536493ccc7SSimon Glass 		}
1546493ccc7SSimon Glass 		prev = c;
1556493ccc7SSimon Glass 	}
1566493ccc7SSimon Glass 
1576493ccc7SSimon Glass 	if (outputcnt)
1586493ccc7SSimon Glass 		*output = 0;
1596493ccc7SSimon Glass 	else
1606493ccc7SSimon Glass 		*(output - 1) = 0;
1616493ccc7SSimon Glass 
1626493ccc7SSimon Glass 	debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
1636493ccc7SSimon Glass 		     strlen(output_start), output_start);
1646493ccc7SSimon Glass }
1656493ccc7SSimon Glass 
1666493ccc7SSimon Glass  /*
1676493ccc7SSimon Glass  * WARNING:
1686493ccc7SSimon Glass  *
1696493ccc7SSimon Glass  * We must create a temporary copy of the command since the command we get
17000caae6dSSimon Glass  * may be the result from env_get(), which returns a pointer directly to
1716493ccc7SSimon Glass  * the environment data, which may change magicly when the command we run
1726493ccc7SSimon Glass  * creates or modifies environment variables (like "bootp" does).
1736493ccc7SSimon Glass  */
cli_simple_run_command(const char * cmd,int flag)1746493ccc7SSimon Glass int cli_simple_run_command(const char *cmd, int flag)
1756493ccc7SSimon Glass {
1766493ccc7SSimon Glass 	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
1776493ccc7SSimon Glass 	char *token;			/* start of token in cmdbuf	*/
1786493ccc7SSimon Glass 	char *sep;			/* end of token (separator) in cmdbuf */
1796493ccc7SSimon Glass 	char finaltoken[CONFIG_SYS_CBSIZE];
1806493ccc7SSimon Glass 	char *str = cmdbuf;
1816493ccc7SSimon Glass 	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
1826493ccc7SSimon Glass 	int argc, inquotes;
1836493ccc7SSimon Glass 	int repeatable = 1;
1846493ccc7SSimon Glass 	int rc = 0;
1856493ccc7SSimon Glass 
1866493ccc7SSimon Glass 	debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
1876493ccc7SSimon Glass 	if (DEBUG_PARSER) {
1886493ccc7SSimon Glass 		/* use puts - string may be loooong */
1896493ccc7SSimon Glass 		puts(cmd ? cmd : "NULL");
1906493ccc7SSimon Glass 		puts("\"\n");
1916493ccc7SSimon Glass 	}
1926493ccc7SSimon Glass 	clear_ctrlc();		/* forget any previous Control C */
1936493ccc7SSimon Glass 
1946493ccc7SSimon Glass 	if (!cmd || !*cmd)
1956493ccc7SSimon Glass 		return -1;	/* empty command */
1966493ccc7SSimon Glass 
1976493ccc7SSimon Glass 	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
1986493ccc7SSimon Glass 		puts("## Command too long!\n");
1996493ccc7SSimon Glass 		return -1;
2006493ccc7SSimon Glass 	}
2016493ccc7SSimon Glass 
2026493ccc7SSimon Glass 	strcpy(cmdbuf, cmd);
2036493ccc7SSimon Glass 
2046493ccc7SSimon Glass 	/* Process separators and check for invalid
2056493ccc7SSimon Glass 	 * repeatable commands
2066493ccc7SSimon Glass 	 */
2076493ccc7SSimon Glass 
2086493ccc7SSimon Glass 	debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
2096493ccc7SSimon Glass 	while (*str) {
2106493ccc7SSimon Glass 		/*
2116493ccc7SSimon Glass 		 * Find separator, or string end
2126493ccc7SSimon Glass 		 * Allow simple escape of ';' by writing "\;"
2136493ccc7SSimon Glass 		 */
2146493ccc7SSimon Glass 		for (inquotes = 0, sep = str; *sep; sep++) {
2156493ccc7SSimon Glass 			if ((*sep == '\'') &&
2166493ccc7SSimon Glass 			    (*(sep - 1) != '\\'))
2176493ccc7SSimon Glass 				inquotes = !inquotes;
2186493ccc7SSimon Glass 
2196493ccc7SSimon Glass 			if (!inquotes &&
2206493ccc7SSimon Glass 			    (*sep == ';') &&	/* separator		*/
2216493ccc7SSimon Glass 			    (sep != str) &&	/* past string start	*/
2226493ccc7SSimon Glass 			    (*(sep - 1) != '\\'))	/* and NOT escaped */
2236493ccc7SSimon Glass 				break;
2246493ccc7SSimon Glass 		}
2256493ccc7SSimon Glass 
2266493ccc7SSimon Glass 		/*
2276493ccc7SSimon Glass 		 * Limit the token to data between separators
2286493ccc7SSimon Glass 		 */
2296493ccc7SSimon Glass 		token = str;
2306493ccc7SSimon Glass 		if (*sep) {
2316493ccc7SSimon Glass 			str = sep + 1;	/* start of command for next pass */
2326493ccc7SSimon Glass 			*sep = '\0';
2336493ccc7SSimon Glass 		} else {
2346493ccc7SSimon Glass 			str = sep;	/* no more commands for next pass */
2356493ccc7SSimon Glass 		}
2366493ccc7SSimon Glass 		debug_parser("token: \"%s\"\n", token);
2376493ccc7SSimon Glass 
2386493ccc7SSimon Glass 		/* find macros in this token and replace them */
239a06be2d0SHans de Goede 		cli_simple_process_macros(token, finaltoken);
2406493ccc7SSimon Glass 
2416493ccc7SSimon Glass 		/* Extract arguments */
242e1bf824dSSimon Glass 		argc = cli_simple_parse_line(finaltoken, argv);
2436493ccc7SSimon Glass 		if (argc == 0) {
2446493ccc7SSimon Glass 			rc = -1;	/* no command at all */
2456493ccc7SSimon Glass 			continue;
2466493ccc7SSimon Glass 		}
2476493ccc7SSimon Glass 
2486493ccc7SSimon Glass 		if (cmd_process(flag, argc, argv, &repeatable, NULL))
2496493ccc7SSimon Glass 			rc = -1;
2506493ccc7SSimon Glass 
2516493ccc7SSimon Glass 		/* Did the user stop this? */
2526493ccc7SSimon Glass 		if (had_ctrlc())
2536493ccc7SSimon Glass 			return -1;	/* if stopped then not repeatable */
2546493ccc7SSimon Glass 	}
2556493ccc7SSimon Glass 
2566493ccc7SSimon Glass 	return rc ? rc : repeatable;
2576493ccc7SSimon Glass }
2586493ccc7SSimon Glass 
cli_simple_loop(void)259c1bb2cd0SSimon Glass void cli_simple_loop(void)
2606493ccc7SSimon Glass {
261ca7def60SImran Zaman 	static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
2626493ccc7SSimon Glass 
2636493ccc7SSimon Glass 	int len;
2646493ccc7SSimon Glass 	int flag;
2656493ccc7SSimon Glass 	int rc = 1;
2666493ccc7SSimon Glass 
2676493ccc7SSimon Glass 	for (;;) {
2686493ccc7SSimon Glass 		if (rc >= 0) {
2696493ccc7SSimon Glass 			/* Saw enough of a valid command to
2706493ccc7SSimon Glass 			 * restart the timeout.
2716493ccc7SSimon Glass 			 */
272b26440f1SSimon Glass 			bootretry_reset_cmd_timeout();
2736493ccc7SSimon Glass 		}
274e1bf824dSSimon Glass 		len = cli_readline(CONFIG_SYS_PROMPT);
2756493ccc7SSimon Glass 
2766493ccc7SSimon Glass 		flag = 0;	/* assume no special flags for now */
2776493ccc7SSimon Glass 		if (len > 0)
278bb08a6e7SPeng Fan 			strlcpy(lastcommand, console_buffer,
279bb08a6e7SPeng Fan 				CONFIG_SYS_CBSIZE + 1);
2806493ccc7SSimon Glass 		else if (len == 0)
2816493ccc7SSimon Glass 			flag |= CMD_FLAG_REPEAT;
2826493ccc7SSimon Glass #ifdef CONFIG_BOOT_RETRY_TIME
2836493ccc7SSimon Glass 		else if (len == -2) {
2846493ccc7SSimon Glass 			/* -2 means timed out, retry autoboot
2856493ccc7SSimon Glass 			 */
2866493ccc7SSimon Glass 			puts("\nTimed out waiting for command\n");
2876493ccc7SSimon Glass # ifdef CONFIG_RESET_TO_RETRY
2886493ccc7SSimon Glass 			/* Reinit board to run initialization code again */
2896493ccc7SSimon Glass 			do_reset(NULL, 0, 0, NULL);
2906493ccc7SSimon Glass # else
2916493ccc7SSimon Glass 			return;		/* retry autoboot */
2926493ccc7SSimon Glass # endif
2936493ccc7SSimon Glass 		}
2946493ccc7SSimon Glass #endif
2956493ccc7SSimon Glass 
2966493ccc7SSimon Glass 		if (len == -1)
2976493ccc7SSimon Glass 			puts("<INTERRUPT>\n");
2986493ccc7SSimon Glass 		else
29952715f89SThomas Betker 			rc = run_command_repeatable(lastcommand, flag);
3006493ccc7SSimon Glass 
3016493ccc7SSimon Glass 		if (rc <= 0) {
3026493ccc7SSimon Glass 			/* invalid command or not repeatable, forget it */
3036493ccc7SSimon Glass 			lastcommand[0] = 0;
3046493ccc7SSimon Glass 		}
3056493ccc7SSimon Glass 	}
3066493ccc7SSimon Glass }
3076493ccc7SSimon Glass 
cli_simple_run_command_list(char * cmd,int flag)3086493ccc7SSimon Glass int cli_simple_run_command_list(char *cmd, int flag)
3096493ccc7SSimon Glass {
3106493ccc7SSimon Glass 	char *line, *next;
3116493ccc7SSimon Glass 	int rcode = 0;
3126493ccc7SSimon Glass 
3136493ccc7SSimon Glass 	/*
3146493ccc7SSimon Glass 	 * Break into individual lines, and execute each line; terminate on
3156493ccc7SSimon Glass 	 * error.
3166493ccc7SSimon Glass 	 */
3176493ccc7SSimon Glass 	next = cmd;
3186493ccc7SSimon Glass 	line = cmd;
3196493ccc7SSimon Glass 	while (*next) {
3206493ccc7SSimon Glass 		if (*next == '\n') {
3216493ccc7SSimon Glass 			*next = '\0';
3226493ccc7SSimon Glass 			/* run only non-empty commands */
3236493ccc7SSimon Glass 			if (*line) {
3246493ccc7SSimon Glass 				debug("** exec: \"%s\"\n", line);
3256493ccc7SSimon Glass 				if (cli_simple_run_command(line, 0) < 0) {
3266493ccc7SSimon Glass 					rcode = 1;
3276493ccc7SSimon Glass 					break;
3286493ccc7SSimon Glass 				}
3296493ccc7SSimon Glass 			}
3306493ccc7SSimon Glass 			line = next + 1;
3316493ccc7SSimon Glass 		}
3326493ccc7SSimon Glass 		++next;
3336493ccc7SSimon Glass 	}
3346493ccc7SSimon Glass 	if (rcode == 0 && *line)
3354eb580b7SSimon Glass 		rcode = (cli_simple_run_command(line, 0) < 0);
3366493ccc7SSimon Glass 
3376493ccc7SSimon Glass 	return rcode;
3386493ccc7SSimon Glass }
339