[BusyBox] enhanced cmdedit

Stefani Seibold stefani at seibold.net
Tue Jan 20 16:59:10 UTC 2004


Hi folks,

below a patch against busybox-1.00-pre5.

- First it fix a small bug in the tab command completion. 

  Currently the tab command completion use only the pathes in the 
  enviroment variable path and not the acutal defined path variable in the shell.

  This is a bad behavior because it make it necessary to invoke a new shell for tab 
  completion with the new defined path.

- Second it enables incremental search in the command history
  
  You can use now CTRL-R for reverse incremental search and CTRL-S for
  forward incremental search in the command history like in the bash shell. The
  behavior is mostly like the bash shell.

  This is a very especial usefull feature for embedded developer when testing and
  working on the target.

- Third it enables to jump to the bottom or top of the history
  
  The page up and page down key jumps now to the top or bottom
  of the hisotry.

  This is still also very usefull for embedded developers.

Please apply the patch to the current development tree if you like.

Have fun,
Stefani

----------------------------------------------------------------------------------------

diff -ru busybox-1.00-pre5.orig/shell/ash.c busybox-1.00-pre5/shell/ash.c
--- busybox-1.00-pre5.orig/shell/ash.c	2003-11-25 21:45:38.000000000 +0100
+++ busybox-1.00-pre5/shell/ash.c	2004-01-12 11:58:27.000000000 +0100
@@ -6041,6 +6041,9 @@
 	if (!iflag || parsefile->fd)
 		nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
 	else {
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+		cmdedit_comp_path = lookupvar("PATH");
+#endif
 		nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
 		if(nr == 0) {
 			/* Ctrl+C presend */
diff -ru busybox-1.00-pre5.orig/shell/cmdedit.c busybox-1.00-pre5/shell/cmdedit.c
--- busybox-1.00-pre5.orig/shell/cmdedit.c	2003-09-15 10:33:40.000000000 +0200
+++ busybox-1.00-pre5/shell/cmdedit.c	2004-01-20 14:48:26.000000000 +0100
@@ -96,6 +96,8 @@
 #define MAX_HISTORY   CONFIG_FEATURE_COMMAND_HISTORY
 #endif
 
+char *cmdedit_comp_path;
+
 #if MAX_HISTORY < 1
 #warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
 #else
@@ -110,6 +112,11 @@
 #define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
 #define getTermSettings(fd,argp) tcgetattr(fd, argp);
 
+enum {
+	ESC = 27,
+	DEL = 127,
+};
+
 /* Current termio and the previous termio before starting sh */
 static struct termios initial_settings, new_settings;
 
@@ -261,6 +268,7 @@
 static inline void beep(void)
 {
 	putchar('\007');
+	fflush(stdout);
 }
 
 /* Move back one charactor */
@@ -308,7 +316,6 @@
 {
 	cmdedit_prompt = prmt_ptr;
 	cmdedit_prmt_len = strlen(prmt_ptr);
-	put_prompt();
 }
 #else
 static void parse_prompt(const char *prmt_ptr)
@@ -428,7 +435,6 @@
 		free(pwd_buf);
 	cmdedit_prompt = prmt_mem_ptr;
 	cmdedit_prmt_len = prmt_len - sub_len;
-	put_prompt();
 }
 #endif
 
@@ -616,16 +622,16 @@
 {
 	int npth;
 	char *tmp;
-	char *pth;
 
 	/* if not setenv PATH variable, to search cur dir "." */
-	if (flags != FIND_EXE_ONLY || (pth = getenv("PATH")) == 0 ||
+	if (flags != FIND_EXE_ONLY ||
 		/* PATH=<empty> or PATH=:<empty> */
-		*pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
+		*cmdedit_comp_path == 0 ||
+		(*cmdedit_comp_path == ':' && *(cmdedit_comp_path + 1) == 0)) {
 		return 1;
 	}
 
-	tmp = pth;
+	tmp = cmdedit_comp_path;
 	npth = 0;
 
 	for (;;) {
@@ -640,19 +646,19 @@
 
 	*p = xmalloc(npth * sizeof(char *));
 
-	tmp = pth;
+	tmp = cmdedit_comp_path;
 	(*p)[0] = bb_xstrdup(tmp);
 	npth = 1;                       /* count words is + 1 count ':' */
 
 	for (;;) {
 		tmp = strchr(tmp, ':');
 		if (tmp) {
-			(*p)[0][(tmp - pth)] = 0;       /* ':' -> '\0' */
+			(*p)[0][(tmp - cmdedit_comp_path)] = 0;       /* ':' -> '\0' */
 			if (*++tmp == 0)
 				break;                  /* :<empty> */
 		} else
 			break;
-		(*p)[npth++] = &(*p)[0][(tmp - pth)];   /* p[next]=p[0][&'\0'+1] */
+		(*p)[npth++] = &(*p)[0][(tmp - cmdedit_comp_path)];   /* p[next]=p[0][&'\0'+1] */
 	}
 
 	return npth;
@@ -1122,26 +1128,211 @@
 #endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
 
 #if MAX_HISTORY >= 1
-static void get_previous_history(void)
+static void store_history(void)
 {
 	if(command_ps[0] != 0 || history[cur_history] == 0) {
 		free(history[cur_history]);
 		history[cur_history] = bb_xstrdup(command_ps);
 	}
+}
+
+static void get_previous_history(void)
+{
+	store_history();
 	cur_history--;
 }
 
 static int get_next_history(void)
 {
-	int ch = cur_history;
+	if (cur_history < n_history) {
+		store_history(); /* save the current history line */
+		return ++cur_history;
+	}
+	beep();
+	return 0;
+}
+
+static int find_str(char *s1,char *s2,int s1_len,int c)
+{
+	int	s2_len;
+	int i;
 
-	if (ch < n_history) {
-		get_previous_history(); /* save the current history line */
-		return (cur_history = ch+1);
-	} else {
+	s2_len=strlen(s2);
+
+	for(i=0;i+s1_len<=s2_len;i++) {
+
+		if ((strncmp(s1,s2+i,s1_len)==0)&&((c<0)||(s2[i+s1_len]==c)))
+			return i;
+	}
+	return -1;
+}
+
+static int find_history(int idx, int last,int searchLen,int c,int dir) 
+{
+	int	ret;
+
+	do {
+		ret=find_str(command_ps+cursor,history[idx],searchLen,c);
+		if (ret>=0) {
+			cursor=ret;
+
+			if (idx==cur_history)
+				return 0;
+
+			if (strcmp(history[cur_history],history[idx])) {
+				cur_history=idx;
+				len=strlen(strcpy(command_ps,history[cur_history]));
+				return 1;
+			}
+		}
+		idx+=dir;
+	} while(idx!=last);
+
+	return -1;
+}
+
+static void histPrompt(char *promptBuf,char *promptStr,int searchLen)
+{
+	int	sav_cursor;
+
+	cmdedit_prmt_len = strlen(promptStr);
+	cmdedit_prompt = promptBuf;
+
+	memcpy(promptBuf,promptStr,cmdedit_prmt_len);
+
+	promptBuf+=cmdedit_prmt_len;
+
+	memcpy(promptBuf,command_ps+cursor,searchLen);
+	
+	promptBuf+=searchLen;
+
+	*promptBuf++='\'';
+	*promptBuf++=':';
+	*promptBuf++=' ';
+
+	*promptBuf=0;
+
+	cmdedit_prmt_len+=searchLen + 3;
+
+	sav_cursor=cursor;
+
+	redraw(cmdedit_y, len - cursor);
+
+	cursor=sav_cursor;
+
+	fflush(stdout);                 /* buffered out to fast */
+}
+
+static int searchHistory(char *histPromptStr,int first,int last,int dir)
+{
+	unsigned char c = 0;
+	int	searchLen;
+	int	ret;
+	char *histPromptBuf;
+
+	if (n_history==0) {
 		beep();
+
 		return 0;
 	}
+
+	histPromptBuf = xmalloc(BUFSIZ+strlen(histPromptStr)+3);
+
+#if !defined(CONFIG_ASH)
+	lastWasTab = FALSE;
+#endif
+	searchLen=-1;
+
+	histPrompt(histPromptBuf,histPromptStr,0);
+
+	while(safe_read(0, &c, 1) >= 1) {
+
+		switch(c){
+		case 3:
+			/* Control-c -- stop gathering input */
+			*command_ps = 0;
+			len = 0;
+			free(histPromptBuf);
+			return 2;
+		case '\n':
+		case '\r':
+			/* Enter */
+			free(histPromptBuf);
+			return 1;
+		case ESC:
+			free(histPromptBuf);
+			return 0;
+		case '\b':
+		case DEL:
+			/* Control-h and DEL */
+			if (searchLen<=0) {
+				beep();
+				
+				break;
+			}
+			searchLen--;
+
+			histPrompt(histPromptBuf,histPromptStr,searchLen);
+
+			break;
+		case 18:
+			if (searchLen<=0) {
+				beep();
+					
+				break;
+			}
+			ret=find_str(command_ps+cursor,history[cur_history]+cursor+1,searchLen,-1);
+			if (ret>=0) {
+				cursor+=ret+1;
+				histPrompt(histPromptBuf,histPromptStr,searchLen);
+
+				break;
+			}
+			if ((cur_history==0)||(find_history(cur_history+dir,last,searchLen,-1,dir))<0) {
+				beep();
+				
+				break;
+			}
+			histPrompt(histPromptBuf,histPromptStr,searchLen);
+
+			break;
+		default:
+			if (!Isprint(c))        /* Skip non-printable characters */
+				break;
+
+			if (searchLen<0) {
+				cur_history=n_history;
+
+				store_history();
+
+				if (find_history(first,0,0,c,dir)<0) {
+					beep();
+
+					break;
+				}
+				searchLen=1;
+			}
+			else {
+				if (find_history(cur_history,0,searchLen,c,dir)<0) {
+					beep();
+
+					break;
+				}
+				searchLen++;
+			}
+			histPrompt(histPromptBuf,histPromptStr,searchLen);
+
+			break;
+		}
+	}
+	free(histPromptBuf);
+#if !defined(CONFIG_ASH)
+	printf("exit");
+	goto_new_line();
+	/* cmdedit_reset_term() called in atexit */
+	exit(EXIT_SUCCESS);
+#endif
+	return -1;
 }
 
 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
@@ -1197,12 +1388,6 @@
 
 #endif
 
-enum {
-	ESC = 27,
-	DEL = 127,
-};
-
-
 /*
  * This function is used to grab a character buffer
  * from the input file descriptor and allows you to
@@ -1257,6 +1442,7 @@
 	cmdedit_init();
 	/* Print out the command prompt */
 	parse_prompt(prompt);
+	put_prompt();
 
 	while (1) {
 
@@ -1283,7 +1469,6 @@
 			break;
 		case 3:
 			/* Control-c -- stop gathering input */
-			goto_new_line();
 			command[0] = 0;
 			len = 0;
 #if !defined(CONFIG_ASH)
@@ -1355,6 +1540,24 @@
 				beep();
 			}
 			break;
+		case 18:
+			/* Control-R -- Search reverse history buffer */
+			break_out=searchHistory("(reverse-i-search)`",n_history-1,0,-1);
+			parse_prompt(prompt);
+			redraw(cmdedit_y, len - cursor);
+			if (break_out==1)
+				goto_new_line();
+			fflush(stdout);
+			break;
+		case 19:
+			/* Control-S -- Search history buffer */
+			break_out=searchHistory("(i-search)`",0,n_history-1, 1);
+			parse_prompt(prompt);
+			redraw(cmdedit_y, len - cursor);
+			if (break_out==1)
+				goto_new_line();
+			fflush(stdout);
+			break;
 #endif
 		case 21:
 			/* Control-U -- Clear line before cursor */
@@ -1423,6 +1626,26 @@
 				/* End (Ctrl-E) */
 				input_end();
 				break;
+			case '5':
+				/* first history entry */
+				if (n_history==0) {
+					beep();
+
+					break;
+				}
+				store_history();
+				cur_history=0;
+				goto rewrite_line;
+			case '6':
+				/* last history entry */
+				if (n_history==0) {
+					beep();
+
+					break;
+				}
+				store_history();
+				cur_history=n_history-1;
+				goto rewrite_line;
 			default:
 				if (!(c >= '1' && c <= '9'))
 					c = 0;
@@ -1490,19 +1713,22 @@
 	free(history[MAX_HISTORY]);
 	history[MAX_HISTORY] = 0;
 	if (len) {                                      /* no put empty line */
-		int i = n_history;
-			/* 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];
-		}
-		history[i++] = bb_xstrdup(command);
-		cur_history = i;
-		n_history = i;
+			/* don't waist history with consecutive repeating identical commands */
+		if ((n_history==0)||(strcmp(history[n_history-1],command)!=0)){
+			int i = n_history;
+				/* 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];
+			}
+			history[i++] = bb_xstrdup(command);
+			cur_history = i;
+			n_history = i;
 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
-		num_ok_lines++;
+			num_ok_lines++;
 #endif
+		}
 	}
 #else  /* MAX_HISTORY < 1 */
 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
diff -ru busybox-1.00-pre5.orig/shell/cmdedit.h busybox-1.00-pre5/shell/cmdedit.h
--- busybox-1.00-pre5.orig/shell/cmdedit.h	2002-12-09 12:10:40.000000000 +0100
+++ busybox-1.00-pre5/shell/cmdedit.h	2004-01-12 11:59:03.000000000 +0100
@@ -1,5 +1,7 @@
 #ifndef CMDEDIT_H
 #define CMDEDIT_H
+	
+extern char *cmdedit_comp_path;
 
 int     cmdedit_read_input(char* promptStr, char* command);
 
diff -ru busybox-1.00-pre5.orig/shell/hush.c busybox-1.00-pre5/shell/hush.c
--- busybox-1.00-pre5.orig/shell/hush.c	2003-07-14 23:21:05.000000000 +0200
+++ busybox-1.00-pre5/shell/hush.c	2004-01-12 11:56:26.000000000 +0100
@@ -908,6 +908,9 @@
 	 ** atexit() handlers and other unwanted stuff to our
 	 ** child processes (rob at sysgo.de)
 	 */
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+	cmdedit_comp_path = getenv("PATH");
+#endif
 	cmdedit_read_input(prompt_str, the_command);
 #else
 	fputs(prompt_str, stdout);
diff -ru busybox-1.00-pre5.orig/shell/lash.c busybox-1.00-pre5/shell/lash.c
--- busybox-1.00-pre5.orig/shell/lash.c	2003-07-14 23:21:05.000000000 +0200
+++ busybox-1.00-pre5/shell/lash.c	2004-01-12 11:55:25.000000000 +0100
@@ -710,6 +710,9 @@
 		** atexit() handlers and other unwanted stuff to our
 		** child processes (rob at sysgo.de)
 		*/
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+		cmdedit_comp_path = getenv("PATH");
+#endif
 		cmdedit_read_input(prompt_str, command);
 		return 0;
 #else
diff -ru busybox-1.00-pre5.orig/shell/msh.c busybox-1.00-pre5/shell/msh.c
--- busybox-1.00-pre5.orig/shell/msh.c	2003-09-02 04:36:18.000000000 +0200
+++ busybox-1.00-pre5/shell/msh.c	2004-01-12 11:53:43.000000000 +0100
@@ -4550,6 +4550,9 @@
 	    static int position = 0, size = 0;
 
 	    while (size == 0 || position >= size) {
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+		cmdedit_comp_path = lookup("PATH");
+#endif
 		cmdedit_read_input(current_prompt, mycommand);
 		size = strlen(mycommand);
 		position = 0;





More information about the busybox mailing list