main.c 31.5 KB
Newer Older
wdenk's avatar
wdenk committed
1 2 3 4
/*
 * (C) Copyright 2000
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
5 6 7 8
 * Add to readline cmdline-editing by
 * (C) Copyright 2005
 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
 *
wdenk's avatar
wdenk committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

28 29
/* #define	DEBUG	*/

wdenk's avatar
wdenk committed
30 31 32
#include <common.h>
#include <watchdog.h>
#include <command.h>
33
#include <malloc.h>
Andreas Bießmann's avatar
Andreas Bießmann committed
34
#include <version.h>
Wolfgang Denk's avatar
Wolfgang Denk committed
35 36 37
#ifdef CONFIG_MODEM_SUPPORT
#include <malloc.h>		/* for free() prototype */
#endif
wdenk's avatar
wdenk committed
38

39
#ifdef CONFIG_SYS_HUSH_PARSER
wdenk's avatar
wdenk committed
40 41 42
#include <hush.h>
#endif

43
#include <post.h>
44
#include <linux/ctype.h>
45
#include <menu.h>
46

47
#if defined(CONFIG_SILENT_CONSOLE) || defined(CONFIG_POST) || defined(CONFIG_CMDLINE_EDITING)
48 49 50
DECLARE_GLOBAL_DATA_PTR;
#endif

51 52 53 54
/*
 * Board-specific Platform code can reimplement show_boot_progress () if needed
 */
void inline __show_boot_progress (int val) {}
55
void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress")));
56

57
#if defined(CONFIG_UPDATE_TFTP)
58
int update_tftp (ulong addr);
59
#endif /* CONFIG_UPDATE_TFTP */
wdenk's avatar
wdenk committed
60

wdenk's avatar
wdenk committed
61 62 63 64
#define MAX_DELAY_STOP_STR 32

#undef DEBUG_PARSER

65
char        console_buffer[CONFIG_SYS_CBSIZE + 1];	/* console I/O buffer	*/
wdenk's avatar
wdenk committed
66

67
static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen);
68 69
static const char erase_seq[] = "\b \b";		/* erase sequence	*/
static const char   tab_seq[] = "        ";		/* used to expand TABs	*/
wdenk's avatar
wdenk committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

#ifdef CONFIG_BOOT_RETRY_TIME
static uint64_t endtime = 0;  /* must be set, default is instant timeout */
static int      retry_time = -1; /* -1 so can call readline before main_loop */
#endif

#define	endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk())

#ifndef CONFIG_BOOT_RETRY_MIN
#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME
#endif

#ifdef CONFIG_MODEM_SUPPORT
int do_mdm_init = 0;
extern void mdm_init(void); /* defined in board.c */
#endif

/***************************************************************************
 * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
89
 * returns: 0 -  no key string, allow autoboot 1 - got key string, abort
wdenk's avatar
wdenk committed
90 91 92
 */
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
# if defined(CONFIG_AUTOBOOT_KEYED)
93 94 95 96
#ifndef CONFIG_MENU
static inline
#endif
int abortboot(int bootdelay)
wdenk's avatar
wdenk committed
97 98 99
{
	int abort = 0;
	uint64_t etime = endtick(bootdelay);
Wolfgang Denk's avatar
Wolfgang Denk committed
100
	struct {
wdenk's avatar
wdenk committed
101 102 103 104
		char* str;
		u_int len;
		int retry;
	}
Wolfgang Denk's avatar
Wolfgang Denk committed
105
	delaykey [] = {
wdenk's avatar
wdenk committed
106 107 108 109 110 111 112 113 114 115 116 117
		{ str: getenv ("bootdelaykey"),  retry: 1 },
		{ str: getenv ("bootdelaykey2"), retry: 1 },
		{ str: getenv ("bootstopkey"),   retry: 0 },
		{ str: getenv ("bootstopkey2"),  retry: 0 },
	};

	char presskey [MAX_DELAY_STOP_STR];
	u_int presskey_len = 0;
	u_int presskey_max = 0;
	u_int i;

#  ifdef CONFIG_AUTOBOOT_PROMPT
Stefan Roese's avatar
Stefan Roese committed
118
	printf(CONFIG_AUTOBOOT_PROMPT);
wdenk's avatar
wdenk committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
#  endif

#  ifdef CONFIG_AUTOBOOT_DELAY_STR
	if (delaykey[0].str == NULL)
		delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
#  endif
#  ifdef CONFIG_AUTOBOOT_DELAY_STR2
	if (delaykey[1].str == NULL)
		delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2;
#  endif
#  ifdef CONFIG_AUTOBOOT_STOP_STR
	if (delaykey[2].str == NULL)
		delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR;
#  endif
#  ifdef CONFIG_AUTOBOOT_STOP_STR2
	if (delaykey[3].str == NULL)
		delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2;
#  endif

	for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) {
		delaykey[i].len = delaykey[i].str == NULL ?
				    0 : strlen (delaykey[i].str);
		delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ?
				    MAX_DELAY_STOP_STR : delaykey[i].len;

		presskey_max = presskey_max > delaykey[i].len ?
				    presskey_max : delaykey[i].len;

#  if DEBUG_BOOTKEYS
		printf("%s key:<%s>\n",
		       delaykey[i].retry ? "delay" : "stop",
		       delaykey[i].str ? delaykey[i].str : "NULL");
#  endif
	}

	/* In order to keep up with incoming data, check timeout only
	 * when catch up.
	 */
