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