Convert cmdedit into more generic line input facility
(make history and completion optional at runtime).
Use it for fdisk, as an example.
Some unrelated fixes in fdisk are also here.
diff --git a/shell/Kbuild b/shell/Kbuild
index eb0199e..9c60698 100644
--- a/shell/Kbuild
+++ b/shell/Kbuild
@@ -5,8 +5,8 @@
 # Licensed under the GPL v2, see the file LICENSE in this tarball.
 
 lib-y:=
-lib-$(CONFIG_ASH)			+= ash.o
-lib-$(CONFIG_HUSH)			+= hush.o
-lib-$(CONFIG_LASH)			+= lash.o
-lib-$(CONFIG_MSH)			+= msh.o
-lib-$(CONFIG_FEATURE_COMMAND_EDITING)	+= cmdedit.o
+lib-y              += cmdedit.o
+lib-$(CONFIG_ASH)  += ash.o
+lib-$(CONFIG_HUSH) += hush.o
+lib-$(CONFIG_LASH) += lash.o
+lib-$(CONFIG_MSH)  += msh.o
diff --git a/shell/ash.c b/shell/ash.c
index 2db3302..8afdf3d 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -92,7 +92,6 @@
 #include <termios.h>
 #endif
 
-#include "cmdedit.h"
 
 #ifdef __GLIBC__
 /* glibc sucks */
@@ -1238,7 +1237,7 @@
 static int getoptscmd(int, char **);
 #endif
 static int hashcmd(int, char **);
-#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
 static int helpcmd(int argc, char **argv);
 #endif
 #if JOBS
@@ -1347,7 +1346,7 @@
 	{ BUILTIN_REGULAR       "getopts", getoptscmd },
 #endif
 	{ BUILTIN_NOSPEC        "hash", hashcmd },
-#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
 	{ BUILTIN_NOSPEC        "help", helpcmd },
 #endif
 #if JOBS