157 158 159 160 161 162 163 164 165 166 167 168 169
	do {
		if (tstc()) {
			if (presskey_len < presskey_max) {
				presskey [presskey_len ++] = getc();
			}
			else {
				for (i = 0; i < presskey_max - 1; i ++)
					presskey [i] = presskey [i + 1];

				presskey [i] = getc();
			}
		}

wdenk's avatar
wdenk committed
170 171 172 173
		for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) {
			if (delaykey[i].len > 0 &&
			    presskey_len >= delaykey[i].len &&
			    memcmp (presskey + presskey_len - delaykey[i].len,
wdenk's avatar
wdenk committed
174
				    delaykey[i].str,
wdenk's avatar
wdenk committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188
				    delaykey[i].len) == 0) {
#  if DEBUG_BOOTKEYS
				printf("got %skey\n",
				       delaykey[i].retry ? "delay" : "stop");
#  endif

#  ifdef CONFIG_BOOT_RETRY_TIME
				/* don't retry auto boot */
				if (! delaykey[i].retry)
					retry_time = -1;
#  endif
				abort = 1;
			}
		}
189
	} while (!abort && get_ticks() <= etime);
wdenk's avatar
wdenk committed
190 191 192

#  if DEBUG_BOOTKEYS
	if (!abort)
193
		puts("key timeout\n");
wdenk's avatar
wdenk committed
194 195
#  endif

196
#ifdef CONFIG_SILENT_CONSOLE
197 198
	if (abort)
		gd->flags &= ~GD_FLG_SILENT;
199 200
#endif

wdenk's avatar
wdenk committed
201 202 203 204 205
	return abort;
}

# else	/* !defined(CONFIG_AUTOBOOT_KEYED) */

206 207 208 209
#ifdef CONFIG_MENUKEY
static int menukey = 0;
#endif

210 211 212 213
#ifndef CONFIG_MENU
static inline
#endif
int abortboot(int bootdelay)
wdenk's avatar
wdenk committed
214 215 216
{
	int abort = 0;

217
#ifdef CONFIG_MENUPROMPT
Stefan Roese's avatar
Stefan Roese committed
218
	printf(CONFIG_MENUPROMPT);
219
#else
wdenk's avatar
wdenk committed
220
	printf("Hit any key to stop autoboot: %2d ", bootdelay);
221
#endif
wdenk's avatar
wdenk committed
222 223

#if defined CONFIG_ZERO_BOOTDELAY_CHECK
wdenk's avatar
wdenk committed
224 225 226 227
	/*
	 * Check if key already pressed
	 * Don't check if bootdelay < 0
	 */
wdenk's avatar
wdenk committed
228 229 230
	if (bootdelay >= 0) {
		if (tstc()) {	/* we got a key press	*/
			(void) getc();  /* consume input	*/
231
			puts ("\b\b\b 0");
232
			abort = 1;	/* don't auto boot	*/
wdenk's avatar
wdenk committed
233
		}
wdenk's avatar
wdenk committed
234
	}
wdenk's avatar
wdenk committed
235 236
#endif

237
	while ((bootdelay > 0) && (!abort)) {
wdenk's avatar
wdenk committed
238 239 240 241 242 243 244 245
		int i;

		--bootdelay;
		/* delay 100 * 10ms */
		for (i=0; !abort && i<100; ++i) {
			if (tstc()) {	/* we got a key press	*/
				abort  = 1;	/* don't auto boot	*/
				bootdelay = 0;	/* no more delay	*/
246 247 248
# ifdef CONFIG_MENUKEY
				menukey = getc();
# else
wdenk's avatar
wdenk committed
249
				(void) getc();  /* consume input	*/
250
# endif
wdenk's avatar
wdenk committed
251 252
				break;
			}
253
			udelay(10000);
wdenk's avatar
wdenk committed
254 255
		}

256
		printf("\b\b\b%2d ", bootdelay);
wdenk's avatar
wdenk committed
257 258
	}

259
	putc('\n');
wdenk's avatar
wdenk committed
260

261
#ifdef CONFIG_SILENT_CONSOLE
262 263
	if (abort)
		gd->flags &= ~GD_FLG_SILENT;
264 265
#endif

wdenk's avatar
wdenk committed
266 267 268 269 270 271 272 273 274
	return abort;
}
# endif	/* CONFIG_AUTOBOOT_KEYED */
#endif	/* CONFIG_BOOTDELAY >= 0  */

/****************************************************************************/

void main_loop (void)
{
275 276
#ifndef CONFIG_SYS_HUSH_PARSER
	static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
wdenk's avatar
wdenk committed
277 278 279 280 281 282 283 284 285 286 287 288
	int len;
	int rc = 1;
	int flag;
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	char *s;
	int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
	char *p;
#endif
289 290 291 292 293 294
#ifdef CONFIG_BOOTCOUNT_LIMIT
	unsigned long bootcount = 0;
	unsigned long bootlimit = 0;
	char *bcs;
	char bcs_set[16];
#endif /* CONFIG_BOOTCOUNT_LIMIT */
wdenk's avatar
wdenk committed
295

296 297 298 299 300 301 302 303 304 305
#ifdef CONFIG_BOOTCOUNT_LIMIT
	bootcount = bootcount_load();
	bootcount++;
	bootcount_store (bootcount);
	sprintf (bcs_set, "%lu", bootcount);
	setenv ("bootcount", bcs_set);
	bcs = getenv ("bootlimit");
	bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
#endif /* CONFIG_BOOTCOUNT_LIMIT */

wdenk's avatar
wdenk committed
306 307 308
#ifdef CONFIG_MODEM_SUPPORT
	debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);
	if (do_mdm_init) {
Wolfgang Denk's avatar
Wolfgang Denk committed
309
		char *str = strdup(getenv("mdm_cmd"));
wdenk's avatar
wdenk committed
310 311 312 313 314 315 316
		setenv ("preboot", str);  /* set or delete definition */
		if (str != NULL)
			free (str);
		mdm_init(); /* wait for modem connection */
	}
