[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    PATCH: line_edit, ps, inotify features
From:       Flemming Madsen <busybox () themadsens ! dk>
Date:       2011-08-31 18:19:51
Message-ID: CACWu54X01YYnGPrW2Sg7gRAqKoqeaULw6DnVDcLWc0EYTv62BQ () mail ! gmail ! com
[Download RAW message or body]

Hello

Please consider the following additions to busybox, realized
in the attached.

They are currently supplied as one big patch against 1.19.0.
All tested with valgrind on X86 + test on ARM9

1. inotifyd.c: Implement a builtin 'echo' command for simply
   outputting events on stdout. This will allow programs&scripts
   to popen() 'inotifyd echo ...' for easy inotify
   functionality without excessive fork/exec overhead.

2. ash.c: A history builtin command showing current history

3. procps.c: An 'l' option to ps without enabling full desktop option
  FEATURE_PS_LONG: Adds fields PPID, RSS, START, TIME & TTY

4. lineedit.c ash.c: Consider aliases and ash builtins in command
   completion

5. lineedit.c ash.c: Help on lineedit keycodes.
   FEATURE_EDITING_HELP: "Help on line editing (in ash help command)"
   % help
   Built-in commands:
   ------------------
       . : [ [[ alias bg break cd chdir command continue echo eval exec
       exit export false fg getopts hash help history jobs kill let
       local printf pwd read readonly return set shift source test times
       trap true type ulimit umask unalias unset wait

   Line editing:
   -------------------
       C-A, <Home>    Move to start of line
       C-E, <End>     Move to end of line
       C-B, <Left>    Move back one char
       C-F, <Right>   Move forward one char
       C-D            Delete one char or exit
       <Del>          Delete one char to the right
       C-H            Delete one char to the left
       C-W            Delete one Word to the left
       C-U            Clear line before cursor
       C-K            Kill / Clear rest of line
       C-L            Clear screen
       C-R            Inremental reverse history search
       C-X            Redraw line
       C-P, <Up>      Get previous command line
       C-N, <Down>    Get next command line
       <Tab>          Tab completion
       M-Tab, S-Tab   Tab menu forward. <Tab> goes back again
       M-b, C-Left    Move word backward
       M-f, C-Right   Move word forward
       M-w, M-BkSpace Delete word backward
       M-d, M-Del     Delete word forward
       M-P, M-Up      Get previous line, matching prefix
       M-n, M-Down    Get next line, matching prefix
       M-., M-_       Insert last word of previous line

6. lineedit.c: Support for some of the the above keycodes
   FEATURE_EDITING_EXT: "Additional Meta key line editing commands"
   Enable M-b M-f M-w M-d M-w M-n M-p and M-. M-_ editing commands

   a) Completion menu for browsing completion matches with S-Tab/Tab
   b) Find commands matching prefix before cursor with M-p M-n
   c) Insert last word of previous (repeated) command with M-. or M-_
   d) Word/identifier deletion and movement aliases M-w M-b M-f
   e) Redraw line with C-X (And C-R if inremental search is disabled)


Sizes:
------

Pristine sources:
  text    data     bss     dec     hex filename
 500008    1876    8780  510664   7cac8 ../busybox-1.19.0/busybox

+626: Non config'able changes; inotifyd echo + history + completion extra
  text    data     bss     dec     hex filename
 500634    1876    8780  511290   7cd3a busybox

+628 Lineedit help added
  text    data     bss     dec     hex filename
 501262    1876    8780  511918   7cfae busybox

+757 Ps long option added
  text    data     bss     dec     hex filename
 502019    1876    8780  512675   7d2a3 busybox

+1765 Line edit additions added
  text    data     bss     dec     hex filename
 503784    1876    8780  514440   7d988 busybox


Enjoy

/Flemming

["flemming.patch" (text/x-patch)]