@@ -1529,7 +1528,7 @@
 	{0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
 	{0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
 #endif
-#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
+#if ENABLE_FEATURE_COMMAND_SAVEHISTORY
 	{0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
 #endif
 };
@@ -1934,10 +1933,6 @@
 #define debug optlist[15]
 #endif
 
-#ifndef CONFIG_FEATURE_COMMAND_EDITING_VI
-#define setvimode(on) viflag = 0   /* forcibly keep the option off */
-#endif
-
 /*      options.c */
 
 
@@ -3718,7 +3713,7 @@
 	clearredir(1);
 	envp = environment();
 	if (strchr(argv[0], '/') || is_safe_applet(argv[0])
-#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
 	 || find_applet_by_name(argv[0])
 #endif
 	) {
@@ -3775,7 +3770,7 @@
 		applet_name = cmd;
 		exit(a->main(argc, argv));
 	}
-#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
 	if (find_applet_by_name(cmd) != NULL) {
 		/* re-exec ourselves with the new arguments */
 		execve(CONFIG_BUSYBOX_EXEC_PATH,argv,envp);
@@ -3949,7 +3944,7 @@
 		return;
 	}
 
-#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
 	if (find_applet_by_name(name)) {
 		entry->cmdtype = CMDNORMAL;
 		entry->u.index = -1;
@@ -6045,21 +6040,18 @@
 }
 
 
-
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
-#ifdef CONFIG_ASH_EXPAND_PRMT
-static char *cmdedit_prompt;
-#else
+#if ENABLE_FEATURE_COMMAND_EDITING
+static line_input_t *line_input_state;
+//static SKIP_ASH_EXPAND_PRMT(const) char *cmdedit_prompt;
 static const char *cmdedit_prompt;
-#endif
 static void putprompt(const char *s)
 {
-#ifdef CONFIG_ASH_EXPAND_PRMT
-	free(cmdedit_prompt);
-	cmdedit_prompt = xstrdup(s);
-#else
+	if (ENABLE_ASH_EXPAND_PRMT) {
+		free((char*)cmdedit_prompt);
+		cmdedit_prompt = xstrdup(s);
+		return;
+	}
 	cmdedit_prompt = s;
-#endif
 }
 #else
 static void putprompt(const char *s)
@@ -6068,6 +6060,16 @@
 }
 #endif
 
+#if ENABLE_FEATURE_COMMAND_EDITING_VI
+#define setvimode(on) do { \
+	if (on) line_input_state->flags |= VI_MODE; \
+	else line_input_state->flags &= ~VI_MODE; \
+} while (0)
+#else
+#define setvimode(on) viflag = 0   /* forcibly keep the option off */
+#endif
+
+
 static int preadfd(void)
 {
 	int nr;
@@ -6075,25 +6077,25 @@
 	parsenextc = buf;
 
 retry:
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 	if (!iflag || parsefile->fd)
 		nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
 	else {
-#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
-		cmdedit_path_lookup = pathval();
+#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
+		line_input_state->path_lookup = pathval();
 #endif
-		nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
-		if(nr == 0) {
-			/* Ctrl+C presend */
-			if(trap[SIGINT]) {
+		nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
+		if (nr == 0) {
+			/* Ctrl+C pressed */
+			if (trap[SIGINT]) {
 				buf[0] = '\n';
-				buf[1] = 0;
+				buf[1] = '\0';
 				raise(SIGINT);
 				return 1;
 			}
 			goto retry;
 		}
-		if(nr < 0 && errno == 0) {
+		if (nr < 0 && errno == 0) {
 			/* Ctrl+D presend */
 			nr = 0;
 		}
@@ -7913,6 +7915,10 @@
 #if PROFILE
 	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
 #endif
+
+#if ENABLE_FEATURE_COMMAND_EDITING
+	line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
+#endif
 	state = 0;
 	if (setjmp(jmploc.loc)) {
 		int e;
@@ -7954,11 +7960,11 @@
 	init();
 	setstackmark(&smark);
 	procargs(argc, argv);
-#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
-	if ( iflag ) {
+#if ENABLE_FEATURE_COMMAND_SAVEHISTORY
+	if (iflag) {
 		const char *hp = lookupvar("HISTFILE");
 
-		if(hp == NULL ) {
+		if (hp == NULL) {
 			hp = lookupvar("HOME");
 			if(hp != NULL) {
 				char *defhp = concat_path_file(hp, ".ash_history");
@@ -7995,15 +8001,15 @@
 		evalstring(minusc, 0);
 
 	if (sflag || minusc == NULL) {
-#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
-	    if ( iflag ) {
-		const char *hp = lookupvar("HISTFILE");
+#if ENABLE_FEATURE_COMMAND_SAVEHISTORY
+		if ( iflag ) {
+			const char *hp = lookupvar("HISTFILE");
 
-		if(hp != NULL )
-			load_history ( hp );
-	    }
+			if (hp != NULL)
+				line_input_state->hist_file = hp;
+		}
 #endif
-state4: /* XXX ??? - why isn't this before the "if" statement */
+ state4: /* XXX ??? - why isn't this before the "if" statement */
 		cmdloop(1);
 	}
 #if PROFILE
@@ -11880,7 +11886,7 @@
 	setsignal(SIGINT);
 	setsignal(SIGQUIT);
 	setsignal(SIGTERM);
-#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
 		if(is_interactive > 1) {
 			/* Looks like they want an interactive shell */
 			static int do_banner;
@@ -11897,7 +11903,7 @@
 }
 
 
-#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
 /*** List the available builtins ***/
 
 static int helpcmd(int argc, char **argv)
@@ -11913,7 +11919,7 @@
 			col = 0;
 		}
 	}
-#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
 	for (i = 0; i < NUM_APPLETS; i++) {
 		col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
 		if (col > 60) {
@@ -11945,7 +11951,7 @@
 /* dash bug: it just does _exit(exitstatus) here
  * but we have to do setjobctl(0) first!
  * (bug is still not fixed in dash-0.5.3 - if you run dash
- * under Midnight Commander, on exit MC is backgrounded) */
+ * under Midnight Commander, on exit from dash MC is backgrounded) */
 			status = exitstatus;
 		goto out;
 	}
@@ -11955,14 +11961,6 @@
 		evalstring(p, 0);
 	}
 	flushall();
-#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
-	if (iflag && rootshell) {
-		const char *hp = lookupvar("HISTFILE");
-
-		if (hp != NULL)
-			save_history(hp);
-	}
-#endif
 out:
 	setjobctl(0);
 	_exit(status);
@@ -13491,7 +13489,7 @@
 #define endexpression &op_tokens[sizeof(op_tokens)-7]
 
 
-static arith_t arith (const char *expr, int *perrcode)
+static arith_t arith(const char *expr, int *perrcode)
 {
     char arithval; /* Current character under analysis */
     operator lasttok, op;
diff --git a/shell/cmdedit.c b/shell/cmdedit.c
index a1432af..554a4eb 100644
--- a/shell/cmdedit.c
+++ b/shell/cmdedit.c
@@ -30,7 +30,6 @@
 
 #include <sys/ioctl.h>
 #include "busybox.h"
-#include "cmdedit.h"
 
 
 /* FIXME: obsolete CONFIG item? */
@@ -51,7 +50,6 @@
 /* Entire file (except TESTing part) sits inside this #if */
 #if ENABLE_FEATURE_COMMAND_EDITING
 
-
 #if ENABLE_LOCALE_SUPPORT
 #define Isprint(c) isprint(c)
 #else
@@ -61,29 +59,21 @@
 #define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
 (ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION || ENABLE_FEATURE_SH_FANCY_PROMPT)
 
-/* Maximum length of command line history */
-#if !ENABLE_FEATURE_COMMAND_HISTORY
-#define MAX_HISTORY   15
-#else
-#define MAX_HISTORY   (CONFIG_FEATURE_COMMAND_HISTORY + 0)
-#endif
 
+static line_input_t *state;
 
-/* Current termios and the previous termios before starting sh */
 static struct termios initial_settings, new_settings;
 
-static
-volatile unsigned cmdedit_termw = 80;        /* actual terminal width */
-
+static volatile unsigned cmdedit_termw = 80;        /* actual terminal width */
 
 static int cmdedit_x;           /* real x terminal position */
 static int cmdedit_y;           /* pseudoreal y terminal position */
 static int cmdedit_prmt_len;    /* length of prompt (without colors etc) */
 
-static int cursor;
-static int len;
+static unsigned cursor;
+static unsigned command_len;
 static char *command_ps;
-static SKIP_FEATURE_SH_FANCY_PROMPT(const) char *cmdedit_prompt;
+static const char *cmdedit_prompt;
 
 #if ENABLE_FEATURE_SH_FANCY_PROMPT
 static char *hostname_buf;
@@ -142,7 +132,7 @@
 /* Move to end of line (by printing all chars till the end) */
 static void input_end(void)
 {
-	while (cursor < len)
+	while (cursor < command_len)
 		cmdedit_set_out_char(' ');
 }
 
@@ -200,7 +190,7 @@
 static void put_prompt(void)
 {
 	out1str(cmdedit_prompt);
-	cmdedit_x = cmdedit_prmt_len;   /* count real x terminal position */
+	cmdedit_x = cmdedit_prmt_len;
 	cursor = 0;
 // Huh? what if cmdedit_prmt_len >= width?
 	cmdedit_y = 0;                  /* new quasireal y */
@@ -231,7 +221,7 @@
 {
 	int j = cursor;
 
-	if (j == len)
+	if (j == command_len)
 		return;
 
 #if ENABLE_FEATURE_COMMAND_EDITING_VI
@@ -249,7 +239,7 @@
 #endif
 
 	strcpy(command_ps + j, command_ps + j + 1);
-	len--;
+	command_len--;
 	input_end();                    /* rewrite new line */
 	cmdedit_set_out_char(' ');      /* erase char */
 	input_backward(cursor - j);     /* back to old pos cursor */
@@ -285,7 +275,7 @@
 /* Move forward one character */
 static void input_forward(void)
 {
-	if (cursor < len)
+	if (cursor < command_len)
 		cmdedit_set_out_char(command_ps[cursor + 1]);
 }
 
@@ -372,54 +362,50 @@
 	FIND_FILE_ONLY = 2,
 };
 
-#if ENABLE_ASH
-const char *cmdedit_path_lookup;
-#endif
 static int path_parse(char ***p, int flags)
 {
 	int npth;
 	const char *tmp;
-#if ENABLE_ASH
-	const char *pth = cmdedit_path_lookup;
-#else
-	const char *pth = getenv("PATH")
-#endif
+	const char *pth;
+	char **res;
 
 	/* if not setenv PATH variable, to search cur dir "." */
 	if (flags != FIND_EXE_ONLY)
 		return 1;
+
+	if (state->flags & WITH_PATH_LOOKUP)
+		pth = state->path_lookup;
+	else
+		pth = getenv("PATH");
 	/* PATH=<empty> or PATH=:<empty> */
 	if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
 		return 1;
 
 	tmp = pth;
-	npth = 0;
-
+	npth = 1; /* path component count */
 	while (1) {
-		npth++;                 /* count words is + 1 count ':' */
 		tmp = strchr(tmp, ':');
 		if (!tmp)
 			break;
 		if (*++tmp == '\0')
 			break;  /* :<empty> */
+		npth++;
 	}
 
-	*p = xmalloc(npth * sizeof(char *));
-
+	res = xmalloc(npth * sizeof(char*));
+	res[0] = xstrdup(pth);
 	tmp = pth;
-	(*p)[0] = xstrdup(tmp);
-	npth = 1;                       /* count words is + 1 count ':' */
-
+	npth = 1;
 	while (1) {
 		tmp = strchr(tmp, ':');
 		if (!tmp)
 			break;
-		(*p)[0][(tmp - pth)] = 0;       /* ':' -> '\0' */
-		if (*++tmp == 0)
-			break;                  /* :<empty> */
-		(*p)[npth++] = &(*p)[0][(tmp - pth)];   /* p[next]=p[0][&'\0'+1] */
+		*tmp++ = '\0'; /* ':' -> '\0' */
+		if (*tmp == '\0')
+			break; /* :<empty> */
+		res[npth++] = tmp;
 	}
-
+	*p = res;
 	return npth;
 }
 
@@ -742,6 +728,9 @@
 /* Do TAB completion */
 static void input_tab(int *lastWasTab)
 {
+	if (!(state->flags & TAB_COMPLETION))
+		return;
+
 	if (!*lastWasTab) {
 		char *tmp, *tmp1;
 		int len_found;
@@ -764,13 +753,13 @@
 #if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION
 		/* If the word starts with `~' and there is no slash in the word,
 		 * then try completing this word as a username. */
-
-		if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
-			username_tab_completion(matchBuf, NULL);
-		if (!matches)
+		if (state->flags & USERNAME_COMPLETION)
+			if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
+				username_tab_completion(matchBuf, NULL);
 #endif
 		/* Try to match any executable in our path and everything
 		 * in the current working directory */
+		if (!matches)
 			exe_n_cwd_tab_completion(matchBuf, find_type);
 		/* Sort, then remove any duplicates found */
 		if (matches) {
@@ -855,51 +844,48 @@
 	}
 }
 
+#else
+#define input_tab(a) ((void)0)
 #endif  /* FEATURE_COMMAND_TAB_COMPLETION */
 
 
 #if MAX_HISTORY > 0
 
-static char *history[MAX_HISTORY+1]; /* history + current */
-/* saved history lines */
-static int n_history;
-/* current pointer to history line */
-static int cur_history;
-
+/* state->flags is already checked to be nonzero */
 static void get_previous_history(void)
 {
-	if (command_ps[0] != '\0' || history[cur_history] == NULL) {
-		free(history[cur_history]);
-		history[cur_history] = xstrdup(command_ps);
+	if (command_ps[0] != '\0' || state->history[state->cur_history] == NULL) {
+		free(state->history[state->cur_history]);
+		state->history[state->cur_history] = xstrdup(command_ps);
 	}
-	cur_history--;
+	state->cur_history--;
 }
 
 static int get_next_history(void)
 {
-	int ch = cur_history;
-
-	if (ch < n_history) {
-		get_previous_history(); /* save the current history line */
-		cur_history = ch + 1;
-		return cur_history;
-	} else {
-		beep();
-		return 0;
+	if (state->flags & DO_HISTORY) {
+		int ch = state->cur_history;
+		if (ch < state->cnt_history) {
+			get_previous_history(); /* save the current history line */
+			state->cur_history = ch + 1;
+			return state->cur_history;
+		}
 	}
+	beep();
+	return 0;
 }
 
 #if ENABLE_FEATURE_COMMAND_SAVEHISTORY
+/* state->flags is already checked to be nonzero */
 void load_history(const char *fromfile)
 {
 	FILE *fp;
 	int hi;
 
 	/* cleanup old */
-
-	for (hi = n_history; hi > 0;) {
+	for (hi = state->cnt_history; hi > 0;) {
 		hi--;
-		free(history[hi]);
+		free(state->history[hi]);
 	}
 
 	fp = fopen(fromfile, "r");
@@ -917,29 +903,62 @@
 				free(hl);
 				continue;
 			}
-			history[hi++] = hl;
+			state->history[hi++] = hl;
 		}
 		fclose(fp);
 	}
-	cur_history = n_history = hi;
+	state->cur_history = state->cnt_history = hi;
 }
 
+/* state->flags is already checked to be nonzero */
 void save_history(const char *tofile)
 {
-	FILE *fp = fopen(tofile, "w");
+	FILE *fp;
 
+	fp = fopen(tofile, "w");
 	if (fp) {
 		int i;
 
-		for (i = 0; i < n_history; i++) {
-			fprintf(fp, "%s\n", history[i]);
+		for (i = 0; i < state->cnt_history; i++) {
+			fprintf(fp, "%s\n", state->history[i]);
 		}
 		fclose(fp);
 	}
 }
+#else
+#define load_history(a) ((void)0)
+#define save_history(a) ((void)0)
 #endif /* FEATURE_COMMAND_SAVEHISTORY */
 
-#endif /* MAX_HISTORY > 0 */
+static void remember_in_history(const char *str)
+{
+	int i;
+
+	if (!(state->flags & DO_HISTORY))
+		return;
+
+	i = state->cnt_history;
+	free(state->history[MAX_HISTORY]);
+	state->history[MAX_HISTORY] = NULL;
+	/* After max history, remove the oldest command */
+	if (i >= MAX_HISTORY) {
+		free(state->history[0]);
+		for (i = 0; i < MAX_HISTORY-1; i++)
+			state->history[i] = state->history[i+1];
+	}
+// Maybe "if (!i || strcmp(history[i-1], command) != 0) ..."
+// (i.e. do not save dups?)
+	state->history[i++] = xstrdup(str);
+	state->cur_history = i;
+	state->cnt_history = i;
+	if (state->flags & SAVE_HISTORY)
+		save_history(state->hist_file);
+	USE_FEATURE_SH_FANCY_PROMPT(num_ok_lines++;)
+}
+
+#else /* MAX_HISTORY == 0 */
+#define remember_in_history(a) ((void)0)
+#endif /* MAX_HISTORY */
 
 
 /*
@@ -960,13 +979,6 @@
  */
 
 #if ENABLE_FEATURE_COMMAND_EDITING_VI
-static int vi_mode;
-
-void setvimode(int viflag)
-{
-	vi_mode = viflag;
-}
-
 static void
 vi_Word_motion(char *command, int eat)
 {
@@ -1058,13 +1070,11 @@
 			input_backward(1);
 	}
 }
-#else
-enum { vi_mode = 0 };
 #endif
 
 
 /*
- * cmdedit_read_input and its helpers
+ * read_line_input and its helpers
  */
 
 #if !ENABLE_FEATURE_SH_FANCY_PROMPT
@@ -1190,7 +1200,7 @@
 			cmdedit_prmt_len += cur_prmt_len;
 		prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
 	}
-	if (pwd_buf!=(char *)bb_msg_unknown)
+	if (pwd_buf != (char *)bb_msg_unknown)
 		free(pwd_buf);
 	cmdedit_prompt = prmt_mem_ptr;
 	put_prompt();
@@ -1217,7 +1227,7 @@
 		/* new y for current cursor */
 		int new_y = (cursor + cmdedit_prmt_len) / w;
 		/* redraw */
-		redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
+		redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
 		fflush(stdout);
 	}
 }
@@ -1275,9 +1285,10 @@
 #undef CTRL
 #define CTRL(a) ((a) & ~0x40)
 
-
-int cmdedit_read_input(char *prompt, char command[BUFSIZ])
+int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *st)
 {
+	static const int null_flags;
+
 	int lastWasTab = FALSE;
 	unsigned int ic;
 	unsigned char c;
@@ -1286,18 +1297,28 @@
 	smallint vi_cmdmode = 0;
 	smalluint prevc;
 #endif
+
+// FIXME: audit & improve this
+	if (maxsize > BUFSIZ)
+		maxsize = BUFSIZ;
+
+	/* With null flags, no other fields are ever used */
+	state = st ? st : (line_input_t*) &null_flags;
+	if (state->flags & SAVE_HISTORY)
+		load_history(state->hist_file);
+
 	/* prepare before init handlers */
 	cmdedit_y = 0;  /* quasireal y, not true if line > xt*yt */
-	len = 0;
+	command_len = 0;
 	command_ps = command;
 	command[0] = '\0';
 
 	getTermSettings(0, (void *) &initial_settings);
-	memcpy(&new_settings, &initial_settings, sizeof(struct termios));
+	memcpy(&new_settings, &initial_settings, sizeof(new_settings));
 	new_settings.c_lflag &= ~ICANON;        /* unbuffered input */
 	/* Turn off echoing and CTRL-C, so we can trap it */
 	new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
-	/* Hmm, in linux c_cc[] not parsed if set ~ICANON */
+	/* Hmm, in linux c_cc[] is not parsed if ICANON is off */
 	new_settings.c_cc[VMIN] = 1;
 	new_settings.c_cc[VTIME] = 0;
 	/* Turn off CTRL-C, so we can trap it */
@@ -1354,34 +1375,18 @@
 		vi_case(CTRL('C')|vbit:)
 			/* Control-c -- stop gathering input */
 			goto_new_line();
-#if !ENABLE_ASH
-			command[0] = '\0';
-			len = 0;
-			lastWasTab = FALSE;
-			put_prompt();
-#else
-			len = 0;
-			break_out = -1; /* to control traps */
-#endif
+			command_len = 0;
+			break_out = -1; /* "do not append '\n'" */
 			break;
 		case CTRL('D'):
 			/* Control-d -- Delete one character, or exit
 			 * if the len=0 and no chars to delete */
-			if (len == 0) {
+			if (command_len == 0) {
 				errno = 0;
  prepare_to_die:
-// So, our API depends on whether we have ash compiled in or not? Crap...
-#if !ENABLE_ASH
-				printf("exit");
-				goto_new_line();
-				/* cmdedit_reset_term() called in atexit */
-// FIXME. this is definitely not good
-				exit(EXIT_SUCCESS);
-#else
 				/* to control stopped jobs */
-				break_out = len = -1;
+				break_out = command_len = -1;
 				break;
-#endif
 			}
 			input_delete(0);
 			break;
@@ -1407,23 +1412,21 @@
 			break;
 
 		case '\t':
-#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
 			input_tab(&lastWasTab);
-#endif
 			break;
 
 #if ENABLE_FEATURE_EDITING_FANCY_KEYS
 		case CTRL('K'):
 			/* Control-k -- clear to end of line */
 			command[cursor] = 0;
-			len = cursor;
+			command_len = cursor;
 			printf("\033[J");
 			break;
 		case CTRL('L'):
 		vi_case(CTRL('L')|vbit:)
 			/* Control-l -- clear screen */
 			printf("\033[H");
-			redraw(0, len - cursor);
+			redraw(0, command_len - cursor);
 			break;
 #endif
 
@@ -1439,12 +1442,11 @@
 		vi_case(CTRL('P')|vbit:)
 		vi_case('k'|vbit:)
 			/* Control-p -- Get previous command from history */
-			if (cur_history > 0) {
+			if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
 				get_previous_history();
 				goto rewrite_line;
-			} else {
-				beep();
 			}
+			beep();
 			break;
 #endif
 
@@ -1454,7 +1456,8 @@
 			/* Control-U -- Clear line before cursor */
 			if (cursor) {
 				strcpy(command, command + cursor);
-				redraw(cmdedit_y, len -= cursor);
+				command_len -= cursor;
+				redraw(cmdedit_y, command_len);
 			}
 			break;
 #endif
@@ -1571,7 +1574,7 @@
 				break;
 			case '$':  /* "d$", "c$" */
 			clear_to_eol:
-				while (cursor < len)
+				while (cursor < command_len)
 					input_delete(1);
 				break;
 			}
@@ -1599,7 +1602,7 @@
 		case '\x1b': /* ESC */
 
 #if ENABLE_FEATURE_COMMAND_EDITING_VI
-			if (vi_mode) {
+			if (state->flags & VI_MODE) {
 				/* ESC: insert mode --> command mode */
 				vi_cmdmode = 1;
 				input_backward(1);
@@ -1634,7 +1637,7 @@
 #if MAX_HISTORY > 0
 			case 'A':
 				/* Up Arrow -- Get previous command from history */
-				if (cur_history > 0) {
+				if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
 					get_previous_history();
 					goto rewrite_line;
 				}
@@ -1647,9 +1650,9 @@
  rewrite_line:
 				/* Rewrite the line with the selected history item */
 				/* change command */
-				len = strlen(strcpy(command, history[cur_history]));
+				command_len = strlen(strcpy(command, state->history[state->cur_history]));
 				/* redraw and go to eol (bol, in vi */
-				redraw(cmdedit_y, vi_mode ? 9999 : 0);
+				redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
 				break;
 #endif
 			case 'C':
@@ -1700,18 +1703,18 @@
 			if (!Isprint(c)) /* Skip non-printable characters */
 				break;
 
-			if (len >= (BUFSIZ - 2))        /* Need to leave space for enter */
+			if (command_len >= (maxsize - 2))        /* Need to leave space for enter */
 				break;
 
-			len++;
-			if (cursor == (len - 1)) {      /* Append if at the end of the line */
+			command_len++;
+			if (cursor == (command_len - 1)) {      /* Append if at the end of the line */
 				command[cursor] = c;
 				command[cursor+1] = '\0';
 				cmdedit_set_out_char(' ');
 			} else {                        /* Insert otherwise */
 				int sc = cursor;
 
-				memmove(command + sc + 1, command + sc, len - sc);
+				memmove(command + sc + 1, command + sc, command_len - sc);
 				command[sc] = c;
 				sc++;
 				/* rewrite from cursor */
@@ -1728,35 +1731,12 @@
 			lastWasTab = FALSE;
 	}
 
-#if MAX_HISTORY > 0
-	/* Handle command history log */
-	/* cleanup may be saved current command line */
-	if (len > 0) {
-		int i = n_history;
-
-		free(history[MAX_HISTORY]);
-		history[MAX_HISTORY] = NULL;
-		/* After max history, remove the oldest command */
-		if (i >= MAX_HISTORY) {
-			free(history[0]);
-			for (i = 0; i < MAX_HISTORY-1; i++)
-				history[i] = history[i+1];
-		}
-// Maybe "if (!i || strcmp(history[i-1], command) != 0) ..."
-// (i.e. do not save dups?)
-		history[i++] = xstrdup(command);
-		cur_history = i;
-		n_history = i;
-		USE_FEATURE_SH_FANCY_PROMPT(num_ok_lines++;)
-	}
-#else /* MAX_HISTORY == 0 */
-	/* dont put empty line */
-	USE_FEATURE_SH_FANCY_PROMPT(if (len > 0) num_ok_lines++;)
-#endif /* MAX_HISTORY */
+	if (command_len > 0)
+		remember_in_history(command);
 
 	if (break_out > 0) {
-		command[len++] = '\n';
-		command[len] = '\0';
+		command[command_len++] = '\n';
+		command[command_len] = '\0';
 	}
 
 #if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_COMMAND_TAB_COMPLETION
@@ -1764,11 +1744,29 @@
 #endif
 
 #if ENABLE_FEATURE_SH_FANCY_PROMPT
-	free(cmdedit_prompt);
+	free((char*)cmdedit_prompt);
 #endif
 	/* restore initial_settings and SIGWINCH handler */
 	cmdedit_reset_term();
-	return len;
+	return command_len;
+}
+
+line_input_t *new_line_input_t(int flags)
+{
+	line_input_t *n = xzalloc(sizeof(*n));
+	n->flags = flags;
+	return n;
+}
+
+#else
+
+#undef read_line_input
+int read_line_input(const char* prompt, char* command, int maxsize)
+{
+	fputs(prompt, stdout);
+	fflush(stdout);
+	fgets(command, maxsize, stdin);
+	return strlen(command);
 }
 
 #endif  /* FEATURE_COMMAND_EDITING */
@@ -1801,13 +1799,13 @@
 #endif
 	while (1) {
 		int l;
-		l = cmdedit_read_input(prompt, buff);
+		l = read_line_input(prompt, buff);
 		if (l <= 0 || buff[l-1] != '\n')
 			break;
 		buff[l-1] = 0;
-		printf("*** cmdedit_read_input() returned line =%s=\n", buff);
+		printf("*** read_line_input() returned line =%s=\n", buff);
 	}
-	printf("*** cmdedit_read_input() detect ^D\n");
+	printf("*** read_line_input() detect ^D\n");
 	return 0;
 }
 
diff --git a/shell/cmdedit.h b/shell/cmdedit.h
index 4a32cf6..7af2f75 100644
--- a/shell/cmdedit.h
+++ b/shell/cmdedit.h
@@ -1,20 +1 @@
-/* vi: set sw=4 ts=4: */
-#ifndef CMDEDIT_H
-#define CMDEDIT_H
-
-int cmdedit_read_input(char* promptStr, char* command);
-
-#if ENABLE_ASH
-extern const char *cmdedit_path_lookup;
-#endif
-
-#if ENABLE_FEATURE_COMMAND_SAVEHISTORY
-void load_history(const char *fromfile);
-void save_history(const char *tofile);
-#endif
-
-#if ENABLE_FEATURE_COMMAND_EDITING_VI
-void setvimode(int viflag);
-#endif
-
-#endif /* CMDEDIT_H */
+/* TO DELETE */
diff --git a/shell/hush.c b/shell/hush.c
index 8f2dc80..2c88238 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -98,7 +98,6 @@
 /* #include <dmalloc.h> */
 /* #define DEBUG_SHELL */
 
-#include "cmdedit.h"
 
 #define SPECIAL_VAR_SYMBOL 03
 #define FLAG_EXIT_FROM_LOOP 1
@@ -883,20 +882,24 @@
 	debug_printf("result %s\n",*prompt_str);
 }
 
+#if ENABLE_FEATURE_COMMAND_EDITING
+static line_input_t *line_input_state;
+#endif
+
 static void get_user_input(struct in_str *i)
 {
 	char *prompt_str;
 	static char the_command[BUFSIZ];
 
 	setup_prompt_string(i->promptmode, &prompt_str);
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 	/*
 	 ** enable command line editing only while a command line
 	 ** is actually being read; otherwise, we'll end up bequeathing
 	 ** atexit() handlers and other unwanted stuff to our
 	 ** child processes (rob@sysgo.de)
 	 */
-	cmdedit_read_input(prompt_str, the_command);
+	read_line_input(prompt_str, the_command, BUFSIZ, line_input_state);
 #else
 	fputs(prompt_str, stdout);
 	fflush(stdout);
@@ -2647,6 +2650,10 @@
 	FILE *input;
 	char **e = environ;
 
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+	line_input_state = new_line_input_t(FOR_SHELL);
+#endif
+
 	/* XXX what should these be while sourcing /etc/profile? */
 	global_argc = argc;
 	global_argv = argv;
diff --git a/shell/lash.c b/shell/lash.c
index b2ccaf0..a09a9a9 100644
--- a/shell/lash.c
+++ b/shell/lash.c
@@ -23,8 +23,6 @@
 
 #include "busybox.h"
 #include <getopt.h>
-#include "cmdedit.h"
-
 #include <glob.h>
 #define expand_t	glob_t
 
@@ -641,6 +639,10 @@
 #endif
 }
 
+#if ENABLE_FEATURE_COMMAND_EDITING
+static line_input_t *line_input_state;
+#endif
+
 static int get_command(FILE * source, char *command)
 {
 	char *prompt_str;
@@ -659,14 +661,14 @@
 	if (source == stdin) {
 		setup_prompt_string(&prompt_str);
 
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 		/*
 		** enable command line editing only while a command line
 		** is actually being read; otherwise, we'll end up bequeathing
 		** atexit() handlers and other unwanted stuff to our
 		** child processes (rob@sysgo.de)
 		*/
-		cmdedit_read_input(prompt_str, command);
+		read_line_input(prompt_str, command, BUFSIZ, line_input_state);
 		return 0;
 #else
 		fputs(prompt_str, stdout);
@@ -1505,6 +1507,10 @@
 	argc = argc_l;
 	argv = argv_l;
 
+#if ENABLE_FEATURE_COMMAND_EDITING
+	line_input_state = new_line_input_t(FOR_SHELL);
+#endif
+
 	/* These variables need re-initializing when recursing */
 	last_jobid = 0;
 	close_me_list = NULL;
diff --git a/shell/msh.c b/shell/msh.c
index c88308f..8746e42 100644
--- a/shell/msh.c
+++ b/shell/msh.c
@@ -17,7 +17,6 @@
 #include <setjmp.h>
 #include <sys/times.h>
 
-#include "cmdedit.h"
 
 /*#define MSHDEBUG 1*/
 
@@ -777,7 +776,7 @@
 #endif							/* MSHDEBUG */
 
 
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 static char *current_prompt;
 #endif
 
@@ -787,6 +786,10 @@
  */
 
 
+#if ENABLE_FEATURE_COMMAND_EDITING
+static line_input_t *line_input_state;
+#endif
+
 int msh_main(int argc, char **argv)
 {
 	int f;
@@ -795,6 +798,10 @@
 	char *name, **ap;
 	int (*iof) (struct ioarg *);
 
+#if ENABLE_FEATURE_COMMAND_EDITING
+	line_input_state = new_line_input_t(FOR_SHELL);
+#endif
+
 	DBGPRINTF(("MSH_MAIN: argc %d, environ %p\n", argc, environ));
 
 	initarea();
@@ -964,7 +971,7 @@
 
 	for (;;) {
 		if (interactive && e.iop <= iostack) {
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 			current_prompt = prompt->value;
 #else
 			prs(prompt->value);
@@ -2371,7 +2378,7 @@
 		startl = 1;
 		if (multiline || cf & CONTIN) {
 			if (interactive && e.iop <= iostack) {
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 				current_prompt = cprompt->value;
 #else
 				prs(cprompt->value);
@@ -2432,7 +2439,7 @@
 			return YYERRCODE;
 		}
 		if (interactive && c == '\n' && e.iop <= iostack) {
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 			current_prompt = cprompt->value;
 #else
 			prs(cprompt->value);
@@ -4666,7 +4673,7 @@
 					return e.iop->prev = 0;
 				}
 				if (interactive && e.iop == iostack + 1) {
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 					current_prompt = prompt->value;
 #else
 					prs(prompt->value);
@@ -4898,13 +4905,13 @@
 		ap->afpos++;
 		return *bp->bufp++ & 0177;
 	}
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 	if (interactive && isatty(ap->afile)) {
 		static char mycommand[BUFSIZ];
 		static int position = 0, size = 0;
 
 		while (size == 0 || position >= size) {
-			cmdedit_read_input(current_prompt, mycommand);
+			read_line_input(current_prompt, mycommand, BUFSIZ, line_input_state);
 			size = strlen(mycommand);
 			position = 0;
 		}
@@ -4913,7 +4920,6 @@
 		return c;
 	} else
 #endif
-
 	{
 		i = safe_read(ap->afile, &c, sizeof(c));
 		return i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0);
@@ -5150,7 +5156,7 @@
 		e.iobase = e.iop;
 		for (;;) {
 			if (interactive && e.iop <= iostack) {
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#if ENABLE_FEATURE_COMMAND_EDITING
 				current_prompt = cprompt->value;
 #else
 				prs(cprompt->value);