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