--- busybox-1.19.0/include/libbb.h.orig
+++ busybox-1.19.0/include/libbb.h
@@ -1352,34 +1352,42 @@
  * yet doesn't represent any valid Unicode character.
  * Also, -1 is reserved for error indication and we don't use it. */
 enum {
-	KEYCODE_UP       =  -2,
-	KEYCODE_DOWN     =  -3,
-	KEYCODE_RIGHT    =  -4,
-	KEYCODE_LEFT     =  -5,
-	KEYCODE_HOME     =  -6,
-	KEYCODE_END      =  -7,
-	KEYCODE_INSERT   =  -8,
-	KEYCODE_DELETE   =  -9,
-	KEYCODE_PAGEUP   = -10,
-	KEYCODE_PAGEDOWN = -11,
+	KEYCODE_UP        =  -2,
+	KEYCODE_DOWN      =  -3,
+	KEYCODE_RIGHT     =  -4,
+	KEYCODE_LEFT      =  -5,
+	KEYCODE_HOME      =  -6,
+	KEYCODE_END       =  -7,
+	KEYCODE_INSERT    =  -8,
+	KEYCODE_DELETE    =  -9,
+	KEYCODE_PAGEUP    = -10,
+	KEYCODE_PAGEDOWN  = -11,
+	KEYCODE_SHIFT_TAB = -12,
 
-	KEYCODE_CTRL_UP    = KEYCODE_UP    & ~0x40,
-	KEYCODE_CTRL_DOWN  = KEYCODE_DOWN  & ~0x40,
-	KEYCODE_CTRL_RIGHT = KEYCODE_RIGHT & ~0x40,
-	KEYCODE_CTRL_LEFT  = KEYCODE_LEFT  & ~0x40,
+    /* Using this bit reduces keycode count to 32 - Inline them above instead ? */
+	KEYCODE_ALT_UP     = KEYCODE_UP     & ~0x20,
+	KEYCODE_ALT_DOWN   = KEYCODE_DOWN   & ~0x20,
+	KEYCODE_ALT_RIGHT  = KEYCODE_RIGHT  & ~0x20,
+	KEYCODE_ALT_LEFT   = KEYCODE_LEFT   & ~0x20,
+	KEYCODE_ALT_DELETE = KEYCODE_DELETE & ~0x20,
+
+	KEYCODE_CTRL_UP    = KEYCODE_UP     & ~0x40,
+	KEYCODE_CTRL_DOWN  = KEYCODE_DOWN   & ~0x40,
+	KEYCODE_CTRL_RIGHT = KEYCODE_RIGHT  & ~0x40,
+	KEYCODE_CTRL_LEFT  = KEYCODE_LEFT   & ~0x40,
 #if 0
-	KEYCODE_FUN1     = -12,
-	KEYCODE_FUN2     = -13,
-	KEYCODE_FUN3     = -14,
-	KEYCODE_FUN4     = -15,
-	KEYCODE_FUN5     = -16,
-	KEYCODE_FUN6     = -17,
-	KEYCODE_FUN7     = -18,
-	KEYCODE_FUN8     = -19,
-	KEYCODE_FUN9     = -20,
-	KEYCODE_FUN10    = -21,
-	KEYCODE_FUN11    = -22,
-	KEYCODE_FUN12    = -23,
+	KEYCODE_FUN1     = -13,
+	KEYCODE_FUN2     = -14,
+	KEYCODE_FUN3     = -15,
+	KEYCODE_FUN4     = -16,
+	KEYCODE_FUN5     = -17,
+	KEYCODE_FUN6     = -18,
+	KEYCODE_FUN7     = -19,
+	KEYCODE_FUN8     = -20,
+	KEYCODE_FUN9     = -21,
+	KEYCODE_FUN10    = -22,
+	KEYCODE_FUN11    = -23,
+	KEYCODE_FUN12    = -24,
 #endif
 	KEYCODE_CURSOR_POS = -0x100, /* 0xfff..fff00 */
 	/* How long is the longest ESC sequence we know?
@@ -1416,9 +1424,12 @@
 typedef struct line_input_t {
 	int flags;
 	const char *path_lookup;
+	void (*extra_cmd)(void (*add)(char *), void*);
+	void *extra_cmd_arg;
 # if MAX_HISTORY
 	int cnt_history;
 	int cur_history;
+	int cur_cmdno;
 	int max_history; /* must never be <= 0 */
 #  if ENABLE_FEATURE_EDITING_SAVEHISTORY
 	unsigned cnt_history_in_file;
@@ -1446,6 +1457,7 @@
  * >0 length of input string, including terminating '\n'
  */
 int read_line_input(line_input_t *st, const char *prompt, char *command, int \
maxsize, int timeout) FAST_FUNC; +void line_input_help(void) FAST_FUNC;
 #else
 #define MAX_HISTORY 0
 int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC;
--- busybox-1.19.0/libbb/Config.src.orig
+++ busybox-1.19.0/libbb/Config.src
@@ -70,6 +70,14 @@
 	  You may want to decrease this parameter if your target machine
 	  benefits from smaller stack usage.
 
+config FEATURE_EDITING_HELP
+	bool "Help on line editing (in ash help command)"
+	default n
+	depends on FEATURE_EDITING
+	help
+	  Add a routine for outputting line edit help, and use it in
+          the ash help builtin command
+
 config FEATURE_EDITING_VI
 	bool "vi-style line editing commands"
 	default n
@@ -78,6 +86,15 @@
 	  Enable vi-style line editing. In shells, this mode can be
 	  turned on and off with "set -o vi" and "set +o vi".
 
+config FEATURE_EDITING_EXT
+	bool "Additional Meta key line editing commands"
+	default n
+	depends on FEATURE_EDITING
+	help
+          Extra editing commands. Use either Alt-Key or Esc-Key
+	  Enable M-b M-f M-w M-d M-w M-n M-p and M-. M-_ editing commands
+	  M-n and M-p are history search back and forth.
+
 config FEATURE_EDITING_HISTORY
 	int "History size"
 	# Don't allow way too big values here, code uses fixed "char *history[N]" struct \
                member
--- busybox-1.19.0/libbb/lineedit.c.orig
+++ busybox-1.19.0/libbb/lineedit.c
@@ -145,6 +145,11 @@
 #if ENABLE_FEATURE_TAB_COMPLETION
 	char **matches;
 	unsigned num_matches;
+#if ENABLE_FEATURE_EDITING_EXT
+	int cmpmenu_org_pfx;
+	int cmpmenu_curlen;
+	int cmpmenu_curpos;
+#endif
 #endif
 
 #if ENABLE_FEATURE_EDITING_VI
@@ -752,14 +757,22 @@
 	const char *pfind;
 	char *dirbuf = NULL;
 
+	void add_match_if(char *cmd) {
+		if (0 == strncmp(cmd, pfind, strlen(pfind)))
+			add_match(xstrdup(cmd));
+	}
+
 	npaths = 1;
 	path1[0] = (char*)".";
 
 	pfind = strrchr(command, '/');
 	if (!pfind) {
-		if (type == FIND_EXE_ONLY)
-			npaths = path_parse(&paths);
 		pfind = command;
+		if (type == FIND_EXE_ONLY) {
+			npaths = path_parse(&paths);
+			if (state->extra_cmd)
+				state->extra_cmd(add_match_if, state->extra_cmd_arg);
+		}
 	} else {
 		/* point to 'l' in "..../last_component" */
 		pfind++;
@@ -1065,19 +1078,57 @@
 	return s;
 }
 
+enum {
+	TS_LAST_NOT_TAB = 0,
+	TS_LAST_WAS_TAB,
+	TS_LIST_WAS_PRINTED,
+	TS_MENU_MODE,
+};
+
+
 /* Do TAB completion */
-static NOINLINE void input_tab(smallint *lastWasTab)
+static NOINLINE void input_tab(smallint *lastWasTab, smallint is_meta)
 {
 	char *chosen_match;
 	char *match_buf;
 	size_t len_found;
 	/* Length of string used for matching */
 	unsigned match_pfx_len = match_pfx_len;
+#if !ENABLE_FEATURE_EDITING_EXT
+#define cur_remlen 0
+#else
+#define cmpmenu_org_pfx  (S.cmpmenu_org_pfx)
+#define cmpmenu_curlen   (S.cmpmenu_curlen)
+#define cmpmenu_curpos   (S.cmpmenu_curpos)
+	int cur_remlen = 0;
+# endif
 	int find_type;
 # if ENABLE_UNICODE_SUPPORT
 	/* cursor pos in command converted to multibyte form */
 	int cursor_mb;
 # endif
+
+	void save_cmdline_prefix_in_matchbuf(void)
+	{
+		/* Make a local copy of the string up to the position of the cursor.
+		 * build_match_prefix will expand it into int16_t's, need to allocate
+		 * twice as much as the string_len+1.
+		 * (we then also (ab)use this extra space later - see (**))
+		 */
+		match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
+# if !ENABLE_UNICODE_SUPPORT
+		save_string(match_buf, cursor + 1); /* +1 for NUL */
+# else
+		{
+			CHAR_T wc = command_ps[cursor];
+			command_ps[cursor] = BB_NUL;
+			save_string(match_buf, MAX_LINELEN);
+			command_ps[cursor] = wc;
+			cursor_mb = strlen(match_buf);
+		}
+# endif
+	}
+
 	if (!(state->flags & TAB_COMPLETION))
 		return;
 
@@ -1085,36 +1136,65 @@
 		/* The last char was a TAB too.
 		 * Print a list of all the available choices.
 		 */
+#if ENABLE_FEATURE_EDITING_EXT
+		if (num_matches > 1 && ((is_meta && *lastWasTab > TS_LAST_WAS_TAB) ||
+								*lastWasTab > TS_LIST_WAS_PRINTED)) {
+			int wholeword = 1;
+			*lastWasTab = TS_MENU_MODE;
+			cur_remlen = cmpmenu_curlen;
+#if ENABLE_UNICODE_SUPPORT
+			save_cmdline_prefix_in_matchbuf(); /* make sure to set cursor_mb  */
+			cursor_mb -= cmpmenu_curlen;
+#else
+			cursor -= cmpmenu_curlen;
+# endif
+
+			if (is_meta && cmpmenu_curpos < num_matches) { /* Forward one match */
+				chosen_match = quote_special_chars(matches[++cmpmenu_curpos - 1]);
+			} else if (!is_meta && cmpmenu_curpos > 1) {  /* Backward one match */
+				chosen_match = quote_special_chars(matches[--cmpmenu_curpos - 1]);
+			} else {                           /* Back to start */
+				beep();
+				wholeword = 0;
+				cmpmenu_curpos = is_meta ? 0 : (num_matches + 1);
+				chosen_match = quote_special_chars(matches[0]);
+				chosen_match[cmpmenu_org_pfx] = 0;
+			}
+			match_pfx_len = 0;
+			len_found = strlen(chosen_match);
+			if (wholeword && chosen_match[len_found-1] != '/') {
+				chosen_match[len_found] = ' ';
+				chosen_match[++len_found] = '\0';
+			}
+			cmpmenu_curlen = len_found;
+#if 0
+			printf("\nLW:%d CR:%d CL:%d ORG:%d CRS:%d M'%s'\n",
+				   *lastWasTab, cur_remlen, cmpmenu_curlen,
+				   cmpmenu_org_pfx, cursor, chosen_match);
+#endif
+			goto do_redraw;
+		} else
+#endif
 		if (num_matches > 0) {
 			/* cursor will be changed by goto_new_line() */
 			int sav_cursor = cursor;
+			*lastWasTab = TS_LIST_WAS_PRINTED;
 			goto_new_line();
 			showfiles();
 			redraw(0, command_len - sav_cursor);
 		}
 		return;
 	}
+#if ENABLE_FEATURE_EDITING_EXT
+	cmpmenu_org_pfx = 0; /* reset */
+	cmpmenu_curlen = 0;
+	cmpmenu_curpos = 0;
+#endif
 
-	*lastWasTab = 1;
+	*lastWasTab = TS_LAST_WAS_TAB;
 	chosen_match = NULL;
 
-	/* Make a local copy of the string up to the position of the cursor.
-	 * build_match_prefix will expand it into int16_t's, need to allocate
-	 * twice as much as the string_len+1.
-	 * (we then also (ab)use this extra space later - see (**))
-	 */
-	match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
-# if !ENABLE_UNICODE_SUPPORT
-	save_string(match_buf, cursor + 1); /* +1 for NUL */
-# else
-	{
-		CHAR_T wc = command_ps[cursor];
-		command_ps[cursor] = BB_NUL;
-		save_string(match_buf, MAX_LINELEN);
-		command_ps[cursor] = wc;
-		cursor_mb = strlen(match_buf);
-	}
-# endif
+	save_cmdline_prefix_in_matchbuf();
 	find_type = build_match_prefix(match_buf);
 
 	/* Free up any memory already allocated */
@@ -1197,6 +1277,11 @@
 		}
 	}
 