#endif  /* CONFIG_MODEM_SUPPORT */

317 318
#ifdef CONFIG_VERSION_VARIABLE
	{
319
		setenv ("ver", version_string);  /* set version variable */
320 321 322
	}
#endif /* CONFIG_VERSION_VARIABLE */

323
#ifdef CONFIG_SYS_HUSH_PARSER
wdenk's avatar
wdenk committed
324 325 326
	u_boot_hush_start ();
#endif

327 328 329 330
#if defined(CONFIG_HUSH_INIT_VAR)
	hush_init_var ();
#endif

wdenk's avatar
wdenk committed
331 332 333 334 335 336
#ifdef CONFIG_PREBOOT
	if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
		int prev = disable_ctrlc(1);	/* disable Control C checking */
# endif

337
		run_command_list(p, -1, 0);
wdenk's avatar
wdenk committed
338 339 340 341 342 343 344

# ifdef CONFIG_AUTOBOOT_KEYED
		disable_ctrlc(prev);	/* restore Control C checking */
# endif
	}
#endif /* CONFIG_PREBOOT */

345
#if defined(CONFIG_UPDATE_TFTP)
346
	update_tftp (0UL);
347 348
#endif /* CONFIG_UPDATE_TFTP */

wdenk's avatar
wdenk committed
349 350 351 352
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	s = getenv ("bootdelay");
	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

353
	debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
wdenk's avatar
wdenk committed
354

355 356 357
#if defined(CONFIG_MENU_SHOW)
	bootdelay = menu_show(bootdelay);
#endif
wdenk's avatar
wdenk committed
358
# ifdef CONFIG_BOOT_RETRY_TIME
wdenk's avatar
wdenk committed
359
	init_cmd_timeout ();
wdenk's avatar
wdenk committed
360 361
# endif	/* CONFIG_BOOT_RETRY_TIME */

362 363 364 365 366 367
#ifdef CONFIG_POST
	if (gd->flags & GD_FLG_POSTFAIL) {
		s = getenv("failbootcmd");
	}
	else
#endif /* CONFIG_POST */
368 369 370 371 372 373 374 375 376
#ifdef CONFIG_BOOTCOUNT_LIMIT
	if (bootlimit && (bootcount > bootlimit)) {
		printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
		        (unsigned)bootlimit);
		s = getenv ("altbootcmd");
	}
	else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
		s = getenv ("bootcmd");
377 378 379

	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

wdenk's avatar
wdenk committed
380 381 382 383 384
	if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
		int prev = disable_ctrlc(1);	/* disable Control C checking */
# endif

385
		run_command_list(s, -1, 0);
wdenk's avatar
wdenk committed
386 387 388 389 390

# ifdef CONFIG_AUTOBOOT_KEYED
		disable_ctrlc(prev);	/* restore Control C checking */
# endif
	}
391 392

# ifdef CONFIG_MENUKEY
393
	if (menukey == CONFIG_MENUKEY) {
394
		s = getenv("menucmd");
395
		if (s)
396
			run_command_list(s, -1, 0);
397 398
	}
#endif /* CONFIG_MENUKEY */
Wolfgang Denk's avatar
Wolfgang Denk committed
399
#endif /* CONFIG_BOOTDELAY */
400

wdenk's avatar
wdenk committed
401 402 403
	/*
	 * Main Loop for Monitor Command Processing
	 */
404
#ifdef CONFIG_SYS_HUSH_PARSER
wdenk's avatar
wdenk committed
405 406 407 408 409 410 411 412 413 414 415 416 417
	parse_file_outer();
	/* This point is never reached */
	for (;;);
#else
	for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
		if (rc >= 0) {
			/* Saw enough of a valid command to
			 * restart the timeout.
			 */
			reset_cmd_timeout();
		}
#endif
418
		len = readline (CONFIG_SYS_PROMPT);
wdenk's avatar
wdenk committed
419 420 421 422 423 424 425 426 427 428

		flag = 0;	/* assume no special flags for now */
		if (len > 0)
			strcpy (lastcommand, console_buffer);
		else if (len == 0)
			flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
		else if (len == -2) {
			/* -2 means timed out, retry autoboot
			 */
429
			puts ("\nTimed out waiting for command\n");
wdenk's avatar
wdenk committed
430 431 432 433 434 435 436 437 438 439
# ifdef CONFIG_RESET_TO_RETRY
			/* Reinit board to run initialization code again */
			do_reset (NULL, 0, 0, NULL);
# else
			return;		/* retry autoboot */
# endif
		}
#endif

		if (len == -1)
440
			puts ("<INTERRUPT>\n");
wdenk's avatar
wdenk committed
441
		else
Simon Glass's avatar
Simon Glass committed
442
			rc = run_command(lastcommand, flag);
wdenk's avatar
wdenk committed
443 444 445 446 447 448

		if (rc <= 0) {
			/* invalid command or not repeatable, forget it */
			lastcommand[0] = 0;
		}
	}
