ARM: renesas: Drop unused mmc.h
[pandora-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 int cli_simple_process_macros(const char *input, char *output, int max_size)
26 {
27         char c, prev;
28         const char *varname_start = NULL;
29         int inputcnt = strlen(input);
30         int outputcnt = max_size;
31         int state = 0;          /* 0 = waiting for '$'  */
32         int ret;
33
34         /* 1 = waiting for '(' or '{' */
35         /* 2 = waiting for ')' or '}' */
36         /* 3 = waiting for '''  */
37         char __maybe_unused *output_start = output;
38
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 */
99                                 envval = env_get(envname);
100
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
123         ret = inputcnt ? -ENOSPC : 0;
124         if (outputcnt) {
125                 *output = 0;
126         } else {
127                 *(output - 1) = 0;
128                 ret = -ENOSPC;
129         }
130
131         debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
132                      strlen(output_start), output_start);
133
134         return ret;
135 }
136
137 #ifdef CONFIG_CMDLINE
138 int 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
175  /*
176  * WARNING:
177  *
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).
182  */
183 int 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 */
248                 cli_simple_process_macros(token, finaltoken,
249                                           sizeof(finaltoken));
250
251                 /* Extract arguments */
252                 argc = cli_simple_parse_line(finaltoken, argv);
253                 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
269 void cli_simple_loop(void)
270 {
271         static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
272
273         int len;
274         int flag;
275         int rc = 1;
276
277         for (;;) {
278                 if (rc >= 0) {
279                         /* Saw enough of a valid command to
280                          * restart the timeout.
281                          */
282                         bootretry_reset_cmd_timeout();
283                 }
284                 len = cli_readline(CONFIG_SYS_PROMPT);
285
286                 flag = 0;       /* assume no special flags for now */
287                 if (len > 0)
288                         strlcpy(lastcommand, console_buffer,
289                                 CONFIG_SYS_CBSIZE + 1);
290                 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
309                         rc = run_command_repeatable(lastcommand, flag);
310
311                 if (rc <= 0) {
312                         /* invalid command or not repeatable, forget it */
313                         lastcommand[0] = 0;
314                 }
315         }
316 }
317
318 int 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)
345                 rcode = (cli_simple_run_command(line, 0) < 0);
346
347         return rcode;
348 }
349 #endif