+#if ENABLE_FEATURE_EDITING_EXT
+	cmpmenu_org_pfx = match_pfx_len;
+	cmpmenu_curlen = match_pfx_len;
+#endif
+do_redraw:
 # if !ENABLE_UNICODE_SUPPORT
 	/* Have space to place the match? */
 	/* The result consists of three parts with these lengths: */
@@ -1205,7 +1290,7 @@
 	if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
 		int pos;
 		/* save tail */
-		strcpy(match_buf, &command_ps[cursor]);
+		strcpy(match_buf, &command_ps[cursor + cur_remlen]);
 		/* add match and tail */
 		sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
 		command_len = strlen(command_ps);
@@ -1224,7 +1309,7 @@
 		if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
 			int pos;
 			/* save tail */
-			strcpy(match_buf, &command[cursor_mb]);
+			strcpy(match_buf, &command[cursor_mb + cur_remlen]);
 			/* where do we want to have cursor after all? */
 			strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
 			len = load_string(command);
@@ -1943,6 +2028,36 @@
 # define isrtl_str() 0
 #endif
 
+static void insert_text(CHAR_T *c, int n)
+{
+	while (n--) {
+		CHAR_T ic = *c++;
+
+		command_len++;
+		if (cursor == (command_len - 1)) {
+			/* We are at the end, append */
+			command_ps[cursor] = ic;
+			command_ps[cursor + 1] = BB_NUL;
+			put_cur_glyph_and_inc_cursor();
+			if (unicode_bidi_isrtl(ic))
+				input_backward(1);
+		} else {
+			/* In the middle, insert */
+			int sc = cursor;
+
+			memmove(command_ps + sc + 1, command_ps + sc,
+				(command_len - sc) * sizeof(command_ps[0]));
+			command_ps[sc] = ic;
+			/* is right-to-left char, or neutral one (e.g. comma) was just added to rtl text? \
*/ +			if (!isrtl_str())
+				sc++; /* no */
+			put_till_end_and_adv_cursor();
+			/* to prev x pos + 1 */
+			input_backward(cursor - sc);
+		}
+	}
+}
+
 /* leave out the "vi-mode"-only case labels if vi editing isn't
  * configured. */
 #define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
