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