blob: f89ba92d1b05c50bfa01a52498de5b7a5558e1c9 [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 Glassadaaa482019-11-14 12:57:43 -070014#include <command.h>
Simon Glassa73bda42015-11-08 23:47:45 -070015#include <console.h>
Simon Glass0af6e2d2019-08-01 09:46:52 -060016#include <env.h>
Simon Glass0f2af882020-05-10 11:40:05 -060017#include <log.h>
Simon Glass66b8c8b2014-04-10 20:01:26 -060018#include <linux/ctype.h>
19
20#define DEBUG_PARSER 0 /* set to 1 to debug */
21
22#define debug_parser(fmt, args...) \
23 debug_cond(DEBUG_PARSER, fmt, ##args)
24
Simon Glassc7b03e82020-11-05 10:33:47 -070025int cli_simple_process_macros(const char *input, char *output, int max_size)
Simon Glass66b8c8b2014-04-10 20:01:26 -060026{
27 char c, prev;
28 const char *varname_start = NULL;
29 int inputcnt = strlen(input);
Simon Glassc7b03e82020-11-05 10:33:47 -070030 int outputcnt = max_size;
Simon Glass66b8c8b2014-04-10 20:01:26 -060031 int state = 0; /* 0 = waiting for '$' */
Simon Glassc7b03e82020-11-05 10:33:47 -070032 int ret;
Simon Glass66b8c8b2014-04-10 20:01:26 -060033
34 /* 1 = waiting for '(' or '{' */
35 /* 2 = waiting for ')' or '}' */
36 /* 3 = waiting for ''' */
Heiko Schocher62cb1562015-06-29 09:10:46 +020037 char __maybe_unused *output_start = output;
Simon Glass66b8c8b2014-04-10 20:01:26 -060038
39 debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
40 input);
41
42 prev = '\0'; /* previous character */
43
44 while (inputcnt && outputcnt) {
45 c = *input++;
46 inputcnt--;
47
48 if (state != 3) {
49 /* remove one level of escape characters */
50 if ((c == '\\') && (prev != '\\')) {
51 if (inputcnt-- == 0)
52 break;
53 prev = c;
54 c = *input++;
55 }
56 }
57
58 switch (state) {
59 case 0: /* Waiting for (unescaped) $ */
60 if ((c == '\'') && (prev != '\\')) {
61 state = 3;
62 break;
63 }
64 if ((c == '$') && (prev != '\\')) {
65 state++;
66 } else {
67 *(output++) = c;
68 outputcnt--;
69 }
70 break;
71 case 1: /* Waiting for ( */
72 if (c == '(' || c == '{') {
73 state++;
74 varname_start = input;
75 } else {
76 state = 0;
77 *(output++) = '$';
78 outputcnt--;
79
80 if (outputcnt) {
81 *(output++) = c;
82 outputcnt--;
83 }
84 }
85 break;
86 case 2: /* Waiting for ) */
87 if (c == ')' || c == '}') {
88 int i;
89 char envname[CONFIG_SYS_CBSIZE], *envval;
90 /* Varname # of chars */
91 int envcnt = input - varname_start - 1;
92
93 /* Get the varname */
94 for (i = 0; i < envcnt; i++)
95 envname[i] = varname_start[i];
96 envname[i] = 0;
97
98 /* Get its value */
Simon Glass64b723f2017-08-03 12:22:12 -060099 envval = env_get(envname);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600100
101 /* Copy into the line if it exists */
102 if (envval != NULL)
103 while ((*envval) && outputcnt) {
104 *(output++) = *(envval++);
105 outputcnt--;
106 }
107 /* Look for another '$' */
108 state = 0;
109 }
110 break;
111 case 3: /* Waiting for ' */
112 if ((c == '\'') && (prev != '\\')) {
113 state = 0;
114 } else {
115 *(output++) = c;
116 outputcnt--;
117 }
118 break;
119 }
120 prev = c;
121 }
122
Simon Glassc7b03e82020-11-05 10:33:47 -0700123 ret = inputcnt ? -ENOSPC : 0;
124 if (outputcnt) {
Simon Glass66b8c8b2014-04-10 20:01:26 -0600125 *output = 0;
Simon Glassc7b03e82020-11-05 10:33:47 -0700126 } else {
Simon Glass66b8c8b2014-04-10 20:01:26 -0600127 *(output - 1) = 0;
Simon Glassc7b03e82020-11-05 10:33:47 -0700128 ret = -ENOSPC;
129 }
Simon Glass66b8c8b2014-04-10 20:01:26 -0600130
131 debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
132 strlen(output_start), output_start);
Simon Glassc7b03e82020-11-05 10:33:47 -0700133
134 return ret;
Simon Glass66b8c8b2014-04-10 20:01:26 -0600135}
136
Tom Rini5a347532023-10-26 14:31:19 -0400137#ifdef CONFIG_CMDLINE
138int cli_simple_parse_line(char *line, char *argv[])
139{
140 int nargs = 0;
141
142 debug_parser("%s: \"%s\"\n", __func__, line);
143 while (nargs < CONFIG_SYS_MAXARGS) {
144 /* skip any white space */
145 while (isblank(*line))
146 ++line;
147
148 if (*line == '\0') { /* end of line, no more args */
149 argv[nargs] = NULL;
150 debug_parser("%s: nargs=%d\n", __func__, nargs);
151 return nargs;
152 }
153
154 argv[nargs++] = line; /* begin of argument string */
155
156 /* find end of string */
157 while (*line && !isblank(*line))
158 ++line;
159
160 if (*line == '\0') { /* end of line, no more args */
161 argv[nargs] = NULL;
162 debug_parser("parse_line: nargs=%d\n", nargs);
163 return nargs;
164 }
165
166 *line++ = '\0'; /* terminate current arg */
167 }
168
169 printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
170
171 debug_parser("%s: nargs=%d\n", __func__, nargs);
172 return nargs;
173}
174
Simon Glass66b8c8b2014-04-10 20:01:26 -0600175 /*
176 * WARNING:
177 *
178 * We must create a temporary copy of the command since the command we get
Simon Glass64b723f2017-08-03 12:22:12 -0600179 * may be the result from env_get(), which returns a pointer directly to
Simon Glass66b8c8b2014-04-10 20:01:26 -0600180 * the environment data, which may change magicly when the command we run
181 * creates or modifies environment variables (like "bootp" does).
182 */
183int cli_simple_run_command(const char *cmd, int flag)
184{
185 char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
186 char *token; /* start of token in cmdbuf */
187 char *sep; /* end of token (separator) in cmdbuf */
188 char finaltoken[CONFIG_SYS_CBSIZE];
189 char *str = cmdbuf;
190 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
191 int argc, inquotes;
192 int repeatable = 1;
193 int rc = 0;
194
195 debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
196 if (DEBUG_PARSER) {
197 /* use puts - string may be loooong */
198 puts(cmd ? cmd : "NULL");
199 puts("\"\n");
200 }
201 clear_ctrlc(); /* forget any previous Control C */
202
203 if (!cmd || !*cmd)
204 return -1; /* empty command */
205
206 if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
207 puts("## Command too long!\n");
208 return -1;
209 }
210
211 strcpy(cmdbuf, cmd);
212
213 /* Process separators and check for invalid
214 * repeatable commands
215 */
216
217 debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
218 while (*str) {
219 /*
220 * Find separator, or string end
221 * Allow simple escape of ';' by writing "\;"
222 */
223 for (inquotes = 0, sep = str; *sep; sep++) {
224 if ((*sep == '\'') &&
225 (*(sep - 1) != '\\'))
226 inquotes = !inquotes;
227
228 if (!inquotes &&
229 (*sep == ';') && /* separator */
230 (sep != str) && /* past string start */
231 (*(sep - 1) != '\\')) /* and NOT escaped */
232 break;
233 }
234
235 /*
236 * Limit the token to data between separators
237 */
238 token = str;
239 if (*sep) {
240 str = sep + 1; /* start of command for next pass */
241 *sep = '\0';
242 } else {
243 str = sep; /* no more commands for next pass */
244 }
245 debug_parser("token: \"%s\"\n", token);
246
247 /* find macros in this token and replace them */
Simon Glassc7b03e82020-11-05 10:33:47 -0700248 cli_simple_process_macros(token, finaltoken,
249 sizeof(finaltoken));
Simon Glass66b8c8b2014-04-10 20:01:26 -0600250
251 /* Extract arguments */
Simon Glassbe6aafc2014-04-10 20:01:27 -0600252 argc = cli_simple_parse_line(finaltoken, argv);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600253 if (argc == 0) {
254 rc = -1; /* no command at all */
255 continue;
256 }
257
258 if (cmd_process(flag, argc, argv, &repeatable, NULL))
259 rc = -1;
260
261 /* Did the user stop this? */
262 if (had_ctrlc())
263 return -1; /* if stopped then not repeatable */
264 }
265
266 return rc ? rc : repeatable;
267}
268
Simon Glass33f79132014-04-10 20:01:34 -0600269void cli_simple_loop(void)
Simon Glass66b8c8b2014-04-10 20:01:26 -0600270{
Imran Zaman066ecbf2015-09-07 11:24:08 +0300271 static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
Simon Glass66b8c8b2014-04-10 20:01:26 -0600272
273 int len;
274 int flag;
275 int rc = 1;
276
277 for (;;) {
Simon Glass66b8c8b2014-04-10 20:01:26 -0600278 if (rc >= 0) {
279 /* Saw enough of a valid command to
280 * restart the timeout.
281 */
Simon Glass09007c42014-04-10 20:01:31 -0600282 bootretry_reset_cmd_timeout();
Simon Glass66b8c8b2014-04-10 20:01:26 -0600283 }
Simon Glassbe6aafc2014-04-10 20:01:27 -0600284 len = cli_readline(CONFIG_SYS_PROMPT);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600285
286 flag = 0; /* assume no special flags for now */
287 if (len > 0)
Peng Fan00ed2df2016-01-10 13:01:22 +0800288 strlcpy(lastcommand, console_buffer,
289 CONFIG_SYS_CBSIZE + 1);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600290 else if (len == 0)
291 flag |= CMD_FLAG_REPEAT;
292#ifdef CONFIG_BOOT_RETRY_TIME
293 else if (len == -2) {
294 /* -2 means timed out, retry autoboot
295 */
296 puts("\nTimed out waiting for command\n");
297# ifdef CONFIG_RESET_TO_RETRY
298 /* Reinit board to run initialization code again */
299 do_reset(NULL, 0, 0, NULL);
300# else
301 return; /* retry autoboot */
302# endif
303 }
304#endif
305
306 if (len == -1)
307 puts("<INTERRUPT>\n");
308 else
Thomas Betker02717562014-06-05 20:07:58 +0200309 rc = run_command_repeatable(lastcommand, flag);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600310
311 if (rc <= 0) {
312 /* invalid command or not repeatable, forget it */
313 lastcommand[0] = 0;
314 }
315 }
316}
317
318int cli_simple_run_command_list(char *cmd, int flag)
319{
320 char *line, *next;
321 int rcode = 0;
322
323 /*
324 * Break into individual lines, and execute each line; terminate on
325 * error.
326 */
327 next = cmd;
328 line = cmd;
329 while (*next) {
330 if (*next == '\n') {
331 *next = '\0';
332 /* run only non-empty commands */
333 if (*line) {
334 debug("** exec: \"%s\"\n", line);
335 if (cli_simple_run_command(line, 0) < 0) {
336 rcode = 1;
337 break;
338 }
339 }
340 line = next + 1;
341 }
342 ++next;
343 }
344 if (rcode == 0 && *line)
Simon Glass3ad24d52014-05-30 14:41:51 -0600345 rcode = (cli_simple_run_command(line, 0) < 0);
Simon Glass66b8c8b2014-04-10 20:01:26 -0600346
347 return rcode;
348}
Tom Rini5a347532023-10-26 14:31:19 -0400349#endif