449
#endif /*CONFIG_SYS_HUSH_PARSER*/
wdenk's avatar
wdenk committed
450 451
}

wdenk's avatar
wdenk committed
452 453
#ifdef CONFIG_BOOT_RETRY_TIME
/***************************************************************************
Wolfgang Denk's avatar
Wolfgang Denk committed
454
 * initialize command line timeout
wdenk's avatar
wdenk committed
455 456 457 458 459 460
 */
void init_cmd_timeout(void)
{
	char *s = getenv ("bootretry");

	if (s != NULL)
wdenk's avatar
wdenk committed
461
		retry_time = (int)simple_strtol(s, NULL, 10);
wdenk's avatar
wdenk committed
462 463 464 465 466 467 468
	else
		retry_time =  CONFIG_BOOT_RETRY_TIME;

	if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN)
		retry_time = CONFIG_BOOT_RETRY_MIN;
}

wdenk's avatar
wdenk committed
469 470 471 472 473 474 475 476 477
/***************************************************************************
 * reset command line timeout to retry_time seconds
 */
void reset_cmd_timeout(void)
{
	endtime = endtick(retry_time);
}
#endif

478 479 480 481 482 483 484 485
#ifdef CONFIG_CMDLINE_EDITING

/*
 * cmdline-editing related codes from vivi.
 * Author: Janghoon Lyu <nandy@mizi.com>
 */

#define putnstr(str,n)	do {			\
Andrew Klossner's avatar
Andrew Klossner committed
486
		printf ("%.*s", (int)n, str);	\
487 488 489 490 491 492 493 494 495 496 497 498 499
	} while (0)

#define CTL_CH(c)		((c) - 'a' + 1)
#define CTL_BACKSPACE		('\b')
#define DEL			((char)255)
#define DEL7			((char)127)
#define CREAD_HIST_CHAR		('!')

#define getcmd_putch(ch)	putc(ch)
#define getcmd_getch()		getc()
#define getcmd_cbeep()		getcmd_putch('\a')

#define HIST_MAX		20
500
#define HIST_SIZE		CONFIG_SYS_CBSIZE
501 502 503 504 505 506 507

static int hist_max = 0;
static int hist_add_idx = 0;
static int hist_cur = -1;
unsigned hist_num = 0;

char* hist_list[HIST_MAX];
508
char hist_lines[HIST_MAX][HIST_SIZE + 1];	 /* Save room for NULL */
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581

#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)

static void hist_init(void)
{
	int i;

	hist_max = 0;
	hist_add_idx = 0;
	hist_cur = -1;
	hist_num = 0;

	for (i = 0; i < HIST_MAX; i++) {
		hist_list[i] = hist_lines[i];
		hist_list[i][0] = '\0';
	}
}

static void cread_add_to_hist(char *line)
{
	strcpy(hist_list[hist_add_idx], line);

	if (++hist_add_idx >= HIST_MAX)
		hist_add_idx = 0;

	if (hist_add_idx > hist_max)
		hist_max = hist_add_idx;

	hist_num++;
}

static char* hist_prev(void)
{
	char *ret;
	int old_cur;

	if (hist_cur < 0)
		return NULL;

	old_cur = hist_cur;
	if (--hist_cur < 0)
		hist_cur = hist_max;

	if (hist_cur == hist_add_idx) {
		hist_cur = old_cur;
		ret = NULL;
	} else
		ret = hist_list[hist_cur];

	return (ret);
}

static char* hist_next(void)
{
	char *ret;

	if (hist_cur < 0)
		return NULL;

	if (hist_cur == hist_add_idx)
		return NULL;

	if (++hist_cur > hist_max)
		hist_cur = 0;

	if (hist_cur == hist_add_idx) {
		ret = "";
	} else
		ret = hist_list[hist_cur];

	return (ret);
}

582
#ifndef CONFIG_CMDLINE_EDITING
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
static void cread_print_hist_list(void)
{
	int i;
	unsigned long n;

	n = hist_num - hist_max;

	i = hist_add_idx + 1;
	while (1) {
		if (i > hist_max)
			i = 0;
		if (i == hist_add_idx)
			break;
		printf("%s\n", hist_list[i]);
		n++;
		i++;
	}
}
601
#endif /* CONFIG_CMDLINE_EDITING */
602 603 604 605 606 607 608 609 610 611

#define BEGINNING_OF_LINE() {			\
	while (num) {				\
		getcmd_putch(CTL_BACKSPACE);	\
		num--;				\
	}					\
}

#define ERASE_TO_EOL() {				\
	if (num < eol_num) {				\
612 613
		printf("%*s", (int)(eol_num - num), ""); \
		do {					\
614
			getcmd_putch(CTL_BACKSPACE);	\
615
		} while (--eol_num > num);		\
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
	}						\
}

#define REFRESH_TO_EOL() {			\
	if (num < eol_num) {			\
		wlen = eol_num - num;		\
		putnstr(buf + num, wlen);	\
		num = eol_num;			\
	}					\
}

static void cread_add_char(char ichar, int insert, unsigned long *num,
	       unsigned long *eol_num, char *buf, unsigned long len)
{
	unsigned long wlen;

	/* room ??? */
	if (insert || *num == *eol_num) {
		if (*eol_num > len - 1) {
			getcmd_cbeep();
			return;
		}
		(*eol_num)++;
	}

	if (insert) {
		wlen = *eol_num - *num;
		if (wlen > 1) {
			memmove(&buf[*num+1], &buf[*num], wlen-1);
		}

		buf[*num] = ichar;
		putnstr(buf + *num, wlen);
		(*num)++;
		while (--wlen) {
			getcmd_putch(CTL_BACKSPACE);
		}
	} else {
		/* echo the character */
		wlen = 1;
		buf[*num] = ichar;
		putnstr(buf + *num, wlen);
		(*num)++;
	}
}

