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