c550812f570fef5f68205bcb4eb308e7ba9336d5
[oweals/u-boot_mod.git] / u-boot / common / main.c
1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <command.h>
26
27 #if defined(CFG_HUSH_PARSER)
28 #include <hush.h>
29 #endif
30
31 #if defined(CONFIG_SILENT_CONSOLE)
32 DECLARE_GLOBAL_DATA_PTR;
33 #endif
34
35 #define MAX_STOPSTR_LEN 16
36
37 static char *delete_char(char *buffer, char *p, int *colp, int *np, int plen);
38 static int parse_line(char *, char *[]);
39
40 char console_buffer[CFG_CBSIZE];        /* console I/O buffer  */
41 static char erase_seq[] = "\b \b";      /* erase sequence      */
42 static char tab_seq[] = "        ";     /* used to expand TABs */
43
44 /*
45  * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
46  * returns: 0 -  no key string, allow autoboot
47  *          1 - got key string, abort
48  */
49 #if defined(CONFIG_BOOTDELAY) &&\
50            (CONFIG_BOOTDELAY >= 0)
51 static __inline__ int abortboot(int bootdelay)
52 {
53         int abort = 0;
54         int tested = 0;
55         int stopstr_len = 0;
56         int envstopstr_len = 0;
57
58         char c;
59         char *envstopstr;
60         char stopstr[MAX_STOPSTR_LEN] = { 0 };
61
62 #if defined(CONFIG_SILENT_CONSOLE)
63         if (gd->flags & GD_FLG_SILENT) {
64                 /* Restore serial console */
65                 console_assign(stdout, "serial");
66                 console_assign(stderr, "serial");
67         }
68 #endif
69
70         if (bootdelay <= 0)
71                 return abort;
72
73         /* Check value of 'bootstopkey' and just ignore it if it's over limit */
74         envstopstr = getenv("bootstopkey");
75         if (envstopstr) {
76                 envstopstr_len = strlen(envstopstr);
77
78                 if (envstopstr_len > MAX_STOPSTR_LEN)
79                         envstopstr = NULL;
80         }
81
82 #if defined(CONFIG_MENUPROMPT)
83         printf(CONFIG_MENUPROMPT, bootdelay);
84 #else
85         /*
86          * Use custom CONFIG_MENUPROMPT if bootstopkey
87          * string contains nonprintable characters (e.g. ESC)
88          */
89         if (envstopstr)
90                 printf("Enter '%s' to stop booting: %2d", envstopstr, bootdelay);
91         else
92                 printf("Hit any key to stop booting: %2d", bootdelay);
93 #endif
94
95         while ((bootdelay > 0) && (!abort)) {
96                 int i;
97
98                 --bootdelay;
99
100                 /* delay 500 * 2 ms */
101                 for (i = 0; !abort && i < 500; ++i, udelay(2000)) {
102                         if (!tstc() || tested)
103                                 continue;
104
105                         /* Ignore nulls */
106                         c = getc();
107                         if (c == 0)
108                                 continue;
109
110                         /* Interrupt by any key if bootstopkey isn't used */
111                         if (!envstopstr) {
112                                 abort = 1;
113                                 bootdelay = 0;
114                                 break;
115                         }
116
117                         /* Consume characters up to the strlen(envstopstr) */
118                         if (stopstr_len < envstopstr_len)
119                                 stopstr[stopstr_len++] = c;
120
121                         if (stopstr_len == envstopstr_len) {
122
123                                 if (memcmp(envstopstr, stopstr,
124                                            envstopstr_len) == 0) {
125                                         abort = 1;
126                                         bootdelay = 0;
127                                         break;
128                                 } else
129                                         tested = 1;
130                         }
131                 }
132                 printf("\b\b%2d", bootdelay);
133         }
134         puts("\n\n");
135
136 #if defined(CONFIG_SILENT_CONSOLE)
137         if (abort) {
138                 /* Permanently enable normal console output */
139                 gd->flags &= ~(GD_FLG_SILENT);
140         } else if (gd->flags & GD_FLG_SILENT) {
141                 /* Restore silent console */
142                 console_assign(stdout, "nulldev");
143                 console_assign(stderr, "nulldev");
144         }
145 #endif
146
147         return abort;
148 }
149 #endif /* CONFIG_BOOTDELAY && CONFIG_BOOTDELAY >= 0 */
150
151 /*
152  * =========
153  * Main loop
154  * =========
155  */
156 void main_loop(void)
157 {
158         char *bootcmd;
159
160 #if defined(CONFIG_BTN_RECOVERY_SCRIPT)
161         int stop_boot;
162         char *c;
163 #endif
164
165 #ifndef CFG_HUSH_PARSER
166         static char lastcommand[CFG_CBSIZE] = { 0, };
167         int flag, len;
168         int rc = 1;
169 #endif
170
171 #if defined(CONFIG_BOOTDELAY) &&\
172            (CONFIG_BOOTDELAY >= 0)
173         int bootdelay;
174         char *s;
175 #endif
176
177 #if defined(CFG_HUSH_PARSER)
178         u_boot_hush_start();
179 #endif
180
181         /* Get boot command */
182         bootcmd = getenv("bootcmd");
183
184 #if defined(CONFIG_BOOTCOMMAND)
185         if (!bootcmd)
186                 setenv("bootcmd", CONFIG_BOOTCOMMAND);
187
188         bootcmd = getenv("bootcmd");
189 #endif
190
191 /* Recovery mode before normal boot */
192 #if defined(CONFIG_BTN_RECOVERY_SCRIPT)
193         if (reset_button_status()) {
194                 #if defined(CONFIG_SILENT_CONSOLE)
195                 if (gd->flags & GD_FLG_SILENT) {
196                         /* Restore serial console */
197                         console_assign(stdout, "serial");
198                         console_assign(stderr, "serial");
199                 }
200
201                 /* Enable normal console output */
202                 gd->flags &= ~(GD_FLG_SILENT);
203                 #endif
204
205                 /* Do we have recovery script in env var at all? */
206                 c = getenv("recovery");
207                 if (c == NULL) {
208                         puts("** Warning: recovery script is missing\n");
209                         puts("   in env, use 'defenv' to reset env\n\n");
210                 } else {
211                         /*
212                          * Always clear values of variables used in recovery
213                          * script as they could be accidentally saved before
214                          */
215                         setenv("stop_boot", NULL);
216                         setenv("cnt", NULL);
217
218                         run_command("run recovery", 0);
219
220                         /* Should we stop booting after recovery mode? */
221                         c = getenv("stop_boot");
222                         stop_boot = c ? (int)simple_strtol(c, NULL, 10) : 0;
223
224                         if (stop_boot) {
225                                 setenv("stop_boot", NULL);
226                                 bootcmd = NULL;
227                         }
228
229                         setenv("cnt", NULL);
230                 }
231         }
232 #endif /* CONFIG_RECOVERY_MODE */
233
234 #if defined(CONFIG_BOOTDELAY) &&\
235            (CONFIG_BOOTDELAY >= 0)
236         /* Get boot delay (seconds) */
237         s = getenv("bootdelay");
238         bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
239
240         if (bootdelay >= 0 && bootcmd && !abortboot(bootdelay)) {
241                 /* Try to boot */
242         #ifndef CFG_HUSH_PARSER
243                 run_command(bootcmd, 0);
244         #else
245                 parse_string_outer(bootcmd, FLAG_PARSE_SEMICOLON |
246                                             FLAG_EXIT_FROM_LOOP);
247         #endif
248         }
249 #else
250         if (bootcmd) {
251                 /* Try to boot */
252         #ifndef CFG_HUSH_PARSER
253                 run_command(bootcmd, 0);
254         #else
255                 parse_string_outer(bootcmd, FLAG_PARSE_SEMICOLON |
256                                             FLAG_EXIT_FROM_LOOP);
257         #endif
258         }
259 #endif /* CONFIG_BOOTDELAY && CONFIG_BOOTDELAY >= 0 */
260
261         /* Main loop for monitor command processing */
262 #if defined(CFG_HUSH_PARSER)
263         parse_file_outer();
264
265         /* This point is never reached */
266         for (;;)
267                 ;
268 #else
269         for (;;) {
270                 len = readline(CFG_PROMPT);
271
272                 /* Assume no special flags for now */
273                 flag = 0;
274
275                 if (len > 0)
276                         strcpy(lastcommand, console_buffer);
277                 else if (len == 0)
278                         flag |= CMD_FLAG_REPEAT;
279
280                 if (len == -1)
281                         puts("<INTERRUPT>\n");
282                 else
283                         rc = run_command(lastcommand, flag);
284
285                 /* Invalid command or not repeatable, forget it */
286                 if (rc <= 0)
287                         lastcommand[0] = 0;
288         }
289 #endif /* CFG_HUSH_PARSER */
290 }
291
292 /*
293  * Prompt for input and read a line.
294  * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
295  * time out when time goes past endtime (timebase time in ticks).
296  * Return:      number of read characters
297  *              -1 if break
298  *              -2 if timed out
299  */
300 int readline(const char * const prompt)
301 {
302         char *p = console_buffer;
303         int plen = 0;   /* prompt length     */
304         int n = 0;      /* buffer index      */
305         int col;        /* output column cnt */
306         char c;
307
308         /* Print prompt */
309         if (prompt) {
310                 plen = strlen(prompt);
311                 puts(prompt);
312         }
313         col = plen;
314
315         for (;;) {
316                 c = getc();
317
318                 /* Special character handling */
319                 switch (c) {
320                 /* Enter */
321                 case '\r':
322                 case '\n':
323                         *p = '\0';
324                         puts("\r\n");
325                         return p - console_buffer;
326                 /* NULL */
327                 case '\0':
328                         continue;
329                 /* ^C - break */
330                 case 0x03:
331                         /* discard input */
332                         console_buffer[0] = '\0';
333                         return -1;
334                 /* ^U - erase line */
335                 case 0x15:
336                         while (col > plen) {
337                                 puts(erase_seq);
338                                 --col;
339                         }
340
341                         p = console_buffer;
342                         n = 0;
343
344                         continue;
345                 /* ^W - erase word */
346                 case 0x17:
347                         p = delete_char(console_buffer, p, &col, &n, plen);
348                         while ((n > 0) && (*p != ' '))
349                                 p = delete_char(console_buffer, p, &col, &n, plen);
350
351                         continue;
352                 /* ^H  - backspace */
353                 /* DEL - backspace */
354                 case 0x08:
355                 case 0x7F:
356                         p = delete_char(console_buffer, p, &col, &n, plen);
357                         continue;
358                 /* Must be a normal character then */
359                 default:
360                         if (n < CFG_CBSIZE - 2) {
361                                 if (c == '\t') {
362                                         /* Expand TABs */
363                                         puts(tab_seq + (col & 07));
364                                         col += 8 - (col & 07);
365                                 } else {
366                                         /* Echo input */
367                                         ++col;
368                                         putc(c);
369                                 }
370
371                                 *p++ = c;
372                                 ++n;
373                         } else {
374                                 /* Buffer full */
375                                 putc('\a');
376                         }
377                 }
378         }
379 }
380
381 static char *delete_char(char *buffer,
382                          char *p,
383                          int *colp,
384                          int *np,
385                          int plen)
386 {
387         char *s;
388
389         if (*np == 0)
390                 return p;
391
392         if (*(--p) == '\t') {
393                 /* Will retype the whole line */
394                 while (*colp > plen) {
395                         puts(erase_seq);
396                         (*colp)--;
397                 }
398
399                 for (s = buffer; s < p; ++s) {
400                         if (*s == '\t') {
401                                 puts(tab_seq + ((*colp) & 07));
402                                 *colp += 8 - ((*colp) & 07);
403                         } else {
404                                 ++(*colp);
405                                 putc(*s);
406                         }
407                 }
408         } else {
409                 puts(erase_seq);
410                 (*colp)--;
411         }
412
413         (*np)--;
414         return p;
415 }
416
417 int parse_line(char *line, char *argv[])
418 {
419         int nargs = 0;
420
421         while (nargs < CFG_MAXARGS) {
422                 /* Skip any white space */
423                 while ((*line == ' ') || (*line == '\t'))
424                         ++line;
425
426                 /* End of line, no more args */
427                 if (*line == '\0') {
428                         argv[nargs] = NULL;
429                         return nargs;
430                 }
431
432                 /* Begin of argument string */
433                 argv[nargs++] = line;
434
435                 /* Find end of string */
436                 while (*line && (*line != ' ') && (*line != '\t'))
437                         ++line;
438
439                 /* End of line, no more args */
440                 if (*line == '\0') {
441                         argv[nargs] = NULL;
442                         return nargs;
443                 }
444
445                 /* Terminate current arg */
446                 *line++ = '\0';
447         }
448
449         printf_err("too many args (max. %d)\n", CFG_MAXARGS);
450
451         return nargs;
452 }
453
454 static void process_macros(const char *input, char *output)
455 {
456         const char *varname_start = NULL;
457         int inputcnt = strlen(input);
458         int outputcnt = CFG_CBSIZE;
459         char c, prev;
460         int state = 0;  /* 0 = waiting for '$'
461                            1 = waiting for '(' or '{'
462                            2 = waiting for ')' or '}'
463                            3 = waiting for ''' */
464
465         /* Previous character */
466         prev = '\0';
467
468         while (inputcnt && outputcnt) {
469                 c = *input++;
470                 inputcnt--;
471
472                 if (state != 3) {
473                         /* Remove one level of escape characters */
474                         if ((c == '\\') && (prev != '\\')) {
475                                 if (inputcnt-- == 0)
476                                         break;
477
478                                 prev = c;
479                                 c = *input++;
480                         }
481                 }
482
483                 switch(state){
484                 /* Waiting for (unescaped) $ */
485                 case 0:
486                         if ((c == '\'') && (prev != '\\')) {
487                                 state = 3;
488                                 break;
489                         }
490
491                         if ((c == '$') && (prev != '\\')) {
492                                 state++;
493                         } else {
494                                 *(output++) = c;
495                                 outputcnt--;
496                         }
497
498                         break;
499                 /* Waiting for ( */
500                 case 1:
501                         if (c == '(' || c == '{') {
502                                 state++;
503                                 varname_start = input;
504                         } else {
505                                 state = 0;
506                                 *(output++) = '$';
507                                 outputcnt--;
508
509                                 if (outputcnt) {
510                                         *(output++) = c;
511                                         outputcnt--;
512                                 }
513                         }
514
515                         break;
516                 /* Waiting for ) */
517                 case 2:
518                         if (c == ')' || c == '}') {
519                                 int envcnt = input - varname_start - 1; /* Varname # of chars */
520                                 char envname[CFG_CBSIZE], *envval;
521                                 int i;
522
523                                 /* Get the varname */
524                                 for (i = 0; i < envcnt; i++)
525                                         envname[i] = varname_start[i];
526
527                                 envname[i] = 0;
528
529                                 /* Get its value */
530                                 envval = getenv(envname);
531
532                                 /* Copy into the line if it exists */
533                                 if (envval != NULL) {
534                                         while ((*envval) && outputcnt) {
535                                                 *(output++) = *(envval++);
536                                                 outputcnt--;
537                                         }
538                                 }
539
540                                 /* Look for another '$' */
541                                 state = 0;
542                         }
543
544                         break;
545                 /* Waiting for ' */
546                 case 3:
547                         if ((c == '\'') && (prev != '\\')) {
548                                 state = 0;
549                         } else {
550                                 *(output++) = c;
551                                 outputcnt--;
552                         }
553
554                         break;
555                 }
556
557                 prev = c;
558         }
559
560         if (outputcnt)
561                 *output = 0;
562 }
563
564 /*
565  * returns:
566  *      1  - command executed, repeatable
567  *      0  - command executed but not repeatable, interrupted commands are
568  *           always considered not repeatable
569  *      -1 - not executed (unrecognized, bootd recursion or too many args)
570  *           (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is
571  *           considered unrecognized)
572  *
573  * WARNING:
574  *
575  * We must create a temporary copy of the command since the command we get
576  * may be the result from getenv(), which returns a pointer directly to
577  * the environment data, which may change magicly when the command we run
578  * creates or modifies environment variables (like "bootp" does).
579  */
580
581 int run_command(const char *cmd, int flag){
582         cmd_tbl_t *cmdtp;
583         char *argv[CFG_MAXARGS + 1];    /* NULL terminated                    */
584         char cmdbuf[CFG_CBSIZE];        /* Working copy of cmd                */
585         char *token;                    /* Start of token in cmdbuf           */
586         char *sep;                      /* End of token (separator) in cmdbuf */
587         char finaltoken[CFG_CBSIZE];
588         char *str = cmdbuf;
589         int argc, inquotes;
590         int repeatable = 1;
591         int rc = 0;
592
593         /* Forget any previous Control-C */
594         clear_ctrlc();
595
596         /* Empty command */
597         if (!cmd || !*cmd)
598                 return -1;
599
600         if (strlen(cmd) >= CFG_CBSIZE) {
601                 printf_err("command too long!\n");
602                 return -1;
603         }
604
605         strcpy(cmdbuf, cmd);
606
607         /* Process separators and check for invalid repeatable commands */
608         while (*str) {
609                 /*
610                  * Find separator, or string end
611                  * Allow simple escape of ';' by writing "\;"
612                  */
613                 for (inquotes = 0, sep = str; *sep; sep++) {
614                         if ((*sep == '\'') && (*(sep - 1) != '\\'))
615                                 inquotes = !inquotes;
616
617                         if(!inquotes && (*sep == ';')
618                                      && (sep != str) && (*(sep - 1) != '\\'))
619                                 break;
620                 }
621
622                 /* Limit the token to data between separators */
623                 token = str;
624
625                 if (*sep) {
626                         /* Start of command for next pass */
627                         str = sep + 1;
628                         *sep = '\0';
629                 } else {
630                         /* No more commands for next pass */
631                         str = sep;
632                 }
633
634                 /* Find macros in this token and replace them */
635                 process_macros(token, finaltoken);
636
637                 /* Extract arguments */
638                 if ((argc = parse_line(finaltoken, argv)) == 0) {
639                         /* No command at all */
640                         rc = -1;
641                         continue;
642                 }
643
644                 /* Look up command in command table */
645                 if ((cmdtp = find_cmd(argv[0])) == NULL) {
646                         printf_err("unknown command '%s' - try 'help'\n", argv[0]);
647                         puts("\n");
648
649                         /* Give up after bad command */
650                         rc = -1;
651                         continue;
652                 }
653
654                 /* Found - check max args */
655                 if (argc > cmdtp->maxargs) {
656                         print_cmd_help(cmdtp);
657
658                         /* Give up if too many args */
659                         rc = -1;
660                         continue;
661                 }
662
663                 /* OK - call function to do the command */
664                 if ((cmdtp->cmd)(cmdtp, flag, argc, argv) != 0)
665                         rc = -1;
666
667                 repeatable &= cmdtp->repeatable;
668
669                 /* Did the user stop this? If stopped then not repeatable */
670                 if (had_ctrlc())
671                         return(0);
672         }
673
674         return rc ? rc : repeatable;
675 }
676
677 #if defined(CONFIG_CMD_RUN)
678 int do_run(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
679 {
680         int i;
681
682         if (argc < 2) {
683                 print_cmd_help(cmdtp);
684                 return 1;
685         }
686
687         for (i=1; i<argc; ++i) {
688                 char *arg;
689
690                 if ((arg = getenv(argv[i])) == NULL) {
691                         printf_err("'%s' not defined\n", argv[i]);
692                         return 1;
693                 }
694
695         #ifndef CFG_HUSH_PARSER
696                 if (run_command(arg, flag) == -1)
697                         return 1;
698         #else
699                 if (parse_string_outer(arg, FLAG_PARSE_SEMICOLON |
700                                             FLAG_EXIT_FROM_LOOP) != 0)
701                         return 1;
702         #endif
703         }
704
705         return 0;
706 }
707 #endif /* CONFIG_CMD_RUN */