static void cread_add_str(char *str, int strsize, int insert, unsigned long *num,
	      unsigned long *eol_num, char *buf, unsigned long len)
{
	while (strsize--) {
		cread_add_char(*str, insert, num, eol_num, buf, len);
		str++;
	}
}

671 672
static int cread_line(const char *const prompt, char *buf, unsigned int *len,
		int timeout)
673 674 675 676 677 678 679 680
{
	unsigned long num = 0;
	unsigned long eol_num = 0;
	unsigned long wlen;
	char ichar;
	int insert = 1;
	int esc_len = 0;
	char esc_save[8];
681
	int init_len = strlen(buf);
682
	int first = 1;
683 684 685

	if (init_len)
		cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
686 687

	while (1) {
688 689 690 691
#ifdef CONFIG_BOOT_RETRY_TIME
		while (!tstc()) {	/* while no incoming data */
			if (retry_time >= 0 && get_ticks() > endtime)
				return (-2);	/* timed out */
692
			WATCHDOG_RESET();
693 694
		}
#endif
695 696 697 698 699 700 701 702 703 704
		if (first && timeout) {
			uint64_t etime = endtick(timeout);

			while (!tstc()) {	/* while no incoming data */
				if (get_ticks() >= etime)
					return -2;	/* timed out */
				WATCHDOG_RESET();
			}
			first = 0;
		}
705

706 707 708
		ichar = getcmd_getch();

		if ((ichar == '\n') || (ichar == '\r')) {
Wolfgang Denk's avatar
Wolfgang Denk committed
709
			putc('\n');
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
			break;
		}

		/*
		 * handle standard linux xterm esc sequences for arrow key, etc.
		 */
		if (esc_len != 0) {
			if (esc_len == 1) {
				if (ichar == '[') {
					esc_save[esc_len] = ichar;
					esc_len = 2;
				} else {
					cread_add_str(esc_save, esc_len, insert,
						      &num, &eol_num, buf, *len);
					esc_len = 0;
				}
				continue;
			}

			switch (ichar) {

			case 'D':	/* <- key */
				ichar = CTL_CH('b');
				esc_len = 0;
				break;
			case 'C':	/* -> key */
				ichar = CTL_CH('f');
				esc_len = 0;
				break;	/* pass off to ^F handler */
			case 'H':	/* Home key */
				ichar = CTL_CH('a');
				esc_len = 0;
				break;	/* pass off to ^A handler */
			case 'A':	/* up arrow */
				ichar = CTL_CH('p');
				esc_len = 0;
				break;	/* pass off to ^P handler */
			case 'B':	/* down arrow */
				ichar = CTL_CH('n');
				esc_len = 0;
				break;	/* pass off to ^N handler */
			default:
				esc_save[esc_len++] = ichar;
				cread_add_str(esc_save, esc_len, insert,
					      &num, &eol_num, buf, *len);
				esc_len = 0;
				continue;
			}
		}

		switch (ichar) {
		case 0x1b:
			if (esc_len == 0) {
				esc_save[esc_len] = ichar;
				esc_len = 1;
			} else {
Wolfgang Denk's avatar
Wolfgang Denk committed
766
				puts("impossible condition #876\n");
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
				esc_len = 0;
			}
			break;

		case CTL_CH('a'):
			BEGINNING_OF_LINE();
			break;
		case CTL_CH('c'):	/* ^C - break */
			*buf = '\0';	/* discard input */
			return (-1);
		case CTL_CH('f'):
			if (num < eol_num) {
				getcmd_putch(buf[num]);
				num++;
			}
			break;
		case CTL_CH('b'):
			if (num) {
				getcmd_putch(CTL_BACKSPACE);
				num--;
			}
			break;
		case CTL_CH('d'):
			if (num < eol_num) {
				wlen = eol_num - num - 1;
				if (wlen) {
					memmove(&buf[num], &buf[num+1], wlen);
					putnstr(buf + num, wlen);
				}

				getcmd_putch(' ');
				do {
					getcmd_putch(CTL_BACKSPACE);
				} while (wlen--);
				eol_num--;
			}
			break;
		case CTL_CH('k'):
			ERASE_TO_EOL();
			break;
		case CTL_CH('e'):
			REFRESH_TO_EOL();
			break;
		case CTL_CH('o'):
			insert = !insert;
			break;
		case CTL_CH('x'):
814
		case CTL_CH('u'):
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
			BEGINNING_OF_LINE();
			ERASE_TO_EOL();
			break;
		case DEL:
		case DEL7:
		case 8:
			if (num) {
				wlen = eol_num - num;
				num--;
				memmove(&buf[num], &buf[num+1], wlen);
				getcmd_putch(CTL_BACKSPACE);
				putnstr(buf + num, wlen);
				getcmd_putch(' ');
				do {
					getcmd_putch(CTL_BACKSPACE);
				} while (wlen--);
				eol_num--;
			}
			break;
		case CTL_CH('p'):
		case CTL_CH('n'):
		{
			char * hline;

			esc_len = 0;

			if (ichar == CTL_CH('p'))
				hline = hist_prev();
			else
				hline = hist_next();

			if (!hline) {
				getcmd_cbeep();
				continue;
			}

			/* nuke the current line */
			/* first, go home */
			BEGINNING_OF_LINE();

			/* erase to end of line */
			ERASE_TO_EOL();

			/* copy new line into place and display */
			strcpy(buf, hline);
			eol_num = strlen(buf);
			REFRESH_TO_EOL();
			continue;
		}
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
#ifdef CONFIG_AUTO_COMPLETE
		case '\t': {
			int num2, col;

			/* do not autocomplete when in the middle */
			if (num < eol_num) {
				getcmd_cbeep();
				break;
			}

			buf[num] = '\0';
			col = strlen(prompt) + eol_num;
			num2 = num;
			if (cmd_auto_complete(prompt, buf, &num2, &col)) {
				col = num2 - num;
				num += col;
				eol_num += col;
			}
			break;
		}
#endif
885 886 887 888 889 890 891 892 893 894 895 896
		default:
			cread_add_char(ichar, insert, &num, &eol_num, buf, *len);
			break;
		}
	}
	*len = eol_num;
	buf[eol_num] = '\0';	/* lose the newline */

	if (buf[0] && buf[0] != CREAD_HIST_CHAR)
		cread_add_to_hist(buf);
	hist_cur = hist_add_idx;

897
	return 0;
898 899 900 901
}

