1 // SPDX-License-Identifier: GPL-2.0+
6 * Add to readline cmdline-editing by
12 #include <bootretry.h>
18 #include <linux/ctype.h>
20 #define DEBUG_PARSER 0 /* set to 1 to debug */
22 #define debug_parser(fmt, args...) \
23 debug_cond(DEBUG_PARSER, fmt, ##args)
25 int cli_simple_process_macros(const char *input, char *output, int max_size)
28 const char *varname_start = NULL;
29 int inputcnt = strlen(input);
30 int outputcnt = max_size;
31 int state = 0; /* 0 = waiting for '$' */
34 /* 1 = waiting for '(' or '{' */
35 /* 2 = waiting for ')' or '}' */
36 /* 3 = waiting for ''' */
37 char __maybe_unused *output_start = output;
39 debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
42 prev = '\0'; /* previous character */
44 while (inputcnt && outputcnt) {
49 /* remove one level of escape characters */
50 if ((c == '\\') && (prev != '\\')) {
59 case 0: /* Waiting for (unescaped) $ */
60 if ((c == '\'') && (prev != '\\')) {
64 if ((c == '$') && (prev != '\\')) {
71 case 1: /* Waiting for ( */
72 if (c == '(' || c == '{') {
74 varname_start = input;
86 case 2: /* Waiting for ) */
87 if (c == ')' || c == '}') {
89 char envname[CONFIG_SYS_CBSIZE], *envval;
90 /* Varname # of chars */
91 int envcnt = input - varname_start - 1;
94 for (i = 0; i < envcnt; i++)
95 envname[i] = varname_start[i];
99 envval = env_get(envname);
101 /* Copy into the line if it exists */
103 while ((*envval) && outputcnt) {
104 *(output++) = *(envval++);
107 /* Look for another '$' */
111 case 3: /* Waiting for ' */
112 if ((c == '\'') && (prev != '\\')) {
123 ret = inputcnt ? -ENOSPC : 0;
131 debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
132 strlen(output_start), output_start);
137 #ifdef CONFIG_CMDLINE
138 int cli_simple_parse_line(char *line, char *argv[])
142 debug_parser("%s: \"%s\"\n", __func__, line);
143 while (nargs < CONFIG_SYS_MAXARGS) {
144 /* skip any white space */
145 while (isblank(*line))
148 if (*line == '\0') { /* end of line, no more args */
150 debug_parser("%s: nargs=%d\n", __func__, nargs);
154 argv[nargs++] = line; /* begin of argument string */
156 /* find end of string */
157 while (*line && !isblank(*line))
160 if (*line == '\0') { /* end of line, no more args */
162 debug_parser("parse_line: nargs=%d\n", nargs);
166 *line++ = '\0'; /* terminate current arg */
169 printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
171 debug_parser("%s: nargs=%d\n", __func__, nargs);
178 * We must create a temporary copy of the command since the command we get
179 * may be the result from env_get(), which returns a pointer directly to
180 * the environment data, which may change magicly when the command we run
181 * creates or modifies environment variables (like "bootp" does).
183 int cli_simple_run_command(const char *cmd, int flag)
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];
190 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
195 debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
197 /* use puts - string may be loooong */
198 puts(cmd ? cmd : "NULL");
201 clear_ctrlc(); /* forget any previous Control C */
204 return -1; /* empty command */
206 if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
207 puts("## Command too long!\n");
213 /* Process separators and check for invalid
214 * repeatable commands
217 debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
220 * Find separator, or string end
221 * Allow simple escape of ';' by writing "\;"
223 for (inquotes = 0, sep = str; *sep; sep++) {
224 if ((*sep == '\'') &&
225 (*(sep - 1) != '\\'))
226 inquotes = !inquotes;
229 (*sep == ';') && /* separator */
230 (sep != str) && /* past string start */
231 (*(sep - 1) != '\\')) /* and NOT escaped */
236 * Limit the token to data between separators
240 str = sep + 1; /* start of command for next pass */
243 str = sep; /* no more commands for next pass */
245 debug_parser("token: \"%s\"\n", token);
247 /* find macros in this token and replace them */
248 cli_simple_process_macros(token, finaltoken,
251 /* Extract arguments */
252 argc = cli_simple_parse_line(finaltoken, argv);
254 rc = -1; /* no command at all */
258 if (cmd_process(flag, argc, argv, &repeatable, NULL))
261 /* Did the user stop this? */
263 return -1; /* if stopped then not repeatable */
266 return rc ? rc : repeatable;
269 void cli_simple_loop(void)
271 static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
279 /* Saw enough of a valid command to
280 * restart the timeout.
282 bootretry_reset_cmd_timeout();
284 len = cli_readline(CONFIG_SYS_PROMPT);
286 flag = 0; /* assume no special flags for now */
288 strlcpy(lastcommand, console_buffer,
289 CONFIG_SYS_CBSIZE + 1);
291 flag |= CMD_FLAG_REPEAT;
292 #ifdef CONFIG_BOOT_RETRY_TIME
293 else if (len == -2) {
294 /* -2 means timed out, retry autoboot
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);
301 return; /* retry autoboot */
307 puts("<INTERRUPT>\n");
309 rc = run_command_repeatable(lastcommand, flag);
312 /* invalid command or not repeatable, forget it */
318 int cli_simple_run_command_list(char *cmd, int flag)
324 * Break into individual lines, and execute each line; terminate on
332 /* run only non-empty commands */
334 debug("** exec: \"%s\"\n", line);
335 if (cli_simple_run_command(line, 0) < 0) {
344 if (rcode == 0 && *line)
345 rcode = (cli_simple_run_command(line, 0) < 0);