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