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