@@ -1950,6 +2065,7 @@
 /* convert uppercase ascii to equivalent control char, for readability */
 #undef CTRL
 #define CTRL(a) ((a) & ~0x40)
+#define META(a) ((a) | 0x80)
 
 enum {
 	VI_CMDMODE_BIT = 0x40000000,
@@ -2101,6 +2217,10 @@
 #if ENABLE_FEATURE_EDITING_VI
 	smallint vi_cmdmode = 0;
 #endif
+#if ENABLE_FEATURE_EDITING_EXT && MAX_HISTORY > 0
+	int search_prefix = 0;
+	int yank_end = 0;
+#endif
 	struct termios initial_settings;
 	struct termios new_settings;
 	char read_key_buffer[KEYCODE_BUFFER_SIZE];
@@ -2133,8 +2253,10 @@
 #if MAX_HISTORY > 0
 # if ENABLE_FEATURE_EDITING_SAVEHISTORY
 	if ((state->flags & SAVE_HISTORY) && state->hist_file)
-		if (state->cnt_history == 0)
+		if (state->cnt_history == 0) {
 			load_history(state);
+			state->cur_cmdno = state->cnt_history;
+		}
 # endif
 	if (state->flags & DO_HISTORY)
 		state->cur_history = state->cnt_history;
@@ -2261,7 +2383,7 @@
 			break;
 #if ENABLE_FEATURE_TAB_COMPLETION
 		case '\t':
-			input_tab(&lastWasTab);
+			input_tab(&lastWasTab, FALSE);
 			break;
 #endif
 		case CTRL('K'):
@@ -2274,6 +2396,10 @@
 		vi_case(CTRL('L')|VI_CMDMODE_BIT:)
 			/* Control-l -- clear screen */
 			printf(ESC"[H"); /* cursor to top,left */
+#if !ENABLE_FEATURE_REVERSE_SEARCH
+		case CTRL('R'):
+#endif
+		case CTRL('X'): /* Redraw line */
 			redraw(0, command_len - cursor);
 			break;
 #if MAX_HISTORY > 0
@@ -2446,14 +2572,186 @@
 				bb_putchar('\b');
 			}
 			break;
+#endif /* FEATURE_COMMAND_EDITING_VI */
+#if ENABLE_FEATURE_EDITING_EXT 
+		case KEYCODE_SHIFT_TAB:
+		case KEYCODE_ALT_UP:
+		case KEYCODE_ALT_DOWN:
+		case KEYCODE_ALT_LEFT:
+		case KEYCODE_ALT_RIGHT:
+		case KEYCODE_ALT_DELETE:
+			// Continue into ESC handling
+#endif
 		case '\x1b': /* ESC */
