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