Split out simple parser and readline into separate files

It doesn't make sense to have the simple parser and the readline code
all in main. Split them out into separate files.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/common/Makefile b/common/Makefile
index da184a8..7998325 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -11,7 +11,14 @@
 obj-y += command.o
 obj-y += exports.o
 obj-y += hash.o
-obj-$(CONFIG_SYS_HUSH_PARSER) += cli_hush.o
+ifdef CONFIG_SYS_HUSH_PARSER
+obj-y += cli_hush.o
+endif
+
+# We always have this since drivers/ddr/fs/interactive.c needs it
+obj-y += cli_simple.o
+
+obj-y += cli_readline.o
 obj-y += s_record.o
 obj-y += xyzModem.o
 obj-y += cmd_disk.o
diff --git a/common/cli_readline.c b/common/cli_readline.c
new file mode 100644
index 0000000..cea0b7c
--- /dev/null
+++ b/common/cli_readline.c
@@ -0,0 +1,670 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Add to readline cmdline-editing by
+ * (C) Copyright 2005
+ * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <cli.h>
+#include <watchdog.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char erase_seq[] = "\b \b";	/* erase sequence */
+static const char   tab_seq[] = "        ";	/* used to expand TABs */
+
+#ifdef CONFIG_BOOT_RETRY_TIME
+static uint64_t endtime;      /* must be set, default is instant timeout */
+static int      retry_time = -1; /* -1 so can call readline before main_loop */
+#endif
+
+char console_buffer[CONFIG_SYS_CBSIZE + 1];	/* console I/O buffer	*/
+
+#ifndef CONFIG_BOOT_RETRY_MIN
+#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME
+#endif
+
+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;
+}
+
+#ifdef CONFIG_CMDLINE_EDITING
+
+/*
+ * cmdline-editing related codes from vivi.
+ * Author: Janghoon Lyu <nandy@mizi.com>
+ */
+
+#define putnstr(str, n)	printf("%.*s", (int)n, str)
+
+#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
+#define HIST_SIZE		CONFIG_SYS_CBSIZE
+
+static int hist_max;
+static int hist_add_idx;
+static int hist_cur = -1;
+static unsigned hist_num;
+
+static char *hist_list[HIST_MAX];
+static char hist_lines[HIST_MAX][HIST_SIZE + 1];	/* Save room for NULL */
+
+#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;
+}
+
+#ifndef CONFIG_CMDLINE_EDITING
+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++;
+	}
+}
+#endif /* CONFIG_CMDLINE_EDITING */
+
+#define BEGINNING_OF_LINE() {			\
+	while (num) {				\
+		getcmd_putch(CTL_BACKSPACE);	\
+		num--;				\
+	}					\
+}
+
+#define ERASE_TO_EOL() {				\
+	if (num < eol_num) {				\
+		printf("%*s", (int)(eol_num - num), ""); \
+		do {					\
+			getcmd_putch(CTL_BACKSPACE);	\
+		} while (--eol_num > num);		\
+	}						\
+}
+
+#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++;
+	}
+}
+
+static int cread_line(const char *const prompt, char *buf, unsigned int *len,
+		int timeout)
+{
+	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];
+	int init_len = strlen(buf);
+	int first = 1;
+
+	if (init_len)
+		cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
+
+	while (1) {
+#ifdef CONFIG_BOOT_RETRY_TIME
+		while (!tstc()) {	/* while no incoming data */
+			if (retry_time >= 0 && get_ticks() > endtime)
+				return -2;	/* timed out */
+			WATCHDOG_RESET();
+		}
+#endif
+		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;
+		}
+
+		ichar = getcmd_getch();
+
+		if ((ichar == '\n') || (ichar == '\r')) {
+			putc('\n');
+			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 {
+				puts("impossible condition #876\n");
+				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'):
+		case CTL_CH('u'):
+			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;
+		}
+#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
+		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;
+
+	return 0;
+}
+
+#endif /* CONFIG_CMDLINE_EDITING */
+
+/****************************************************************************/
+
+int readline(const char *const prompt)
+{
+	/*
+	 * 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';
+
+	return readline_into_buffer(prompt, console_buffer, 0);
+}
+
+
+int readline_into_buffer(const char *const prompt, char *buffer, int timeout)
+{
+	char *p = buffer;
+#ifdef CONFIG_CMDLINE_EDITING
+	unsigned int len = CONFIG_SYS_CBSIZE;
+	int rc;
+	static int initted;
+
+	/*
+	 * 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;
+		}
+
+		if (prompt)
+			puts(prompt);
+
+		rc = cread_line(prompt, p, &len, timeout);
+		return rc < 0 ? rc : len;
+
+	} else {
+#endif	/* CONFIG_CMDLINE_EDITING */
+	char *p_buf = p;
+	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 */
+			WATCHDOG_RESET();
+		}
+#endif
+		WATCHDOG_RESET();	/* Trigger watchdog, if needed */
+
+#ifdef CONFIG_SHOW_ACTIVITY
+		while (!tstc()) {
+			show_activity(0);
+			WATCHDOG_RESET();
+		}
+#endif
+		c = getc();
+
+		/*
+		 * Special character handling
+		 */
+		switch (c) {
+		case '\r':			/* Enter		*/
+		case '\n':
+			*p = '\0';
+			puts("\r\n");
+			return p - p_buf;
+
+		case '\0':			/* nul			*/
+			continue;
+
+		case 0x03:			/* ^C - break		*/
+			p_buf[0] = '\0';	/* discard input */
+			return -1;
+
+		case 0x15:			/* ^U - erase line	*/
+			while (col > plen) {
+				puts(erase_seq);
+				--col;
+			}
+			p = p_buf;
+			n = 0;
+			continue;
+
+		case 0x17:			/* ^W - erase word	*/
+			p = delete_char(p_buf, p, &col, &n, plen);
+			while ((n > 0) && (*p != ' '))
+				p = delete_char(p_buf, p, &col, &n, plen);
+			continue;
+
+		case 0x08:			/* ^H  - backspace	*/
+		case 0x7F:			/* DEL - backspace	*/
+			p = delete_char(p_buf, p, &col, &n, plen);
+			continue;
+
+		default:
+			/*
+			 * Must be a normal character then
+			 */
+			if (n < CONFIG_SYS_CBSIZE-2) {
+				if (c == '\t') {	/* expand TABs */
+#ifdef CONFIG_AUTO_COMPLETE
+					/*
+					 * if auto completion triggered just
+					 * continue
+					 */
+					*p = '\0';
+					if (cmd_auto_complete(prompt,
+							      console_buffer,
+							      &n, &col)) {
+						p = p_buf + n;	/* reset */
+						continue;
+					}
+#endif
+					puts(tab_seq + (col & 07));
+					col += 8 - (col & 07);
+				} else {
+					char buf[2];
+
+					/*
+					 * Echo input using puts() to force an
+					 * LCD flush if we are using an LCD
+					 */
+					++col;
+					buf[0] = c;
+					buf[1] = '\0';
+					puts(buf);
+				}
+				*p++ = c;
+				++n;
+			} else {			/* Buffer full */
+				putc('\a');
+			}
+		}
+	}
+#ifdef CONFIG_CMDLINE_EDITING
+	}
+#endif
+}
+
+#ifdef CONFIG_BOOT_RETRY_TIME
+/***************************************************************************
+ * initialize command line timeout
+ */
+void init_cmd_timeout(void)
+{
+	char *s = getenv("bootretry");
+
+	if (s != NULL)
+		retry_time = (int)simple_strtol(s, NULL, 10);
+	else
+		retry_time =  CONFIG_BOOT_RETRY_TIME;
+
+	if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN)
+		retry_time = CONFIG_BOOT_RETRY_MIN;
+}
+
+/***************************************************************************
+ * reset command line timeout to retry_time seconds
+ */
+void reset_cmd_timeout(void)
+{
+	endtime = endtick(retry_time);
+}
+
+void bootretry_dont_retry(void)
+{
+	retry_time = -1;
+}
+
+#endif
diff --git a/common/cli_simple.c b/common/cli_simple.c
new file mode 100644
index 0000000..0610615
--- /dev/null
+++ b/common/cli_simple.c
@@ -0,0 +1,338 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Add to readline cmdline-editing by
+ * (C) Copyright 2005
+ * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <cli.h>
+#include <linux/ctype.h>
+
+#define DEBUG_PARSER	0	/* set to 1 to debug */
+
+#define debug_parser(fmt, args...)		\
+	debug_cond(DEBUG_PARSER, fmt, ##args)
+
+
+int parse_line(char *line, char *argv[])
+{
+	int nargs = 0;
+
+	debug_parser("%s: \"%s\"\n", __func__, line);
+	while (nargs < CONFIG_SYS_MAXARGS) {
+		/* skip any white space */
+		while (isblank(*line))
+			++line;
+
+		if (*line == '\0') {	/* end of line, no more args	*/
+			argv[nargs] = NULL;
+			debug_parser("%s: nargs=%d\n", __func__, nargs);
+			return nargs;
+		}
+
+		argv[nargs++] = line;	/* begin of argument string	*/
+
+		/* find end of string */
+		while (*line && !isblank(*line))
+			++line;
+
+		if (*line == '\0') {	/* end of line, no more args	*/
+			argv[nargs] = NULL;
+			debug_parser("parse_line: nargs=%d\n", nargs);
+			return nargs;
+		}
+
+		*line++ = '\0';		/* terminate current arg	 */
+	}
+
+	printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
+
+	debug_parser("%s: nargs=%d\n", __func__, nargs);
+	return nargs;
+}
+
+static void process_macros(const char *input, char *output)
+{
+	char c, prev;
+	const char *varname_start = NULL;
+	int inputcnt = strlen(input);
+	int outputcnt = CONFIG_SYS_CBSIZE;
+	int state = 0;		/* 0 = waiting for '$'  */
+
+	/* 1 = waiting for '(' or '{' */
+	/* 2 = waiting for ')' or '}' */
+	/* 3 = waiting for '''  */
+	char *output_start = output;
+
+	debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
+		     input);
+
+	prev = '\0';		/* previous character   */
+
+	while (inputcnt && outputcnt) {
+		c = *input++;
+		inputcnt--;
+
+		if (state != 3) {
+			/* remove one level of escape characters */
+			if ((c == '\\') && (prev != '\\')) {
+				if (inputcnt-- == 0)
+					break;
+				prev = c;
+				c = *input++;
+			}
+		}
+
+		switch (state) {
+		case 0:	/* Waiting for (unescaped) $    */
+			if ((c == '\'') && (prev != '\\')) {
+				state = 3;
+				break;
+			}
+			if ((c == '$') && (prev != '\\')) {
+				state++;
+			} else {
+				*(output++) = c;
+				outputcnt--;
+			}
+			break;
+		case 1:	/* Waiting for (        */
+			if (c == '(' || c == '{') {
+				state++;
+				varname_start = input;
+			} else {
+				state = 0;
+				*(output++) = '$';
+				outputcnt--;
+
+				if (outputcnt) {
+					*(output++) = c;
+					outputcnt--;
+				}
+			}
+			break;
+		case 2:	/* Waiting for )        */
+			if (c == ')' || c == '}') {
+				int i;
+				char envname[CONFIG_SYS_CBSIZE], *envval;
+				/* Varname # of chars */
+				int envcnt = input - varname_start - 1;
+
+				/* 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;
+		}
+		prev = c;
+	}
+
+	if (outputcnt)
+		*output = 0;
+	else
+		*(output - 1) = 0;
+
+	debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
+		     strlen(output_start), output_start);
+}
+
+ /*
+ * 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).
+ */
+int cli_simple_run_command(const char *cmd, int flag)
+{
+	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
+	char *token;			/* start of token in cmdbuf	*/
+	char *sep;			/* end of token (separator) in cmdbuf */
+	char finaltoken[CONFIG_SYS_CBSIZE];
+	char *str = cmdbuf;
+	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
+	int argc, inquotes;
+	int repeatable = 1;
+	int rc = 0;
+
+	debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
+	if (DEBUG_PARSER) {
+		/* use puts - string may be loooong */
+		puts(cmd ? cmd : "NULL");
+		puts("\"\n");
+	}
+	clear_ctrlc();		/* forget any previous Control C */
+
+	if (!cmd || !*cmd)
+		return -1;	/* empty command */
+
+	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
+		puts("## Command too long!\n");
+		return -1;
+	}
+
+	strcpy(cmdbuf, cmd);
+
+	/* Process separators and check for invalid
+	 * repeatable commands
+	 */
+
+	debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
+	while (*str) {
+		/*
+		 * Find separator, or string end
+		 * Allow simple escape of ';' by writing "\;"
+		 */
+		for (inquotes = 0, sep = str; *sep; sep++) {
+			if ((*sep == '\'') &&
+			    (*(sep - 1) != '\\'))
+				inquotes = !inquotes;
+
+			if (!inquotes &&
+			    (*sep == ';') &&	/* separator		*/
+			    (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 */
+		}
+		debug_parser("token: \"%s\"\n", token);
+
+		/* find macros in this token and replace them */
+		process_macros(token, finaltoken);
+
+		/* Extract arguments */
+		argc = parse_line(finaltoken, argv);
+		if (argc == 0) {
+			rc = -1;	/* no command at all */
+			continue;
+		}
+
+		if (cmd_process(flag, argc, argv, &repeatable, NULL))
+			rc = -1;
+
+		/* Did the user stop this? */
+		if (had_ctrlc())
+			return -1;	/* if stopped then not repeatable */
+	}
+
+	return rc ? rc : repeatable;
+}
+
+void cli_loop(void)
+{
+	static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
+
+	int len;
+	int flag;
+	int rc = 1;
+
+	for (;;) {
+#ifdef CONFIG_BOOT_RETRY_TIME
+		if (rc >= 0) {
+			/* Saw enough of a valid command to
+			 * restart the timeout.
+			 */
+			reset_cmd_timeout();
+		}
+#endif
+		len = readline(CONFIG_SYS_PROMPT);
+
+		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
+			 */
+			puts("\nTimed out waiting for command\n");
+# 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)
+			puts("<INTERRUPT>\n");
+		else
+			rc = run_command(lastcommand, flag);
+
+		if (rc <= 0) {
+			/* invalid command or not repeatable, forget it */
+			lastcommand[0] = 0;
+		}
+	}
+}
+
+int cli_simple_run_command_list(char *cmd, int flag)
+{
+	char *line, *next;
+	int rcode = 0;
+
+	/*
+	 * Break into individual lines, and execute each line; terminate on
+	 * error.
+	 */
+	next = cmd;
+	line = cmd;
+	while (*next) {
+		if (*next == '\n') {
+			*next = '\0';
+			/* run only non-empty commands */
+			if (*line) {
+				debug("** exec: \"%s\"\n", line);
+				if (cli_simple_run_command(line, 0) < 0) {
+					rcode = 1;
+					break;
+				}
+			}
+			line = next + 1;
+		}
+		++next;
+	}
+	if (rcode == 0 && *line)
+		rcode = (cli_simple_run_command(line, 0) >= 0);
+
+	return rcode;
+}
diff --git a/common/main.c b/common/main.c
index 6d1c3a9..88b0adb 100644
--- a/common/main.c
+++ b/common/main.c
@@ -2,10 +2,6 @@
  * (C) Copyright 2000
  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  *
- * Add to readline cmdline-editing by
- * (C) Copyright 2005
- * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
- *
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
@@ -20,8 +16,6 @@
 #include <menu.h>
 #include <post.h>
 #include <version.h>
-#include <watchdog.h>
-#include <linux/ctype.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -33,34 +27,12 @@
 
 #define MAX_DELAY_STOP_STR 32
 
-#define DEBUG_PARSER	0	/* set to 1 to debug */
-
-#define debug_parser(fmt, args...)		\
-	debug_cond(DEBUG_PARSER, fmt, ##args)
-
 #ifndef DEBUG_BOOTKEYS
 #define DEBUG_BOOTKEYS 0
 #endif
 #define debug_bootkeys(fmt, args...)		\
 	debug_cond(DEBUG_BOOTKEYS, fmt, ##args)
 
-char        console_buffer[CONFIG_SYS_CBSIZE + 1];	/* console I/O buffer	*/
-
-static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen);
-static const char erase_seq[] = "\b \b";		/* erase sequence	*/
-static const char   tab_seq[] = "        ";		/* used to expand TABs	*/
-
-#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 */
@@ -162,7 +134,7 @@
 #  ifdef CONFIG_BOOT_RETRY_TIME
 				/* don't retry auto boot */
 				if (! delaykey[i].retry)
-					retry_time = -1;
+					bootretry_dont_retry();
 #  endif
 				abort = 1;
 			}
@@ -416,12 +388,6 @@
 
 void main_loop(void)
 {
-#ifndef CONFIG_SYS_HUSH_PARSER
-	static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
-	int len;
-	int rc = 1;
-	int flag;
-#endif
 #ifdef CONFIG_PREBOOT
 	char *p;
 #endif
@@ -489,942 +455,12 @@
 	/* 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
-		len = readline (CONFIG_SYS_PROMPT);
-
-		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
-			 */
-			puts ("\nTimed out waiting for command\n");
-# 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)
-			puts ("<INTERRUPT>\n");
-		else
-			rc = run_command(lastcommand, flag);
-
-		if (rc <= 0) {
-			/* invalid command or not repeatable, forget it */
-			lastcommand[0] = 0;
-		}
-	}
+	cli_loop();
 #endif /*CONFIG_SYS_HUSH_PARSER*/
 }
 
-#ifdef CONFIG_BOOT_RETRY_TIME
-/***************************************************************************
- * initialize command line timeout
- */
-void init_cmd_timeout(void)
-{
-	char *s = getenv ("bootretry");
-
-	if (s != NULL)
-		retry_time = (int)simple_strtol(s, NULL, 10);
-	else
-		retry_time =  CONFIG_BOOT_RETRY_TIME;
-
-	if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN)
-		retry_time = CONFIG_BOOT_RETRY_MIN;
-}
-
-/***************************************************************************
- * reset command line timeout to retry_time seconds
- */
-void reset_cmd_timeout(void)
-{
-	endtime = endtick(retry_time);
-}
-#endif
-
-#ifdef CONFIG_CMDLINE_EDITING
-
-/*
- * cmdline-editing related codes from vivi.
- * Author: Janghoon Lyu <nandy@mizi.com>
- */
-
-#define putnstr(str,n)	do {			\
-		printf ("%.*s", (int)n, str);	\
-	} 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
-#define HIST_SIZE		CONFIG_SYS_CBSIZE
-
-static int hist_max;
-static int hist_add_idx;
-static int hist_cur = -1;
-static unsigned hist_num;
-
-static char *hist_list[HIST_MAX];
-static char hist_lines[HIST_MAX][HIST_SIZE + 1];	/* Save room for NULL */
-
-#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);
-}
-
-#ifndef CONFIG_CMDLINE_EDITING
-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++;
-	}
-}
-#endif /* CONFIG_CMDLINE_EDITING */
-
-#define BEGINNING_OF_LINE() {			\
-	while (num) {				\
-		getcmd_putch(CTL_BACKSPACE);	\
-		num--;				\
-	}					\
-}
-
-#define ERASE_TO_EOL() {				\
-	if (num < eol_num) {				\
-		printf("%*s", (int)(eol_num - num), ""); \
-		do {					\
-			getcmd_putch(CTL_BACKSPACE);	\
-		} while (--eol_num > num);		\
-	}						\
-}
-
-#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++;
-	}
-}
-
-static int cread_line(const char *const prompt, char *buf, unsigned int *len,
-		int timeout)
-{
-	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];
-	int init_len = strlen(buf);
-	int first = 1;
-
-	if (init_len)
-		cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
-
-	while (1) {
-#ifdef CONFIG_BOOT_RETRY_TIME
-		while (!tstc()) {	/* while no incoming data */
-			if (retry_time >= 0 && get_ticks() > endtime)
-				return (-2);	/* timed out */
-			WATCHDOG_RESET();
-		}
-#endif
-		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;
-		}
-
-		ichar = getcmd_getch();
-
-		if ((ichar == '\n') || (ichar == '\r')) {
-			putc('\n');
-			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 {
-				puts("impossible condition #876\n");
-				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'):
-		case CTL_CH('u'):
-			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;
-		}
-#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
-		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;
-
-	return 0;
-}
-
-#endif /* CONFIG_CMDLINE_EDITING */
-
-/****************************************************************************/
-
-/*
- * 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)
-{
-	/*
-	 * 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';
-
-	return readline_into_buffer(prompt, console_buffer, 0);
-}
-
-
-int readline_into_buffer(const char *const prompt, char *buffer, int timeout)
-{
-	char *p = buffer;
-#ifdef CONFIG_CMDLINE_EDITING
-	unsigned int len = CONFIG_SYS_CBSIZE;
-	int rc;
-	static int initted = 0;
-
-	/*
-	 * 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;
-		}
-
-		if (prompt)
-			puts (prompt);
-
-		rc = cread_line(prompt, p, &len, timeout);
-		return rc < 0 ? rc : len;
-
-	} else {
-#endif	/* CONFIG_CMDLINE_EDITING */
-	char * p_buf = p;
-	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 */
-			WATCHDOG_RESET();
-		}
-#endif
-		WATCHDOG_RESET();		/* Trigger watchdog, if needed */
-
-#ifdef CONFIG_SHOW_ACTIVITY
-		while (!tstc()) {
-			show_activity(0);
-			WATCHDOG_RESET();
-		}
-#endif
-		c = getc();
-
-		/*
-		 * Special character handling
-		 */
-		switch (c) {
-		case '\r':			/* Enter		*/
-		case '\n':
-			*p = '\0';
-			puts ("\r\n");
-			return p - p_buf;
-
-		case '\0':			/* nul			*/
-			continue;
-
-		case 0x03:			/* ^C - break		*/
-			p_buf[0] = '\0';	/* discard input */
-			return -1;
-
-		case 0x15:			/* ^U - erase line	*/
-			while (col > plen) {
-				puts (erase_seq);
-				--col;
-			}
-			p = p_buf;
-			n = 0;
-			continue;
-
-		case 0x17:			/* ^W - erase word	*/
-			p=delete_char(p_buf, p, &col, &n, plen);
-			while ((n > 0) && (*p != ' ')) {
-				p=delete_char(p_buf, p, &col, &n, plen);
-			}
-			continue;
-
-		case 0x08:			/* ^H  - backspace	*/
-		case 0x7F:			/* DEL - backspace	*/
-			p=delete_char(p_buf, p, &col, &n, plen);
-			continue;
-
-		default:
-			/*
-			 * Must be a normal character then
-			 */
-			if (n < CONFIG_SYS_CBSIZE-2) {
-				if (c == '\t') {	/* expand TABs */
-#ifdef CONFIG_AUTO_COMPLETE
-					/* if auto completion triggered just continue */
-					*p = '\0';
-					if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
-						p = p_buf + n;	/* reset */
-						continue;
-					}
-#endif
-					puts (tab_seq+(col&07));
-					col += 8 - (col&07);
-				} else {
-					char buf[2];
-
-					/*
-					 * Echo input using puts() to force an
-					 * LCD flush if we are using an LCD
-					 */
-					++col;
-					buf[0] = c;
-					buf[1] = '\0';
-					puts(buf);
-				}
-				*p++ = c;
-				++n;
-			} else {			/* Buffer full		*/
-				putc ('\a');
-			}
-		}
-	}
-#ifdef CONFIG_CMDLINE_EDITING
-	}
-#endif
-}
-
-/****************************************************************************/
-
-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;
-
-	debug_parser("parse_line: \"%s\"\n", line);
-	while (nargs < CONFIG_SYS_MAXARGS) {
-
-		/* skip any white space */
-		while (isblank(*line))
-			++line;
-
-		if (*line == '\0') {	/* end of line, no more args	*/
-			argv[nargs] = NULL;
-			debug_parser("parse_line: nargs=%d\n", nargs);
-			return nargs;
-		}
-
-		argv[nargs++] = line;	/* begin of argument string	*/
-
-		/* find end of string */
-		while (*line && !isblank(*line))
-			++line;
-
-		if (*line == '\0') {	/* end of line, no more args	*/
-			argv[nargs] = NULL;
-			debug_parser("parse_line: nargs=%d\n", nargs);
-			return nargs;
-		}
-
-		*line++ = '\0';		/* terminate current arg	 */
-	}
-
-	printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
-
-	debug_parser("parse_line: nargs=%d\n", nargs);
-	return (nargs);
-}
-
 /****************************************************************************/
 
