xref: /rk3399_rockchip-uboot/common/main.c (revision 983bebbea304f65daf5baa9a913e66ff6e8fa6ba)
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 #ifdef CONFIG_MODEM_SUPPORT
30 #include <malloc.h>		/* for free() prototype */
31 #endif
32 
33 #ifdef CFG_HUSH_PARSER
34 #include <hush.h>
35 #endif
36 
37 #include <post.h>
38 
39 #ifdef CONFIG_SILENT_CONSOLE
40 DECLARE_GLOBAL_DATA_PTR;
41 #endif
42 
43 #if defined(CONFIG_BOOT_RETRY_TIME) && defined(CONFIG_RESET_TO_RETRY)
44 extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);		/* for do_reset() prototype */
45 #endif
46 
47 extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
48 
49 
50 #define MAX_DELAY_STOP_STR 32
51 
52 static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen);
53 static int parse_line (char *, char *[]);
54 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
55 static int abortboot(int);
56 #endif
57 
58 #undef DEBUG_PARSER
59 
60 char        console_buffer[CFG_CBSIZE];		/* console I/O buffer	*/
61 
62 static char erase_seq[] = "\b \b";		/* erase sequence	*/
63 static char   tab_seq[] = "        ";		/* used to expand TABs	*/
64 
65 #ifdef CONFIG_BOOT_RETRY_TIME
66 static uint64_t endtime = 0;  /* must be set, default is instant timeout */
67 static 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
77 int do_mdm_init = 0;
78 extern 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)
88 static __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 
111 #ifdef CONFIG_SILENT_CONSOLE
112 	if (gd->flags & GD_FLG_SILENT) {
113 		/* Restore serial console */
114 		console_assign (stdout, "serial");
115 		console_assign (stderr, "serial");
116 	}
117 #endif
118 
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,
164 				    delaykey[i].str,
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)
194 		puts ("key timeout\n");
195 #  endif
196 
197 #ifdef CONFIG_SILENT_CONSOLE
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");
205 	}
206 #endif
207 
208 	return abort;
209 }
210 
211 # else	/* !defined(CONFIG_AUTOBOOT_KEYED) */
212 
213 #ifdef CONFIG_MENUKEY
214 static int menukey = 0;
215 #endif
216 
217 static __inline__ int abortboot(int bootdelay)
218 {
219 	int abort = 0;
220 
221 #ifdef CONFIG_SILENT_CONSOLE
222 	if (gd->flags & GD_FLG_SILENT) {
223 		/* Restore serial console */
224 		console_assign (stdout, "serial");
225 		console_assign (stderr, "serial");
226 	}
227 #endif
228 
229 #ifdef CONFIG_MENUPROMPT
230 	printf(CONFIG_MENUPROMPT, bootdelay);
231 #else
232 	printf("Hit any key to stop autoboot: %2d ", bootdelay);
233 #endif
234 
235 #if defined CONFIG_ZERO_BOOTDELAY_CHECK
236 	/*
237 	 * Check if key already pressed
238 	 * Don't check if bootdelay < 0
239 	 */
240 	if (bootdelay >= 0) {
241 		if (tstc()) {	/* we got a key press	*/
242 			(void) getc();  /* consume input	*/
243 			puts ("\b\b\b 0");
244 			abort = 1; 	/* don't auto boot	*/
245 		}
246 	}
247 #endif
248 
249 	while ((bootdelay > 0) && (!abort)) {
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	*/
258 # ifdef CONFIG_MENUKEY
259 				menukey = getc();
260 # else
261 				(void) getc();  /* consume input	*/
262 # endif
263 				break;
264 			}
265 			udelay (10000);
266 		}
267 
268 		printf ("\b\b\b%2d ", bootdelay);
269 	}
270 
271 	putc ('\n');
272 
273 #ifdef CONFIG_SILENT_CONSOLE
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");
281 	}
282 #endif
283 
284 	return abort;
285 }
286 # endif	/* CONFIG_AUTOBOOT_KEYED */
287 #endif	/* CONFIG_BOOTDELAY >= 0  */
288 
289 /****************************************************************************/
290 
291 void 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
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 */
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 
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 
335 #ifdef CONFIG_MODEM_SUPPORT
336 	debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);
337 	if (do_mdm_init) {
338 		char *str = strdup(getenv("mdm_cmd"));
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 
346 #ifdef CONFIG_VERSION_VARIABLE
347 	{
348 		extern char version_string[];
349 
350 		setenv ("ver", version_string);  /* set version variable */
351 	}
352 #endif /* CONFIG_VERSION_VARIABLE */
353 
354 #ifdef CFG_HUSH_PARSER
355 	u_boot_hush_start ();
356 #endif
357 
358 #ifdef CONFIG_AUTO_COMPLETE
359 	install_auto_complete();
360 #endif
361 
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 
385 	debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
386 
387 # ifdef CONFIG_BOOT_RETRY_TIME
388 	init_cmd_timeout ();
389 # endif	/* CONFIG_BOOT_RETRY_TIME */
390 
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");
400 
401 	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
402 
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 	}
419 
420 # ifdef CONFIG_MENUKEY
421 	if (menukey == CONFIG_MENUKEY) {
422 	    s = getenv("menucmd");
423 	    if (s) {
424 # ifndef CFG_HUSH_PARSER
425 		run_command (s, 0);
426 # else
427 		parse_string_outer(s, FLAG_PARSE_SEMICOLON |
428 				    FLAG_EXIT_FROM_LOOP);
429 # endif
430 	    }
431 	}
432 #endif /* CONFIG_MENUKEY */
433 #endif	/* CONFIG_BOOTDELAY */
434 
435 #ifdef CONFIG_AMIGAONEG3SE
436 	{
437 	    extern void video_banner(void);
438 	    video_banner();
439 	}
440 #endif
441 
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 			 */
470 			puts ("\nTimed out waiting for command\n");
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)
481 			puts ("<INTERRUPT>\n");
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 
493 #ifdef CONFIG_BOOT_RETRY_TIME
494 /***************************************************************************
495  * initialise command line timeout
496  */
497 void init_cmd_timeout(void)
498 {
499 	char *s = getenv ("bootretry");
500 
501 	if (s != NULL)
502 		retry_time = (int)simple_strtol(s, NULL, 10);
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 
510 /***************************************************************************
511  * reset command line timeout to retry_time seconds
512  */
513 void 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  */
529 int 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 
571 		case '\0':				/* nul			*/
572 			continue;
573 
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		*/
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
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 
630 static 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 
662 int 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 
712 static 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 '$'	*/
719 			/* 1 = waiting for '(' or '{' */
720 			/* 2 = waiting for ')' or '}' */
721 			/* 3 = waiting for '''  */
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 
734 	    if (state!=3) {
735 	    /* remove one level of escape characters */
736 	    if ((c == '\\') && (prev != '\\')) {
737 		if (inputcnt-- == 0)
738 			break;
739 		prev = c;
740 		c = *input++;
741 	    }
742 	    }
743 
744 	    switch (state) {
745 	    case 0:			/* Waiting for (unescaped) $	*/
746 		if ((c == '\'') && (prev != '\\')) {
747 			state = 3;
748 			break;
749 		}
750 		if ((c == '$') && (prev != '\\')) {
751 			state++;
752 		} else {
753 			*(output++) = c;
754 			outputcnt--;
755 		}
756 		break;
757 	    case 1:			/* Waiting for (	*/
758 		if (c == '(' || c == '{') {
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 )	*/
773 		if (c == ')' || c == '}') {
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;
797 	    case 3:			/* Waiting for '	*/
798 		if ((c == '\'') && (prev != '\\')) {
799 			state = 0;
800 		} else {
801 			*(output++) = c;
802 			outputcnt--;
803 		}
804 		break;
805 	    }
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 
835 int 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	*/
844 	int argc, inquotes;
845 	int repeatable = 1;
846 	int rc = 0;
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 		 */
880 		for (inquotes = 0, sep = str; *sep; sep++) {
881 			if ((*sep=='\'') &&
882 			    (*(sep-1) != '\\'))
883 				inquotes=!inquotes;
884 
885 			if (!inquotes &&
886 			    (*sep == ';') &&	/* separator		*/
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 */
910 		if ((argc = parse_line (finaltoken, argv)) == 0) {
911 			rc = -1;	/* no command at all */
912 			continue;
913 		}
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]);
918 			rc = -1;	/* give up after bad command */
919 			continue;
920 		}
921 
922 		/* found - check max args */
923 		if (argc > cmdtp->maxargs) {
924 			printf ("Usage:\n%s\n", cmdtp->usage);
925 			rc = -1;
926 			continue;
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) {
936 				puts ("'bootd' recursion detected\n");
937 				rc = -1;
938 				continue;
939 			} else {
940 				flag |= CMD_FLAG_BOOTD;
941 			}
942 		}
943 #endif	/* CFG_CMD_BOOTD */
944 
945 		/* OK - call function to do the command */
946 		if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
947 			rc = -1;
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 
957 	return rc ? rc : repeatable;
958 }
959 
960 /****************************************************************************/
961 
962 #if (CONFIG_COMMANDS & CFG_CMD_RUN)
963 int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
964 {
965 	int i;
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) {
973 		char *arg;
974 
975 		if ((arg = getenv (argv[i])) == NULL) {
976 			printf ("## Error: \"%s\" not defined\n", argv[i]);
977 			return 1;
978 		}
979 #ifndef CFG_HUSH_PARSER
980 		if (run_command (arg, flag) == -1)
981 			return 1;
982 #else
983 		if (parse_string_outer(arg,
984 		    FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)
985 			return 1;
986 #endif
987 	}
988 	return 0;
989 }
990 #endif	/* CFG_CMD_RUN */
991