+#if ENABLE_FEATURE_EDITING_VI
 			if (state->flags & VI_MODE) {
 				/* insert mode --> command mode */
-				vi_cmdmode = 1;
-				input_backward(1);
+				if (ic == '\x1b') {
+					vi_cmdmode = 1;
+					input_backward(1);
+				}
+				break;
+			}
+#endif
+#if ENABLE_FEATURE_EDITING_EXT 
+			{
+				int i2 = ic == '\x1b' ? read_key(STDIN_FILENO, read_key_buffer, timeout) : ic;
+				ic_raw = ic;
+
+				switch (i2) {
+				 case KEYCODE_ALT_LEFT:
+				 case KEYCODE_LEFT:
+				 case 'b':
+				   ctrl_left();
+				   break;
+				 case KEYCODE_ALT_RIGHT:
+				 case KEYCODE_RIGHT:
+				 case 'f':
+				   ctrl_right();
+				   break;
+				 case KEYCODE_ALT_DELETE:
+				 case 'd': {
+				   /* M-d Delete word forward */
+				   int nc, sc = cursor;
+				   ctrl_right();
+				   nc = cursor;
+				   input_backward(cursor - sc);
+				   while (nc >= 0 && nc-- >= cursor)
+					input_delete(1);
+				   break;
+				 }
+				 case '\b':   /* ^H */
+				 case '\x7f': /* DEL */
+				 case 'w': {
+				   /* M-w Delete word backward */
+				   int sc = cursor;
+				   ctrl_left();
+				   while (sc-- > cursor)
+					 input_delete(1);
+				   break;
+				 }
+	 
+#if ENABLE_FEATURE_TAB_COMPLETION
+				case KEYCODE_SHIFT_TAB:
+				case '\t':
+					input_tab(&lastWasTab, TRUE);
+					break;
+#endif
+#if MAX_HISTORY > 0
+				case KEYCODE_ALT_UP:
+				case KEYCODE_UP:
+				case 'p':
+				case KEYCODE_ALT_DOWN:
+				case KEYCODE_DOWN:
+				case 'n':
+				{
+					int is_up = 0;
+					switch (ic) {
+						case KEYCODE_ALT_UP:
+						case KEYCODE_UP:
+						case 'p':
+							is_up = 1;
+					}
+					/* Meta-n -- Search next command from history */
+					search_prefix = cursor;
+					if (state->flags & DO_HISTORY) {
+						int ch = state->cur_history + (is_up ? -1 : 1);
+# if ENABLE_UNICODE_SUPPORT
+						char cmdpfx[MAX_LINELEN];
+						CHAR_T sc = command_ps[search_prefix];
+						int pfxlen;
+						command_ps[search_prefix] = BB_NUL;
+						pfxlen = save_string(cmdpfx, sizeof(cmdpfx));
+						command_ps[search_prefix] = sc;
+#else
+						int pfxlen = search_prefix;
+						char *cmdpfx = command_ps;
+#endif
+						while ((!is_up && ch < state->cnt_history) ||
+							   (is_up && ch >= 0)) {
+							if (state->history[ch] && 0 == strncmp(cmdpfx, state->history[ch], pfxlen)) {
+								state->cur_history = ch;
+								goto rewrite_line;
+							}
+							ch += is_up ? -1 : 1;
+						}
+					}
+					if (!is_up && cursor < command_len) {
+						command_ps[cursor] = BB_NUL;
+						command_len = cursor;
+						redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
+						state->cur_history = state->cnt_history;
+					}
+					beep();
+					break;
+				}
+				case '.':
+				case '_':
+					// Insert last word from previous command. More previous on repeat
+					if (yank_end == 0) {
+						yank_end = state->cnt_history;
+						search_prefix = cursor;
+					}
+					if ((state->flags & DO_HISTORY) && yank_end > 0) {
+						char *cmd;
+						int  ix;
+						int cursor_middle;
+						smallint in_sq = 0, in_dq = 0;
+
+						while (cursor > search_prefix)
+							input_backspace();
+
+						cmd = state->history[--yank_end];
+						if (!cmd)
+							break;
+
+						/* Find last shell token in line (approximately) */
+						for (ix = strlen(cmd) - 1; ix > 0; ix--) {
+							if (cmd[ix] == '"' || cmd[ix] == '`') {
+								if (in_dq == cmd[ix] && (ix == 0 || cmd[ix-1] != '\\'))
+									in_dq = 0;
+								else if (!in_sq && !in_dq)
+									in_dq = cmd[ix];
+							}
+							else if (cmd[ix] == '\'' && !in_dq)
+								in_sq = in_sq ? 0 : 1;
+							if (!in_dq && !in_sq &&
+								strchr(" \t<>;&|", cmd[ix-1]) && (ix < 2 || cmd[ix-2] != '\\'))
+							 break;
+						}
+						if ((int)command_len > maxsize - strlen(cmd + ix) - 2)
+							break;
+//  printf("\nYE:%d:%d SP:%d IX:%d'%s'\n", yank_end, state->cnt_history, \
search_prefix, ix, cmd + ix); +
+#if ENABLE_UNICODE_SUPPORT
+						{
+							int siz = strlen(cmd + ix);
+							CHAR_T *word = xzalloc((siz + 1) * sizeof(command_ps[0]));
+							ssize_t wlen = mbstowcs(word, cmd + ix, (siz + 1) * sizeof(command_ps[0]));
+							cursor_middle = cursor + wlen;
+							if (cursor == command_len - 1) {
+								CHAR_T spc = ' ';
+								insert_text(&spc, 1);
+								cursor_middle = 0;
+							}
+							insert_text(word, wlen);
+							free(word);
+						}
+#else
+						cursor_middle = cursor + strlen(cmd + ix);
+						if (cursor == command_len - 1) {
+							insert_text(" ", 1);
+							cursor_middle = 0;
+						}
+						insert_text(cmd + ix, strlen(cmd + ix));
+#endif
+					}
+					break;
+#endif
+				}
 			}
+#endif
 			break;
-#endif /* FEATURE_COMMAND_EDITING_VI */
 
 #if MAX_HISTORY > 0
 		case KEYCODE_UP:
@@ -2470,6 +2768,11 @@
 			command_len = load_string(state->history[state->cur_history] ?
 					state->history[state->cur_history] : "");
 			/* redraw and go to eol (bol, in vi) */
+#if ENABLE_FEATURE_EDITING_EXT 
+			if (search_prefix > 0)
+				redraw(cmdedit_y, command_len - search_prefix);
+			else
+#endif
 			redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
 			break;
 #endif
@@ -2544,27 +2847,13 @@
 				break;
 			}
 
-			command_len++;
-			if (cursor == (command_len - 1)) {
-				/* We are at the end, append */
-				command_ps[cursor] = ic;
-				command_ps[cursor + 1] = BB_NUL;
-				put_cur_glyph_and_inc_cursor();
-				if (unicode_bidi_isrtl(ic))
-					input_backward(1);
-			} else {
-				/* In the middle, insert */
-				int sc = cursor;
-
-				memmove(command_ps + sc + 1, command_ps + sc,
-					(command_len - sc) * sizeof(command_ps[0]));
-				command_ps[sc] = ic;
-				/* is right-to-left char, or neutral one (e.g. comma) was just added to rtl \
                text? */
-				if (!isrtl_str())
-					sc++; /* no */
-				put_till_end_and_adv_cursor();
-				/* to prev x pos + 1 */
-				input_backward(cursor - sc);
+#if ENABLE_FEATURE_EDITING_EXT
+			search_prefix = 0;
+			yank_end = 0;
+#endif
+			{
+				CHAR_T ics[1] = { ic };
+				insert_text(ics, 1);
 			}
 			break;
 		} /* switch (ic) */