#endif /* CONFIG_CMDLINE_EDITING */

wdenk's avatar
wdenk committed
902 903 904 905 906 907 908 909 910 911 912 913
/****************************************************************************/

/*
 * Prompt for input and read a line.
 * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
 * time out when time goes past endtime (timebase time in ticks).
 * Return:	number of read characters
 *		-1 if break
 *		-2 if timed out
 */
int readline (const char *const prompt)
{
914 915 916 917 918 919
	/*
	 * If console_buffer isn't 0-length the user will be prompted to modify
	 * it instead of entering it from scratch as desired.
	 */
	console_buffer[0] = '\0';

920
	return readline_into_buffer(prompt, console_buffer, 0);
James Yang's avatar
James Yang committed
921 922 923
}


924
int readline_into_buffer(const char *const prompt, char *buffer, int timeout)
James Yang's avatar
James Yang committed
925 926
{
	char *p = buffer;
927
#ifdef CONFIG_CMDLINE_EDITING
928
	unsigned int len = CONFIG_SYS_CBSIZE;
929
	int rc;
930 931
	static int initted = 0;

932 933 934 935 936 937 938 939 940 941 942 943
	/*
	 * History uses a global array which is not
	 * writable until after relocation to RAM.
	 * Revert to non-history version if still
	 * running from flash.
	 */
	if (gd->flags & GD_FLG_RELOC) {
		if (!initted) {
			hist_init();
			initted = 1;
		}

944 945
		if (prompt)
			puts (prompt);
946

947
		rc = cread_line(prompt, p, &len, timeout);
948 949 950 951
		return rc < 0 ? rc : len;

	} else {
#endif	/* CONFIG_CMDLINE_EDITING */
Kumar Gala's avatar
Kumar Gala committed
952
	char * p_buf = p;
wdenk's avatar
wdenk committed
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
	int	n = 0;				/* buffer index		*/
	int	plen = 0;			/* prompt length	*/
	int	col;				/* output column cnt	*/
	char	c;

	/* print prompt */
	if (prompt) {
		plen = strlen (prompt);
		puts (prompt);
	}
	col = plen;

	for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
		while (!tstc()) {	/* while no incoming data */
			if (retry_time >= 0 && get_ticks() > endtime)
				return (-2);	/* timed out */
970
			WATCHDOG_RESET();
wdenk's avatar
wdenk committed
971 972 973 974 975 976 977
		}
#endif
		WATCHDOG_RESET();		/* Trigger watchdog, if needed */

#ifdef CONFIG_SHOW_ACTIVITY
		while (!tstc()) {
			show_activity(0);
978
			WATCHDOG_RESET();
wdenk's avatar
wdenk committed
979 980 981 982 983 984 985 986 987 988 989 990
		}
#endif
		c = getc();

		/*
		 * Special character handling
		 */
		switch (c) {
		case '\r':				/* Enter		*/
		case '\n':
			*p = '\0';
			puts ("\r\n");
James Yang's avatar
James Yang committed
991
			return (p - p_buf);
wdenk's avatar
wdenk committed
992

wdenk's avatar
wdenk committed
993 994 995
		case '\0':				/* nul			*/
			continue;

wdenk's avatar
wdenk committed
996
		case 0x03:				/* ^C - break		*/
James Yang's avatar
James Yang committed
997
			p_buf[0] = '\0';	/* discard input */
wdenk's avatar
wdenk committed
998 999 1000 1001 1002 1003 1004
			return (-1);

		case 0x15:				/* ^U - erase line	*/
			while (col > plen) {
				puts (erase_seq);
				--col;
			}
James Yang's avatar
James Yang committed
1005
			p = p_buf;
wdenk's avatar
wdenk committed
1006 1007 1008
			n = 0;
			continue;

1009
		case 0x17:				/* ^W - erase word	*/
James Yang's avatar
James Yang committed
1010
			p=delete_char(p_buf, p, &col, &n, plen);
wdenk's avatar
wdenk committed
1011
			while ((n > 0) && (*p != ' ')) {
James Yang's avatar
James Yang committed
1012
				p=delete_char(p_buf, p, &col, &n, plen);
wdenk's avatar
wdenk committed
1013 1014 1015 1016 1017
			}
			continue;

		case 0x08:				/* ^H  - backspace	*/
		case 0x7F:				/* DEL - backspace	*/
James Yang's avatar
James Yang committed
1018
			p=delete_char(p_buf, p, &col, &n, plen);
wdenk's avatar
wdenk committed
1019 1020 1021 1022 1023 1024
			continue;

		default:
			/*
			 * Must be a normal character then
			 */
1025
			if (n < CONFIG_SYS_CBSIZE-2) {
wdenk's avatar
wdenk committed
1026
				if (c == '\t') {	/* expand TABs		*/
1027 1028 1029 1030
#ifdef CONFIG_AUTO_COMPLETE
					/* if auto completion triggered just continue */
					*p = '\0';
					if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
James Yang's avatar
James Yang committed
1031
						p = p_buf + n;	/* reset */
1032 1033 1034
						continue;
					}
#endif
wdenk's avatar
wdenk committed
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
					puts (tab_seq+(col&07));
					col += 8 - (col&07);
				} else {
					++col;		/* echo input		*/
					putc (c);
				}
				*p++ = c;
				++n;
			} else {			/* Buffer full		*/
				putc ('\a');
			}
		}
	}
