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