@@ -2573,7 +2862,7 @@
 			break;
 
 #if ENABLE_FEATURE_TAB_COMPLETION
-		if (ic_raw != '\t')
+		if (ic_raw != '\t' && ic_raw != KEYCODE_SHIFT_TAB)
 			lastWasTab = 0;
 #endif
 	} /* while (1) */
@@ -2603,8 +2892,10 @@
 	free(command_ps);
 #endif
 
-	if (command_len > 0)
+	if (command_len > 0) {
 		remember_in_history(command);
+		state->cur_cmdno++;
+	}
 
 	if (break_out > 0) {
 		command[command_len++] = '\n';
@@ -2627,6 +2918,57 @@
 	return len; /* can't return command_len, DEINIT_S() destroys it */
 }
 
+void FAST_FUNC line_input_help(void)
+{
+#if ENABLE_FEATURE_EDITING_HELP
+	printf("Line editing:\n-------------------\n%s\n", 
+			"\t" "C-A, <Home>    Move to start of line\n"
+			"\t" "C-E, <End>     Move to end of line\n"
+			"\t" "C-B, <Left>    Move back one char\n"
+			"\t" "C-F, <Right>   Move forward one char\n"
+#if !ENABLE_FEATURE_EDITING_EXT
+			"\t" "C-Left         Move word backward\n"
+			"\t" "C-Right        Move word forward\n"
+#endif
+			"\t" "C-D            Delete one char or exit\n"
+			"\t" "<Del>          Delete one char to the right\n"
+			"\t" "C-H            Delete one char to the left\n"
+			"\t" "C-W            Delete one Word to the left\n"
+			"\t" "C-U            Clear line before cursor\n"
+			"\t" "C-K            Kill / Clear rest of line\n"
+			"\t" "C-L            Clear screen\n"
+#if ENABLE_FEATURE_REVERSE_SEARCH && MAX_HISTORY > 0
+			"\t" "C-R            Inremental reverse history search\n"
+			"\t" "C-X            Redraw line\n"
+#else
+			"\t" "C-R C-X        Redraw line\n"
+#endif
+#if MAX_HISTORY > 0
+			"\t" "C-P, <Up>      Get previous command line\n"
+			"\t" "C-N, <Down>    Get next command line\n"
+#endif
+#if ENABLE_FEATURE_TAB_COMPLETION
+			"\t" "<Tab>          Tab completion\n"
+#endif
+#if ENABLE_FEATURE_EDITING_EXT
+#if ENABLE_FEATURE_TAB_COMPLETION
+			"\t" "M-Tab, S-Tab   Tab menu forward. <Tab> goes back again\n"
+#endif
+			"\t" "M-b, C-Left    Move word backward\n"
+			"\t" "M-f, C-Right   Move word forward\n"
+			"\t" "M-w, M-BkSpace Delete word backward\n"
+			"\t" "M-d, M-Delete  Delete word forward\n"
+#if MAX_HISTORY > 0
+			"\t" "M-P, M-Up      Get previous line, matching prefix\n"
+			"\t" "M-N, M-Down    Get next line, matching prefix\n"
+			"\t" "M-., M-_       Insert last word of previous line\n"
+#endif
+#endif
+
+			);
+#endif
+}
+
 #else  /* !FEATURE_EDITING */
 
 #undef read_line_input
--- busybox-1.19.0/libbb/read_key.c.orig
+++ busybox-1.19.0/libbb/read_key.c
@@ -47,6 +47,7 @@
 		'[','H'        |0x80,KEYCODE_HOME    , /* xterm */
 		/* [ESC] ESC [ [2] H - [Alt-][Shift-]Home */
 		'[','F'        |0x80,KEYCODE_END     , /* xterm */
+		'[','Z'        |0x80,KEYCODE_SHIFT_TAB,
 		'[','1','~'    |0x80,KEYCODE_HOME    , /* vt100? linux vt? or what? */
 		'[','2','~'    |0x80,KEYCODE_INSERT  ,
 		/* ESC [ 2 ; 3 ~ - Alt-Insert */
@@ -86,6 +87,11 @@
 		/* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused */
 		'[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT,
 		'[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT ,
+		'[','1',';','3','A' |0x80,KEYCODE_ALT_UP,
+		'[','1',';','3','B' |0x80,KEYCODE_ALT_DOWN ,
+		'[','1',';','3','C' |0x80,KEYCODE_ALT_RIGHT,
+		'[','1',';','3','D' |0x80,KEYCODE_ALT_LEFT ,
+		'[','3',';','3','~' |0x80,KEYCODE_ALT_DELETE ,
 		0
 		/* ESC [ Z - Shift-Tab */
 	};
--- busybox-1.19.0/miscutils/inotifyd.c.orig
+++ busybox-1.19.0/miscutils/inotifyd.c
@@ -101,6 +101,7 @@
 	args[0] = *argv;
 	args[4] = NULL;
 	argc -= 2; // number of files we watch
+    int echo = strcmp(*argv, "echo") == 0;
 
 	// open inotify
 	pfd.fd = inotify_init();
@@ -177,12 +178,17 @@
 						*s++ = mask_names[i];
 				}
 				*s = '\0';