1048 1049 1050
#ifdef CONFIG_CMDLINE_EDITING
	}
#endif
wdenk's avatar
wdenk committed
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
}

/****************************************************************************/

static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen)
{
	char *s;

	if (*np == 0) {
		return (p);
	}

	if (*(--p) == '\t') {			/* will retype the whole line	*/
		while (*colp > plen) {
			puts (erase_seq);
			(*colp)--;
		}
		for (s=buffer; s<p; ++s) {
			if (*s == '\t') {
				puts (tab_seq+((*colp) & 07));
				*colp += 8 - ((*colp) & 07);
			} else {
				++(*colp);
				putc (*s);
			}
		}
	} else {
		puts (erase_seq);
		(*colp)--;
	}
	(*np)--;
	return (p);
}

/****************************************************************************/

int parse_line (char *line, char *argv[])
{
	int nargs = 0;

#ifdef DEBUG_PARSER
	printf ("parse_line: \"%s\"\n", line);
#endif
1094
	while (nargs < CONFIG_SYS_MAXARGS) {
wdenk's avatar
wdenk committed
1095 1096

		/* skip any white space */
1097
		while (isblank(*line))
wdenk's avatar
wdenk committed
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
			++line;

		if (*line == '\0') {	/* end of line, no more args	*/
			argv[nargs] = NULL;
#ifdef DEBUG_PARSER
		printf ("parse_line: nargs=%d\n", nargs);
#endif
			return (nargs);
		}

		argv[nargs++] = line;	/* begin of argument string	*/

		/* find end of string */
1111
		while (*line && !isblank(*line))
wdenk's avatar
wdenk committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
			++line;

		if (*line == '\0') {	/* end of line, no more args	*/
			argv[nargs] = NULL;
#ifdef DEBUG_PARSER
		printf ("parse_line: nargs=%d\n", nargs);
#endif
			return (nargs);
		}

		*line++ = '\0';		/* terminate current arg	 */
	}

1125
	printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
wdenk's avatar
wdenk committed
1126 1127 1128 1129 1130 1131 1132 1133 1134

#ifdef DEBUG_PARSER
	printf ("parse_line: nargs=%d\n", nargs);
#endif
	return (nargs);
}

/****************************************************************************/

1135
#ifndef CONFIG_SYS_HUSH_PARSER
wdenk's avatar
wdenk committed
1136 1137 1138 1139
static void process_macros (const char *input, char *output)
{
	char c, prev;
	const char *varname_start = NULL;
Wolfgang Denk's avatar
Wolfgang Denk committed
1140
	int inputcnt = strlen (input);
1141
	int outputcnt = CONFIG_SYS_CBSIZE;
Wolfgang Denk's avatar
Wolfgang Denk committed
1142 1143 1144 1145 1146
	int state = 0;		/* 0 = waiting for '$'  */

	/* 1 = waiting for '(' or '{' */
	/* 2 = waiting for ')' or '}' */
	/* 3 = waiting for '''  */
wdenk's avatar
wdenk committed
1147 1148 1149
#ifdef DEBUG_PARSER
	char *output_start = output;

Wolfgang Denk's avatar
Wolfgang Denk committed
1150 1151
	printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen (input),
		input);
wdenk's avatar
wdenk committed
1152 1153
#endif

Wolfgang Denk's avatar
Wolfgang Denk committed
1154
	prev = '\0';		/* previous character   */
