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