-//				bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
-//					ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
-				args[1] = events;
-				args[2] = watches[ie->wd];
-				args[3] = ie->len ? ie->name : NULL;
-				spawn_and_wait((char **)args);
+				if (echo) {
+					printf("%s\t%s\t%s\n", events, watches[ie->wd], ie->len ? ie->name : "");
+				}
+				else {
+//   				bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
+//   					ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
+					args[1] = events;
+					args[2] = watches[ie->wd];
+					args[3] = ie->len ? ie->name : NULL;
+					spawn_and_wait((char **)args);
+				}
 				// we are done if all files got final x event
 				if (ie->mask & 0x8000) {
 					if (--argc <= 0)
--- busybox-1.19.0/procps/Config.src.orig
+++ busybox-1.19.0/procps/Config.src
@@ -96,6 +96,14 @@
 	  If given once, 132 chars are printed, and if given more
 	  than once, the length is unlimited.
 
+config FEATURE_PS_LONG
+	bool "Enable long output option (-l)"
+	default y
+	depends on PS
+	help
+	  Support argument 'l' for long output.
+	  Adds fields PPID, RSS, START, TIME & TTY
+
 config FEATURE_PS_TIME
 	bool "Enable time and elapsed time output"
 	default y
--- busybox-1.19.0/procps/ps.c.orig
+++ busybox-1.19.0/procps/ps.c
@@ -39,6 +39,12 @@
 //usage:	IF_FEATURE_PS_WIDE(
 //usage:     "\n	w	Wide output"
 //usage:	)
+//usage:	IF_FEATURE_PS_LONG(
+//usage:     "\n	l	Long output"
+//usage:	)
+//usage:	IF_FEATURE_SHOW_THREADS(
+//usage:     "\n	T	Show threads"
+//usage:	)
 //usage:
 //usage:#endif /* ENABLE_DESKTOP */
 //usage:
@@ -56,15 +62,15 @@
 //usage:       " 2990 andersen andersen R ps\n"
 
 #include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
 
 /* Absolute maximum on output line length */
 enum { MAX_WIDTH = 2*1024 };
 
 #if ENABLE_DESKTOP
 
-#ifdef __linux__
-# include <sys/sysinfo.h>
-#endif
 #include <sys/times.h> /* for times() */
 #ifndef AT_CLKTCK
 # define AT_CLKTCK 17
@@ -625,15 +631,17 @@
 	enum {
 		OPT_Z = (1 << 0) * ENABLE_SELINUX,
 		OPT_T = (1 << ENABLE_SELINUX) * ENABLE_FEATURE_SHOW_THREADS,
+		OPT_l = (1 << ENABLE_SELINUX) * (1 << ENABLE_FEATURE_SHOW_THREADS) * \
ENABLE_FEATURE_PS_LONG,  };
 	int opts = 0;
 	/* If we support any options, parse argv */
-#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE
+#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE || \
ENABLE_FEATURE_PS_LONG  # if ENABLE_FEATURE_PS_WIDE
 	/* -w is a bit complicated */
 	int w_count = 0;
 	opt_complementary = "-:ww";
-	opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")"w", &w_count);
+	opts = getopt32(argv, \
IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l") +                  \
"w", &w_count);  /* if w is given once, GNU ps sets the width to 132,
 	 * if w is given more than once, it is "unlimited"
 	 */
@@ -648,60 +656,95 @@
 # else
 	/* -w is not supported, only -Z and/or -T */
 	opt_complementary = "-";
-	opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T"));
+	opts = getopt32(argv, \
IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l"));  # endif
-#endif
 
 #if ENABLE_SELINUX
-	if ((opts & OPT_Z) && is_selinux_enabled()) {
-		psscan_flags = PSSCAN_PID | PSSCAN_CONTEXT
-				| PSSCAN_STATE | PSSCAN_COMM;
-		puts("  PID CONTEXT                          STAT COMMAND");
-	} else
-#endif
-	{
-		puts("  PID USER       VSZ STAT COMMAND");
-	}
-	if (opts & OPT_T) {
-		psscan_flags |= PSSCAN_TASKS;
-	}
-
-	p = NULL;
-	while ((p = procps_scan(p, psscan_flags)) != NULL) {
-		int len;
+        if ((opts & OPT_Z) && is_selinux_enabled()) {
+            psscan_flags = PSSCAN_PID | PSSCAN_CONTEXT
+                    | PSSCAN_STATE | PSSCAN_COMM;
+            puts("  PID CONTEXT                          STAT COMMAND");
+        } else
+#endif
+        {
+            if (opts & OPT_T) {
+                psscan_flags |= PSSCAN_TASKS;
+            }
+            if (opts & OPT_l) {
+                psscan_flags |= PSSCAN_PPID | PSSCAN_RSS | PSSCAN_STIME | \
PSSCAN_UTIME | PSSCAN_TTY; +                puts("  PID  PPID USER   VSZ   RSS STAT \
START     TIME   TTY COMMAND"); +            }
+            else {
+                puts("  PID USER       VSZ STAT COMMAND");
+            }
+        }
+#endif
+
+        p = NULL;
+        while ((p = procps_scan(p, psscan_flags)) != NULL) {
+            int len;
 #if ENABLE_SELINUX
-		if (psscan_flags & PSSCAN_CONTEXT) {
-			len = printf("%5u %-32.32s %s  ",
-					p->pid,
-					p->context ? p->context : "unknown",
-					p->state);
-		} else
-#endif
-		{
-			const char *user = get_cached_username(p->uid);
-			//if (p->vsz == 0)
-			//	len = printf("%5u %-8.8s        %s ",
-			//		p->pid, user, p->state);
-			//else
-			{
-				char buf6[6];
-				smart_ulltoa5(p->vsz, buf6, " mgtpezy");
-				buf6[5] = '\0';
-				len = printf("%5u %-8.8s %s %s  ",
-					p->pid, user, buf6, p->state);
-			}
-		}
-
-		{
-			int sz = terminal_width - len;
-			char buf[sz + 1];
-			read_cmdline(buf, sz, p->pid, p->comm);
-			puts(buf);
-		}
-	}
-	if (ENABLE_FEATURE_CLEAN_UP)
-		clear_username_cache();
-	return EXIT_SUCCESS;
-}
+            if (psscan_flags & PSSCAN_CONTEXT) {
+                len = printf("%5u %-32.32s %s  ",
+                        p->pid,
+                        p->context ? p->context : "unknown",
+                        p->state);
+            } else
+#endif
+            {
+                const char *user = get_cached_username(p->uid);
+                //if (p->vsz == 0)
+                //	len = printf("%5u %-8.8s        %s ",
+                //		p->pid, user, p->state);
+                //else
+                {
+                    char buf6[6];
+                    smart_ulltoa5(p->vsz, buf6, " mgtpezy");
+                    buf6[5] = '\0';
+#if ENABLE_FEATURE_PS_LONG
+                    if (opts & OPT_l) {
+                        char bufr[6], strt[6], tty[20];
+                        struct tm *tm;
+                        int sut = (p->stime + p->utime) / 100;
+                        time_t now = time(NULL);
+                        struct sysinfo info;
+                        int elap;
+
+                        sysinfo(&info);
+                        elap = info.uptime - (p->start_time / 100);
+                        now -= elap;
+                        tm = localtime((time_t *) &now);
+
+                        smart_ulltoa5(p->vsz, buf6, "kmgtpezy");
+                        smart_ulltoa5(p->rss, bufr, "kmgtpezy");
+                        bufr[5] = '\0';
+                        strcpy(tty, p->tty_major == 136 ? "pts" :
+                                    (p->tty_major == 4 || p->tty_major == 5) ? "tty" \
: "- "); +                        strcat(tty, p->tty_minor >= 64 ? "S" : "");
+                        if (tty[2])
+                            strcat(tty, itoa(p->tty_minor % 64));
+                        strftime(strt, 6, (elap >= (24 * 3600)) ? "%b%d" : "%H:%M", \
tm); +                        strt[5] = '\0';
+                        len = printf("%5u %5u %-4.4s %5s %5s %s  %s %2d:%02d:%02d \
%5s ", +                                     p->pid, p->ppid, user, buf6, bufr, \
p->state, strt,  +                                     sut / 3600, (sut % 3600) / 60, \
sut % 60, tty); +                    } else
+#endif
+                        len = printf("%5u %-8.8s %s %s  ",
+                            p->pid, user, buf6, p->state);
+                }
+            }
+
+            {
+                int sz = terminal_width - len;
+                char buf[sz + 1];
+                read_cmdline(buf, sz, p->pid, p->comm);
+                puts(buf);
+            }
+        }
+        if (ENABLE_FEATURE_CLEAN_UP)
+            clear_username_cache();
+        return EXIT_SUCCESS;
+    }
 
 #endif /* !ENABLE_DESKTOP */
--- busybox-1.19.0/shell/ash.c.orig
+++ busybox-1.19.0/shell/ash.c
@@ -8765,8 +8765,11 @@
 		if (!did_banner) {
 			/* note: ash and hush share this string */
 			out1fmt("\n\n%s %s\n"
-				"Enter 'help' for a list of built-in commands."
-				"\n\n",
+				"Enter 'help' for a list of built-in commands"
+#if ENABLE_FEATURE_EDITING_HELP
+				" and command line editing keys"
+#endif
+				".\n\n",
 				bb_banner,
 				"built-in shell (ash)"
 			);
@@ -9019,6 +9022,9 @@
 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
 static int helpcmd(int, char **) FAST_FUNC;
 #endif
+#if MAX_HISTORY
+static int historycmd(int, char **) FAST_FUNC;
+#endif 
 #if ENABLE_SH_MATH_SUPPORT
 static int letcmd(int, char **) FAST_FUNC;
 #endif
@@ -9092,6 +9098,9 @@
 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
 	{ BUILTIN_NOSPEC        "help"    , helpcmd    },
 #endif
+#if MAX_HISTORY
+	{ BUILTIN_NOSPEC        "history", historycmd },
+#endif 
 #if JOBS
 	{ BUILTIN_REGULAR       "jobs"    , jobscmd    },
 	{ BUILTIN_REGULAR       "kill"    , killcmd    },
@@ -9163,6 +9172,27 @@
 	return bp;
 }
 
+# if ENABLE_FEATURE_TAB_COMPLETION
+/*
+ * Add these as completion candidates 
+ */
+static void add_aliases_and_builtins(void (*add)(char *), void* arg UNUSED_PARAM)
+{
+	int i;
+	struct alias *ap;
+
+	for (i = 0; i < ATABSIZE; i++) {
+		for (ap = atab[i]; ap; ap = ap->next) {
+			add(ap->name);
+		}
+	}
+	for (i = 0; i < ARRAY_SIZE(builtintab); i++) {
+		add(builtintab[i].name + 1);
+	}
+}
+#endif
+
+
 /*
  * Execute a simple command.
  */
@@ -9632,6 +9662,7 @@
 # endif
 # if ENABLE_FEATURE_TAB_COMPLETION
 		line_input_state->path_lookup = pathval();
+		line_input_state->extra_cmd = add_aliases_and_builtins;
 # endif
 		/* Unicode support should be activated even if LANG is set
 		 * _during_ shell execution, not only if it was set when
@@ -12596,10 +12627,34 @@
 	}
 #endif
 	out1fmt("\n\n");
+
+#if ENABLE_FEATURE_EDITING_HELP
+	line_input_help();
+#endif
 	return EXIT_SUCCESS;
 }
 #endif /* FEATURE_SH_EXTRA_QUIET */
 
+#if MAX_HISTORY
+/* 
+ * Lists command history
+ */
+static int FAST_FUNC
+historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	int i;
+	int n;
+
+	if (!line_input_state)
+		return EXIT_FAILURE;
+
+	n = line_input_state->cur_cmdno - line_input_state->cnt_history;
+	for (i = 0; i < line_input_state->cnt_history; i++, n++)
+		out1fmt("%4d %s\n", n, line_input_state->history[i]);
+	return EXIT_SUCCESS;
+}
+#endif 
+
 /*
  * The export and readonly commands.
  */



_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic