blob: 311880ad56ab33005f344128f051f3d7687fa07e [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass66b8c8b2014-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 Glass66b8c8b2014-04-10 20:01:26 -06009 */
10
11#include <common.h>
Simon Glass399ed9a2014-04-10 20:01:30 -060012#include <bootretry.h>
Simon Glass66b8c8b2014-04-10 20:01:26 -060013#include <cli.h>
Simon Glassa73bda42015-11-08 23:47:45 -070014#include <console.h>
Simon Glass66b8c8b2014-04-10 20:01:26 -060015#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
Simon Glassbe6aafc2014-04-10 20:01:27 -060023int cli_simple_parse_line(char *line, char *argv[])
Simon Glass66b8c8b2014-04-10 20:01:26 -060024{
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
Hans de Goedeb01e5102014-08-06 09:37:38 +020060void cli_simple_process_macros(const char *input, char *output)
Simon Glass66b8c8b2014-04-10 20:01:26 -060061{
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 ''' */
Heiko Schocher62cb1562015-06-29 09:10:46 +020071 char __maybe_unused *output_start = output;
Simon Glass66b8c8b2014-04-10 20:01:26 -060072
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 */
Simon Glass64b723f2017-08-03 12:22:12 -0600133 envval = env_get(envname);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600134
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
Simon Glass64b723f2017-08-03 12:22:12 -0600170 * may be the result from env_get(), which returns a pointer directly to
Simon Glass66b8c8b2014-04-10 20:01:26 -0600171 * the environment data, which may change magicly when the command we run
172 * creates or modifies environment variables (like "bootp" does).
173 */
174int 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 */
Hans de Goedeb01e5102014-08-06 09:37:38 +0200239 cli_simple_process_macros(token, finaltoken);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600240
241 /* Extract arguments */
Simon Glassbe6aafc2014-04-10 20:01:27 -0600242 argc = cli_simple_parse_line(finaltoken, argv);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600243 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
Simon Glass33f79132014-04-10 20:01:34 -0600259void cli_simple_loop(void)
Simon Glass66b8c8b2014-04-10 20:01:26 -0600260{
Imran Zaman066ecbf2015-09-07 11:24:08 +0300261 static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
Simon Glass66b8c8b2014-04-10 20:01:26 -0600262
263 int len;
264 int flag;
265 int rc = 1;
266
267 for (;;) {
Simon Glass66b8c8b2014-04-10 20:01:26 -0600268 if (rc >= 0) {
269 /* Saw enough of a valid command to
270 * restart the timeout.
271 */
Simon Glass09007c42014-04-10 20:01:31 -0600272 bootretry_reset_cmd_timeout();
Simon Glass66b8c8b2014-04-10 20:01:26 -0600273 }
Simon Glassbe6aafc2014-04-10 20:01:27 -0600274 len = cli_readline(CONFIG_SYS_PROMPT);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600275
276 flag = 0; /* assume no special flags for now */
277 if (len > 0)
Peng Fan00ed2df2016-01-10 13:01:22 +0800278 strlcpy(lastcommand, console_buffer,
279 CONFIG_SYS_CBSIZE + 1);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600280 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
Thomas Betker02717562014-06-05 20:07:58 +0200299 rc = run_command_repeatable(lastcommand, flag);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600300
301 if (rc <= 0) {
302 /* invalid command or not repeatable, forget it */
303 lastcommand[0] = 0;
304 }
305 }
306}
307
308int 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)
Simon Glass3ad24d52014-05-30 14:41:51 -0600335 rcode = (cli_simple_run_command(line, 0) < 0);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600336
337 return rcode;
338}