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