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