-#ifndef CONFIG_SYS_HUSH_PARSER
-static void process_macros (const char *input, char *output)
-{
-	char c, prev;
-	const char *varname_start = NULL;
-	int inputcnt = strlen (input);
-	int outputcnt = CONFIG_SYS_CBSIZE;
-	int state = 0;		/* 0 = waiting for '$'  */
-
-	/* 1 = waiting for '(' or '{' */
-	/* 2 = waiting for ')' or '}' */
-	/* 3 = waiting for '''  */
-	char *output_start = output;
-
-	debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
-		     input);
-
-	prev = '\0';		/* previous character   */
-
-	while (inputcnt && outputcnt) {
-		c = *input++;
-		inputcnt--;
-
-		if (state != 3) {
-			/* remove one level of escape characters */
-			if ((c == '\\') && (prev != '\\')) {
-				if (inputcnt-- == 0)
-					break;
-				prev = c;
-				c = *input++;
-			}
-		}
-
-		switch (state) {
-		case 0:	/* Waiting for (unescaped) $    */
-			if ((c == '\'') && (prev != '\\')) {
-				state = 3;
-				break;
-			}
-			if ((c == '$') && (prev != '\\')) {
-				state++;
-			} else {
-				*(output++) = c;
-				outputcnt--;
-			}
-			break;
-		case 1:	/* Waiting for (        */
-			if (c == '(' || c == '{') {
-				state++;
-				varname_start = input;
-			} else {
-				state = 0;
-				*(output++) = '$';
-				outputcnt--;
-
-				if (outputcnt) {
-					*(output++) = c;
-					outputcnt--;
-				}
-			}
-			break;
-		case 2:	/* Waiting for )        */
-			if (c == ')' || c == '}') {
-				int i;
-				char envname[CONFIG_SYS_CBSIZE], *envval;
-				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;
-		}
-		prev = c;
-	}
-
-	if (outputcnt)
-		*output = 0;
-	else
-		*(output - 1) = 0;
-
-	debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
-		     strlen(output_start), output_start);
-}
-
-/****************************************************************************
- * 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)
- *           (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is
- *           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).
- */
-static int builtin_run_command(const char *cmd, int flag)
-{
-	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
-	char *token;			/* start of token in cmdbuf	*/
-	char *sep;			/* end of token (separator) in cmdbuf */
-	char finaltoken[CONFIG_SYS_CBSIZE];
-	char *str = cmdbuf;
-	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
-	int argc, inquotes;
-	int repeatable = 1;
-	int rc = 0;
-
-	debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
-	if (DEBUG_PARSER) {
-		/* use puts - string may be loooong */
-		puts(cmd ? cmd : "NULL");
-		puts("\"\n");
-	}
-	clear_ctrlc();		/* forget any previous Control C */
-
-	if (!cmd || !*cmd) {
-		return -1;	/* empty command */
-	}
-
-	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
-		puts ("## Command too long!\n");
-		return -1;
-	}
-
-	strcpy (cmdbuf, cmd);
-
-	/* Process separators and check for invalid
-	 * repeatable commands
-	 */
-
-	debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
-	while (*str) {
-
-		/*
-		 * Find separator, or string end
-		 * Allow simple escape of ';' by writing "\;"
-		 */
-		for (inquotes = 0, sep = str; *sep; sep++) {
-			if ((*sep=='\'') &&
-			    (*(sep-1) != '\\'))
-				inquotes=!inquotes;
-
-			if (!inquotes &&
-			    (*sep == ';') &&	/* separator		*/
-			    ( 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 */
-		debug_parser("token: \"%s\"\n", token);
-
-		/* find macros in this token and replace them */
-		process_macros (token, finaltoken);
-
-		/* Extract arguments */
-		if ((argc = parse_line (finaltoken, argv)) == 0) {
-			rc = -1;	/* no command at all */
-			continue;
-		}
-
-		if (cmd_process(flag, argc, argv, &repeatable, NULL))
-			rc = -1;
-
-		/* Did the user stop this? */
-		if (had_ctrlc ())
-			return -1;	/* if stopped then not repeatable */
-	}
-
-	return rc ? rc : repeatable;
-}
-#endif
-
 /*
  * Run a command using the selected parser.
  *
@@ -1436,10 +472,10 @@
 {
 #ifndef CONFIG_SYS_HUSH_PARSER
 	/*
-	 * builtin_run_command can return 0 or 1 for success, so clean up
+	 * cli_run_command can return 0 or 1 for success, so clean up
 	 * its result.
 	 */
-	if (builtin_run_command(cmd, flag) == -1)
+	if (cli_simple_run_command(cmd, flag) == -1)
 		return 1;
 
 	return 0;
@@ -1449,49 +485,6 @@
 #endif
 }
 
-#ifndef CONFIG_SYS_HUSH_PARSER
-/**
- * Execute a list of command separated by ; or \n using the built-in parser.
- *
- * This function cannot take a const char * for the command, since if it
- * finds newlines in the string, it replaces them with \0.
- *
- * @param cmd	String containing list of commands
- * @param flag	Execution flags (CMD_FLAG_...)
- * @return 0 on success, or != 0 on error.
- */
-static int builtin_run_command_list(char *cmd, int flag)
-{
-	char *line, *next;
-	int rcode = 0;
-
-	/*
-	 * Break into individual lines, and execute each line; terminate on
-	 * error.
-	 */
-	line = next = cmd;
-	while (*next) {
-		if (*next == '\n') {
-			*next = '\0';
-			/* run only non-empty commands */
-			if (*line) {
-				debug("** exec: \"%s\"\n", line);
-				if (builtin_run_command(line, 0) < 0) {
-					rcode = 1;
-					break;
-				}
-			}
-			line = next + 1;
-		}
-		++next;
-	}
-	if (rcode == 0 && *line)
-		rcode = (builtin_run_command(line, 0) >= 0);
-
-	return rcode;
-}
-#endif
-
 int run_command_list(const char *cmd, int len, int flag)
 {
 	int need_buff = 1;
@@ -1525,7 +518,7 @@
 	 * doing a malloc() which is actually required only in a case that
 	 * is pretty rare.
 	 */
-	rcode = builtin_run_command_list(buff, flag);
+	rcode = cli_simple_run_command_list(buff, flag);
 	if (need_buff)
 		free(buff);
 #endif
diff --git a/include/cli.h b/include/cli.h
index 0075bd4..b04539f 100644
--- a/include/cli.h
+++ b/include/cli.h
@@ -99,4 +99,9 @@
  */
 int parse_line(char *line, char *argv[]);
 
+/** bootretry_dont_retry() - Indicate that we should not retry the boot */
+void bootretry_dont_retry(void);
+
+#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk())
+
 #endif