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