wdenk's avatar
wdenk committed
1155 1156

	while (inputcnt && outputcnt) {
wdenk's avatar
wdenk committed
1157
		c = *input++;
Wolfgang Denk's avatar
Wolfgang Denk committed
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
		inputcnt--;

		if (state != 3) {
			/* remove one level of escape characters */
			if ((c == '\\') && (prev != '\\')) {
				if (inputcnt-- == 0)
					break;
				prev = c;
				c = *input++;
			}
wdenk's avatar
wdenk committed
1168
		}
Wolfgang Denk's avatar
Wolfgang Denk committed
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178

		switch (state) {
		case 0:	/* Waiting for (unescaped) $    */
			if ((c == '\'') && (prev != '\\')) {
				state = 3;
				break;
			}
			if ((c == '$') && (prev != '\\')) {
				state++;
			} else {
wdenk's avatar
wdenk committed
1179 1180 1181
				*(output++) = c;
				outputcnt--;
			}
Wolfgang Denk's avatar
Wolfgang Denk committed
1182 1183 1184 1185 1186 1187 1188 1189 1190
			break;
		case 1:	/* Waiting for (        */
			if (c == '(' || c == '{') {
				state++;
				varname_start = input;
			} else {
				state = 0;
				*(output++) = '$';
				outputcnt--;
wdenk's avatar
wdenk committed
1191

Wolfgang Denk's avatar
Wolfgang Denk committed
1192 1193
				if (outputcnt) {
					*(output++) = c;
wdenk's avatar
wdenk committed
1194 1195
					outputcnt--;
				}
Wolfgang Denk's avatar
Wolfgang Denk committed
1196 1197 1198 1199 1200
			}
			break;
		case 2:	/* Waiting for )        */
			if (c == ')' || c == '}') {
				int i;
1201
				char envname[CONFIG_SYS_CBSIZE], *envval;
Wolfgang Denk's avatar
Wolfgang Denk committed
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
				int envcnt = input - varname_start - 1;	/* Varname # of chars */

				/* Get the varname */
				for (i = 0; i < envcnt; i++) {
					envname[i] = varname_start[i];
				}
				envname[i] = 0;

				/* Get its value */
				envval = getenv (envname);

				/* Copy into the line if it exists */
				if (envval != NULL)
					while ((*envval) && outputcnt) {
						*(output++) = *(envval++);
						outputcnt--;
					}
				/* Look for another '$' */
				state = 0;
			}
			break;
		case 3:	/* Waiting for '        */
			if ((c == '\'') && (prev != '\\')) {
				state = 0;
			} else {
				*(output++) = c;
				outputcnt--;
			}
			break;
wdenk's avatar
wdenk committed
1231
		}
Wolfgang Denk's avatar
Wolfgang Denk committed
1232
		prev = c;
wdenk's avatar
wdenk committed
1233 1234 1235 1236
	}

	if (outputcnt)
		*output = 0;
1237 1238
	else
		*(output - 1) = 0;
wdenk's avatar
wdenk committed
1239 1240 1241

#ifdef DEBUG_PARSER
	printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
Wolfgang Denk's avatar
Wolfgang Denk committed
1242
		strlen (output_start), output_start);
wdenk's avatar
wdenk committed
1243 1244 1245 1246 1247 1248 1249 1250 1251
#endif
}

/****************************************************************************
 * returns:
 *	1  - command executed, repeatable
 *	0  - command executed but not repeatable, interrupted commands are
 *	     always considered not repeatable
 *	-1 - not executed (unrecognized, bootd recursion or too many args)
1252
 *           (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is
wdenk's avatar
wdenk committed
1253 1254 1255 1256 1257 1258 1259 1260 1261
 *           considered unrecognized)
 *
 * WARNING:
 *
 * We must create a temporary copy of the command since the command we get
 * may be the result from getenv(), which returns a pointer directly to
 * the environment data, which may change magicly when the command we run
 * creates or modifies environment variables (like "bootp" does).
 */
Simon Glass's avatar
Simon Glass committed
1262
static int builtin_run_command(const char *cmd, int flag)
wdenk's avatar
wdenk committed
1263
{
1264
	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
wdenk's avatar
wdenk committed
1265 1266
	char *token;			/* start of token in cmdbuf	*/
	char *sep;			/* end of token (separator) in cmdbuf */
1267
	char finaltoken[CONFIG_SYS_CBSIZE];
wdenk's avatar
wdenk committed
1268
	char *str = cmdbuf;
1269
	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
1270
	int argc, inquotes;
wdenk's avatar
wdenk committed
1271
	int repeatable = 1;
1272
	int rc = 0;
wdenk's avatar
wdenk committed
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285

#ifdef DEBUG_PARSER
	printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
	puts (cmd ? cmd : "NULL");	/* use puts - string may be loooong */
	puts ("\"\n");
#endif

	clear_ctrlc();		/* forget any previous Control C */

	if (!cmd || !*cmd) {
		return -1;	/* empty command */
	}

1286
	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
wdenk's avatar
wdenk committed
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
		puts ("## Command too long!\n");
		return -1;
	}

	strcpy (cmdbuf, cmd);

	/* Process separators and check for invalid
	 * repeatable commands
	 */

#ifdef DEBUG_PARSER
	printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
	while (*str) {

		/*
		 * Find separator, or string end
		 * Allow simple escape of ';' by writing "\;"
		 */
wdenk's avatar
wdenk committed
1306 1307 1308 1309 1310 1311 1312
		for (inquotes = 0, sep = str; *sep; sep++) {
			if ((*sep=='\'') &&
			    (*(sep-1) != '\\'))
				inquotes=!inquotes;

			if (!inquotes &&
			    (*sep == ';') &&	/* separator		*/
wdenk's avatar
wdenk committed
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
			    ( sep != str) &&	/* past string start	*/
			    (*(sep-1) != '\\'))	/* and NOT escaped	*/
				break;
		}

		/*
		 * Limit the token to data between separators
		 */
		token = str;
		if (*sep) {
			str = sep + 1;	/* start of command for next pass */
			*sep = '\0';
		}
		else
			str = sep;	/* no more commands for next pass */
#ifdef DEBUG_PARSER
		printf ("token: \"%s\"\n", token);
#endif

		/* find macros in this token and replace them */
		process_macros (token, finaltoken);

		/* Extract arguments */