]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/main.c
* Code cleanup:
[karo-tx-uboot.git] / 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 /* #define      DEBUG   */
25
26 #include <common.h>
27 #include <watchdog.h>
28 #include <command.h>
29 #include <malloc.h>
30
31 #ifdef CFG_HUSH_PARSER
32 #include <hush.h>
33 #endif
34
35 #if defined(CONFIG_BOOT_RETRY_TIME) && defined(CONFIG_RESET_TO_RETRY)
36 extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);               /* for do_reset() prototype */
37 #endif
38
39 extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
40
41
42 #define MAX_DELAY_STOP_STR 32
43
44 static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen);
45 static int parse_line (char *, char *[]);
46 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
47 static int abortboot(int);
48 #endif
49
50 #undef DEBUG_PARSER
51
52 char        console_buffer[CFG_CBSIZE];         /* console I/O buffer   */
53
54 static char erase_seq[] = "\b \b";              /* erase sequence       */
55 static char   tab_seq[] = "        ";           /* used to expand TABs  */
56
57 #ifdef CONFIG_BOOT_RETRY_TIME
58 static uint64_t endtime = 0;  /* must be set, default is instant timeout */
59 static int      retry_time = -1; /* -1 so can call readline before main_loop */
60 #endif
61
62 #define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk())
63
64 #ifndef CONFIG_BOOT_RETRY_MIN
65 #define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME
66 #endif
67
68 #ifdef CONFIG_MODEM_SUPPORT
69 int do_mdm_init = 0;
70 extern void mdm_init(void); /* defined in board.c */
71 #endif
72
73 /***************************************************************************
74  * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
75  * returns: 0 -  no key string, allow autoboot
76  *          1 - got key string, abort
77  */
78 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
79 # if defined(CONFIG_AUTOBOOT_KEYED)
80 static __inline__ int abortboot(int bootdelay)
81 {
82         int abort = 0;
83         uint64_t etime = endtick(bootdelay);
84         struct
85         {
86                 char* str;
87                 u_int len;
88                 int retry;
89         }
90         delaykey [] =
91         {
92                 { str: getenv ("bootdelaykey"),  retry: 1 },
93                 { str: getenv ("bootdelaykey2"), retry: 1 },
94                 { str: getenv ("bootstopkey"),   retry: 0 },
95                 { str: getenv ("bootstopkey2"),  retry: 0 },
96         };
97
98         char presskey [MAX_DELAY_STOP_STR];
99         u_int presskey_len = 0;
100         u_int presskey_max = 0;
101         u_int i;
102
103 #  ifdef CONFIG_AUTOBOOT_PROMPT
104         printf (CONFIG_AUTOBOOT_PROMPT, bootdelay);
105 #  endif
106
107 #  ifdef CONFIG_AUTOBOOT_DELAY_STR
108         if (delaykey[0].str == NULL)
109                 delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
110 #  endif
111 #  ifdef CONFIG_AUTOBOOT_DELAY_STR2
112         if (delaykey[1].str == NULL)
113                 delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2;
114 #  endif
115 #  ifdef CONFIG_AUTOBOOT_STOP_STR
116         if (delaykey[2].str == NULL)
117                 delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR;
118 #  endif
119 #  ifdef CONFIG_AUTOBOOT_STOP_STR2
120         if (delaykey[3].str == NULL)
121                 delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2;
122 #  endif
123
124         for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) {
125                 delaykey[i].len = delaykey[i].str == NULL ?
126                                     0 : strlen (delaykey[i].str);
127                 delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ?
128                                     MAX_DELAY_STOP_STR : delaykey[i].len;
129
130                 presskey_max = presskey_max > delaykey[i].len ?
131                                     presskey_max : delaykey[i].len;
132
133 #  if DEBUG_BOOTKEYS
134                 printf("%s key:<%s>\n",
135                        delaykey[i].retry ? "delay" : "stop",
136                        delaykey[i].str ? delaykey[i].str : "NULL");
137 #  endif
138         }
139
140         /* In order to keep up with incoming data, check timeout only
141          * when catch up.
142          */
143         while (!abort && get_ticks() <= etime) {
144                 for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) {
145                         if (delaykey[i].len > 0 &&
146                             presskey_len >= delaykey[i].len &&
147                             memcmp (presskey + presskey_len - delaykey[i].len,
148                                     delaykey[i].str,
149                                     delaykey[i].len) == 0) {
150 #  if DEBUG_BOOTKEYS
151                                 printf("got %skey\n",
152                                        delaykey[i].retry ? "delay" : "stop");
153 #  endif
154
155 #  ifdef CONFIG_BOOT_RETRY_TIME
156                                 /* don't retry auto boot */
157                                 if (! delaykey[i].retry)
158                                         retry_time = -1;
159 #  endif
160                                 abort = 1;
161                         }
162                 }
163
164                 if (tstc()) {
165                         if (presskey_len < presskey_max) {
166                                 presskey [presskey_len ++] = getc();
167                         }
168                         else {
169                                 for (i = 0; i < presskey_max - 1; i ++)
170                                         presskey [i] = presskey [i + 1];
171
172                                 presskey [i] = getc();
173                         }
174                 }
175         }
176 #  if DEBUG_BOOTKEYS
177         if (!abort)
178                 printf("key timeout\n");
179 #  endif
180
181         return abort;
182 }
183
184 # else  /* !defined(CONFIG_AUTOBOOT_KEYED) */
185
186 #ifdef CONFIG_MENUKEY
187 static int menukey = 0;
188 #endif
189
190 static __inline__ int abortboot(int bootdelay)
191 {
192         int abort = 0;
193
194 #ifdef CONFIG_MENUPROMPT
195         printf(CONFIG_MENUPROMPT, bootdelay);
196 #else
197         printf("Hit any key to stop autoboot: %2d ", bootdelay);
198 #endif
199
200 #if defined CONFIG_ZERO_BOOTDELAY_CHECK
201         /*
202          * Check if key already pressed
203          * Don't check if bootdelay < 0
204          */
205         if (bootdelay >= 0) {
206                 if (tstc()) {   /* we got a key press   */
207                         (void) getc();  /* consume input        */
208                         printf ("\b\b\b 0\n");
209                         return 1;       /* don't auto boot      */
210                 }
211         }
212 #endif
213
214         while (bootdelay > 0) {
215                 int i;
216
217                 --bootdelay;
218                 /* delay 100 * 10ms */
219                 for (i=0; !abort && i<100; ++i) {
220                         if (tstc()) {   /* we got a key press   */
221                                 abort  = 1;     /* don't auto boot      */
222                                 bootdelay = 0;  /* no more delay        */
223 # ifdef CONFIG_MENUKEY
224                                 menukey = getc();
225 # else
226                                 (void) getc();  /* consume input        */
227 # endif
228                                 break;
229                         }
230                         udelay (10000);
231                 }
232
233                 printf ("\b\b\b%2d ", bootdelay);
234         }
235
236         putc ('\n');
237
238         return abort;
239 }
240 # endif /* CONFIG_AUTOBOOT_KEYED */
241 #endif  /* CONFIG_BOOTDELAY >= 0  */
242
243 /****************************************************************************/
244
245 void main_loop (void)
246 {
247 #ifndef CFG_HUSH_PARSER
248         static char lastcommand[CFG_CBSIZE] = { 0, };
249         int len;
250         int rc = 1;
251         int flag;
252 #endif
253
254 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
255         char *s;
256         int bootdelay;
257 #endif
258 #ifdef CONFIG_PREBOOT
259         char *p;
260 #endif
261
262 #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
263         ulong bmp = 0;          /* default bitmap */
264         extern int trab_vfd (ulong bitmap);
265
266 #ifdef CONFIG_MODEM_SUPPORT
267         if (do_mdm_init)
268                 bmp = 1;        /* alternate bitmap */
269 #endif
270         trab_vfd (bmp);
271 #endif  /* CONFIG_VFD && VFD_TEST_LOGO */
272
273 #ifdef CONFIG_MODEM_SUPPORT
274         debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);
275         if (do_mdm_init) {
276                 uchar *str = strdup(getenv("mdm_cmd"));
277                 setenv ("preboot", str);  /* set or delete definition */
278                 if (str != NULL)
279                         free (str);
280                 mdm_init(); /* wait for modem connection */
281         }
282 #endif  /* CONFIG_MODEM_SUPPORT */
283
284 #ifdef CONFIG_VERSION_VARIABLE
285         {
286                 extern char version_string[];
287                 char *str = getenv("ver");
288
289                 if (!str)
290                         setenv ("ver", version_string);  /* set version variable */
291         }
292 #endif /* CONFIG_VERSION_VARIABLE */
293
294 #ifdef CFG_HUSH_PARSER
295         u_boot_hush_start ();
296 #endif
297
298 #ifdef CONFIG_PREBOOT
299         if ((p = getenv ("preboot")) != NULL) {
300 # ifdef CONFIG_AUTOBOOT_KEYED
301                 int prev = disable_ctrlc(1);    /* disable Control C checking */
302 # endif
303
304 # ifndef CFG_HUSH_PARSER
305                 run_command (p, 0);
306 # else
307                 parse_string_outer(p, FLAG_PARSE_SEMICOLON |
308                                     FLAG_EXIT_FROM_LOOP);
309 # endif
310
311 # ifdef CONFIG_AUTOBOOT_KEYED
312                 disable_ctrlc(prev);    /* restore Control C checking */
313 # endif
314         }
315 #endif /* CONFIG_PREBOOT */
316
317 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
318         s = getenv ("bootdelay");
319         bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
320
321         debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
322
323 # ifdef CONFIG_BOOT_RETRY_TIME
324         init_cmd_timeout ();
325 # endif /* CONFIG_BOOT_RETRY_TIME */
326
327         s = getenv ("bootcmd");
328
329         debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
330
331         if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
332 # ifdef CONFIG_AUTOBOOT_KEYED
333                 int prev = disable_ctrlc(1);    /* disable Control C checking */
334 # endif
335
336 # ifndef CFG_HUSH_PARSER
337                 run_command (s, 0);
338 # else
339                 parse_string_outer(s, FLAG_PARSE_SEMICOLON |
340                                     FLAG_EXIT_FROM_LOOP);
341 # endif
342
343 # ifdef CONFIG_AUTOBOOT_KEYED
344                 disable_ctrlc(prev);    /* restore Control C checking */
345 # endif
346         }
347
348 # ifdef CONFIG_MENUKEY
349         if (menukey == CONFIG_MENUKEY) {
350             s = getenv("menucmd");
351             if (s) {
352 # ifndef CFG_HUSH_PARSER
353                 run_command (s, bd, 0);
354 # else
355                 parse_string_outer(s, FLAG_PARSE_SEMICOLON |
356                                     FLAG_EXIT_FROM_LOOP);
357 # endif
358             }
359         }
360 #endif /* CONFIG_MENUKEY */
361 #endif  /* CONFIG_BOOTDELAY */
362
363 #ifdef CONFIG_AMIGAONEG3SE
364         {
365             extern void video_banner(void);
366             video_banner();
367         }
368 #endif
369
370         /*
371          * Main Loop for Monitor Command Processing
372          */
373 #ifdef CFG_HUSH_PARSER
374         parse_file_outer();
375         /* This point is never reached */
376         for (;;);
377 #else
378         for (;;) {
379 #ifdef CONFIG_BOOT_RETRY_TIME
380                 if (rc >= 0) {
381                         /* Saw enough of a valid command to
382                          * restart the timeout.
383                          */
384                         reset_cmd_timeout();
385                 }
386 #endif
387                 len = readline (CFG_PROMPT);
388
389                 flag = 0;       /* assume no special flags for now */
390                 if (len > 0)
391                         strcpy (lastcommand, console_buffer);
392                 else if (len == 0)
393                         flag |= CMD_FLAG_REPEAT;
394 #ifdef CONFIG_BOOT_RETRY_TIME
395                 else if (len == -2) {
396                         /* -2 means timed out, retry autoboot
397                          */
398                         printf("\nTimed out waiting for command\n");
399 # ifdef CONFIG_RESET_TO_RETRY
400                         /* Reinit board to run initialization code again */
401                         do_reset (NULL, 0, 0, NULL);
402 # else
403                         return;         /* retry autoboot */
404 # endif
405                 }
406 #endif
407
408                 if (len == -1)
409                         printf ("<INTERRUPT>\n");
410                 else
411                         rc = run_command (lastcommand, flag);
412
413                 if (rc <= 0) {
414                         /* invalid command or not repeatable, forget it */
415                         lastcommand[0] = 0;
416                 }
417         }
418 #endif /*CFG_HUSH_PARSER*/
419 }
420
421 #ifdef CONFIG_BOOT_RETRY_TIME
422 /***************************************************************************
423  * initialise command line timeout
424  */
425 void init_cmd_timeout(void)
426 {
427         char *s = getenv ("bootretry");
428
429         if (s != NULL)
430                 retry_time = (int)simple_strtoul(s, NULL, 10);
431         else
432                 retry_time =  CONFIG_BOOT_RETRY_TIME;
433
434         if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN)
435                 retry_time = CONFIG_BOOT_RETRY_MIN;
436 }
437
438 /***************************************************************************
439  * reset command line timeout to retry_time seconds
440  */
441 void reset_cmd_timeout(void)
442 {
443         endtime = endtick(retry_time);
444 }
445 #endif
446
447 /****************************************************************************/
448
449 /*
450  * Prompt for input and read a line.
451  * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
452  * time out when time goes past endtime (timebase time in ticks).
453  * Return:      number of read characters
454  *              -1 if break
455  *              -2 if timed out
456  */
457 int readline (const char *const prompt)
458 {
459         char   *p = console_buffer;
460         int     n = 0;                          /* buffer index         */
461         int     plen = 0;                       /* prompt length        */
462         int     col;                            /* output column cnt    */
463         char    c;
464
465         /* print prompt */
466         if (prompt) {
467                 plen = strlen (prompt);
468                 puts (prompt);
469         }
470         col = plen;
471
472         for (;;) {
473 #ifdef CONFIG_BOOT_RETRY_TIME
474                 while (!tstc()) {       /* while no incoming data */
475                         if (retry_time >= 0 && get_ticks() > endtime)
476                                 return (-2);    /* timed out */
477                 }
478 #endif
479                 WATCHDOG_RESET();               /* Trigger watchdog, if needed */
480
481 #ifdef CONFIG_SHOW_ACTIVITY
482                 while (!tstc()) {
483                         extern void show_activity(int arg);
484                         show_activity(0);
485                 }
486 #endif
487                 c = getc();
488
489                 /*
490                  * Special character handling
491                  */
492                 switch (c) {
493                 case '\r':                              /* Enter                */
494                 case '\n':
495                         *p = '\0';
496                         puts ("\r\n");
497                         return (p - console_buffer);
498
499                 case 0x03:                              /* ^C - break           */
500                         console_buffer[0] = '\0';       /* discard input */
501                         return (-1);
502
503                 case 0x15:                              /* ^U - erase line      */
504                         while (col > plen) {
505                                 puts (erase_seq);
506                                 --col;
507                         }
508                         p = console_buffer;
509                         n = 0;
510                         continue;
511
512                 case 0x17:                              /* ^W - erase word      */
513                         p=delete_char(console_buffer, p, &col, &n, plen);
514                         while ((n > 0) && (*p != ' ')) {
515                                 p=delete_char(console_buffer, p, &col, &n, plen);
516                         }
517                         continue;
518
519                 case 0x08:                              /* ^H  - backspace      */
520                 case 0x7F:                              /* DEL - backspace      */
521                         p=delete_char(console_buffer, p, &col, &n, plen);
522                         continue;
523
524                 default:
525                         /*
526                          * Must be a normal character then
527                          */
528                         if (n < CFG_CBSIZE-2) {
529                                 if (c == '\t') {        /* expand TABs          */
530                                         puts (tab_seq+(col&07));
531                                         col += 8 - (col&07);
532                                 } else {
533                                         ++col;          /* echo input           */
534                                         putc (c);
535                                 }
536                                 *p++ = c;
537                                 ++n;
538                         } else {                        /* Buffer full          */
539                                 putc ('\a');
540                         }
541                 }
542         }
543 }
544
545 /****************************************************************************/
546
547 static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen)
548 {
549         char *s;
550
551         if (*np == 0) {
552                 return (p);
553         }
554
555         if (*(--p) == '\t') {                   /* will retype the whole line   */
556                 while (*colp > plen) {
557                         puts (erase_seq);
558                         (*colp)--;
559                 }
560                 for (s=buffer; s<p; ++s) {
561                         if (*s == '\t') {
562                                 puts (tab_seq+((*colp) & 07));
563                                 *colp += 8 - ((*colp) & 07);
564                         } else {
565                                 ++(*colp);
566                                 putc (*s);
567                         }
568                 }
569         } else {
570                 puts (erase_seq);
571                 (*colp)--;
572         }
573         (*np)--;
574         return (p);
575 }
576
577 /****************************************************************************/
578
579 int parse_line (char *line, char *argv[])
580 {
581         int nargs = 0;
582
583 #ifdef DEBUG_PARSER
584         printf ("parse_line: \"%s\"\n", line);
585 #endif
586         while (nargs < CFG_MAXARGS) {
587
588                 /* skip any white space */
589                 while ((*line == ' ') || (*line == '\t')) {
590                         ++line;
591                 }
592
593                 if (*line == '\0') {    /* end of line, no more args    */
594                         argv[nargs] = NULL;
595 #ifdef DEBUG_PARSER
596                 printf ("parse_line: nargs=%d\n", nargs);
597 #endif
598                         return (nargs);
599                 }
600
601                 argv[nargs++] = line;   /* begin of argument string     */
602
603                 /* find end of string */
604                 while (*line && (*line != ' ') && (*line != '\t')) {
605                         ++line;
606                 }
607
608                 if (*line == '\0') {    /* end of line, no more args    */
609                         argv[nargs] = NULL;
610 #ifdef DEBUG_PARSER
611                 printf ("parse_line: nargs=%d\n", nargs);
612 #endif
613                         return (nargs);
614                 }
615
616                 *line++ = '\0';         /* terminate current arg         */
617         }
618
619         printf ("** Too many args (max. %d) **\n", CFG_MAXARGS);
620
621 #ifdef DEBUG_PARSER
622         printf ("parse_line: nargs=%d\n", nargs);
623 #endif
624         return (nargs);
625 }
626
627 /****************************************************************************/
628
629 static void process_macros (const char *input, char *output)
630 {
631         char c, prev;
632         const char *varname_start = NULL;
633         int inputcnt  = strlen (input);
634         int outputcnt = CFG_CBSIZE;
635         int state = 0;  /* 0 = waiting for '$'  */
636                         /* 1 = waiting for '('  */
637                         /* 2 = waiting for ')'  */
638                         /* 3 = waiting for '''  */
639 #ifdef DEBUG_PARSER
640         char *output_start = output;
641
642         printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(input), input);
643 #endif
644
645         prev = '\0';                    /* previous character   */
646
647         while (inputcnt && outputcnt) {
648             c = *input++;
649             inputcnt--;
650
651             if (state!=3) {
652             /* remove one level of escape characters */
653             if ((c == '\\') && (prev != '\\')) {
654                 if (inputcnt-- == 0)
655                         break;
656                 prev = c;
657                 c = *input++;
658             }
659             }
660
661             switch (state) {
662             case 0:                     /* Waiting for (unescaped) $    */
663                 if ((c == '\'') && (prev != '\\')) {
664                         state = 3;
665                         break;
666                 }
667                 if ((c == '$') && (prev != '\\')) {
668                         state++;
669                 } else {
670                         *(output++) = c;
671                         outputcnt--;
672                 }
673                 break;
674             case 1:                     /* Waiting for (        */
675                 if (c == '(') {
676                         state++;
677                         varname_start = input;
678                 } else {
679                         state = 0;
680                         *(output++) = '$';
681                         outputcnt--;
682
683                         if (outputcnt) {
684                                 *(output++) = c;
685                                 outputcnt--;
686                         }
687                 }
688                 break;
689             case 2:                     /* Waiting for )        */
690                 if (c == ')') {
691                         int i;
692                         char envname[CFG_CBSIZE], *envval;
693                         int envcnt = input-varname_start-1; /* Varname # of chars */
694
695                         /* Get the varname */
696                         for (i = 0; i < envcnt; i++) {
697                                 envname[i] = varname_start[i];
698                         }
699                         envname[i] = 0;
700
701                         /* Get its value */
702                         envval = getenv (envname);
703
704                         /* Copy into the line if it exists */
705                         if (envval != NULL)
706                                 while ((*envval) && outputcnt) {
707                                         *(output++) = *(envval++);
708                                         outputcnt--;
709                                 }
710                         /* Look for another '$' */
711                         state = 0;
712                 }
713                 break;
714             case 3:                     /* Waiting for '        */
715                 if ((c == '\'') && (prev != '\\')) {
716                         state = 0;
717                 } else {
718                         *(output++) = c;
719                         outputcnt--;
720                 }
721                 break;
722             }
723             prev = c;
724         }
725
726         if (outputcnt)
727                 *output = 0;
728
729 #ifdef DEBUG_PARSER
730         printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
731                 strlen(output_start), output_start);
732 #endif
733 }
734
735 /****************************************************************************
736  * returns:
737  *      1  - command executed, repeatable
738  *      0  - command executed but not repeatable, interrupted commands are
739  *           always considered not repeatable
740  *      -1 - not executed (unrecognized, bootd recursion or too many args)
741  *           (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is
742  *           considered unrecognized)
743  *
744  * WARNING:
745  *
746  * We must create a temporary copy of the command since the command we get
747  * may be the result from getenv(), which returns a pointer directly to
748  * the environment data, which may change magicly when the command we run
749  * creates or modifies environment variables (like "bootp" does).
750  */
751
752 int run_command (const char *cmd, int flag)
753 {
754         cmd_tbl_t *cmdtp;
755         char cmdbuf[CFG_CBSIZE];        /* working copy of cmd          */
756         char *token;                    /* start of token in cmdbuf     */
757         char *sep;                      /* end of token (separator) in cmdbuf */
758         char finaltoken[CFG_CBSIZE];
759         char *str = cmdbuf;
760         char *argv[CFG_MAXARGS + 1];    /* NULL terminated      */
761         int argc, inquotes;
762         int repeatable = 1;
763         int rc = 0;
764
765 #ifdef DEBUG_PARSER
766         printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
767         puts (cmd ? cmd : "NULL");      /* use puts - string may be loooong */
768         puts ("\"\n");
769 #endif
770
771         clear_ctrlc();          /* forget any previous Control C */
772
773         if (!cmd || !*cmd) {
774                 return -1;      /* empty command */
775         }
776
777         if (strlen(cmd) >= CFG_CBSIZE) {
778                 puts ("## Command too long!\n");
779                 return -1;
780         }
781
782         strcpy (cmdbuf, cmd);
783
784         /* Process separators and check for invalid
785          * repeatable commands
786          */
787
788 #ifdef DEBUG_PARSER
789         printf ("[PROCESS_SEPARATORS] %s\n", cmd);
790 #endif
791         while (*str) {
792
793                 /*
794                  * Find separator, or string end
795                  * Allow simple escape of ';' by writing "\;"
796                  */
797                 for (inquotes = 0, sep = str; *sep; sep++) {
798                         if ((*sep=='\'') &&
799                             (*(sep-1) != '\\'))
800                                 inquotes=!inquotes;
801
802                         if (!inquotes &&
803                             (*sep == ';') &&    /* separator            */
804                             ( sep != str) &&    /* past string start    */
805                             (*(sep-1) != '\\')) /* and NOT escaped      */
806                                 break;
807                 }
808
809                 /*
810                  * Limit the token to data between separators
811                  */
812                 token = str;
813                 if (*sep) {
814                         str = sep + 1;  /* start of command for next pass */
815                         *sep = '\0';
816                 }
817                 else
818                         str = sep;      /* no more commands for next pass */
819 #ifdef DEBUG_PARSER
820                 printf ("token: \"%s\"\n", token);
821 #endif
822
823                 /* find macros in this token and replace them */
824                 process_macros (token, finaltoken);
825
826                 /* Extract arguments */
827                 argc = parse_line (finaltoken, argv);
828
829                 /* Look up command in command table */
830                 if ((cmdtp = find_cmd(argv[0])) == NULL) {
831                         printf ("Unknown command '%s' - try 'help'\n", argv[0]);
832                         rc = -1;        /* give up after bad command */
833                         continue;
834                 }
835
836                 /* found - check max args */
837                 if (argc > cmdtp->maxargs) {
838                         printf ("Usage:\n%s\n", cmdtp->usage);
839                         rc = -1;
840                         continue;
841                 }
842
843 #if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
844                 /* avoid "bootd" recursion */
845                 if (cmdtp->cmd == do_bootd) {
846 #ifdef DEBUG_PARSER
847                         printf ("[%s]\n", finaltoken);
848 #endif
849                         if (flag & CMD_FLAG_BOOTD) {
850                                 printf ("'bootd' recursion detected\n");
851                                 rc = -1;
852                                 continue;
853                         }
854                         else
855                                 flag |= CMD_FLAG_BOOTD;
856                 }
857 #endif  /* CFG_CMD_BOOTD */
858
859                 /* OK - call function to do the command */
860                 if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
861                         rc = -1;
862                 }
863
864                 repeatable &= cmdtp->repeatable;
865
866                 /* Did the user stop this? */
867                 if (had_ctrlc ())
868                         return 0;       /* if stopped then not repeatable */
869         }
870
871         return rc ? rc : repeatable;
872 }
873
874 /****************************************************************************/
875
876 #if (CONFIG_COMMANDS & CFG_CMD_RUN)
877 int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
878 {
879         int i;
880
881         if (argc < 2) {
882                 printf ("Usage:\n%s\n", cmdtp->usage);
883                 return 1;
884         }
885
886         for (i=1; i<argc; ++i) {
887                 char *arg;
888
889                 if ((arg = getenv (argv[i])) == NULL) {
890                         printf ("## Error: \"%s\" not defined\n", argv[i]);
891                         return 1;
892                 }
893 #ifndef CFG_HUSH_PARSER
894                 if (run_command (arg, flag) == -1)
895                         return 1;
896 #else
897                 if (parse_string_outer(arg,
898                     FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)
899                         return 1;
900 #endif
901         }
902         return 0;
903 }
904 #endif  /* CFG_CMD_RUN */