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

List:       busybox
Subject:    [PATCH] Readline's mimic for reverse history search
From:       kyak <bas () bmail ! ru>
Date:       2011-06-30 6:35:51
Message-ID: alpine.LMD.2.00.1106301035360.12089 () bas
[Download RAW message or body]

Implemented readline's mimic for reverse history search (Ctrl+r)

Signed-off-by: kyak <bas@bmail.ru>
---
  libbb/Config.src |    7 ++
  libbb/lineedit.c |  193 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2 files changed, 200 insertions(+), 0 deletions(-)

diff --git a/libbb/Config.src b/libbb/Config.src
index 0ea8f43..3b1487d 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -94,6 +94,13 @@ config FEATURE_EDITING_SAVEHISTORY
  	help
  	  Enable history saving in shells.

+config FEATURE_REVERSE_SEARCH
+	bool "Reverse history search"
+	default n
+	depends on FEATURE_EDITING_SAVEHISTORY
+	help
+	  Enable readline-alike Ctrl+r combination for reverse history search
+
  config FEATURE_TAB_COMPLETION
  	bool "Tab completion"
  	default y
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 4e3bc0e..c9c6728 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -1948,6 +1948,194 @@ static int isrtl_str(void)
  #undef CTRL
  #define CTRL(a) ((a) & ~0x40)

+#if ENABLE_FEATURE_REVERSE_SEARCH
+/* mimic readline's Ctrl+r behaviour for reverse history search */
+static smallint reverse_i_search(void)
+{
+	/* prepare the prompt */
+	const char prmt[] = "(reverse-i-search)`";
+	int prmt_len = strlen(prmt);
+	char *prmt_mem_ptr = xzalloc(1);
+	/* save the real prompt */
+	char *prmt_mem_ptr_save = xzalloc(1);
+	/* user input is collected here */
+	char *match_buf;
+	/* matched lines from history are here */
+	char *cmdline_buf;
+	char read_key_buffer[KEYCODE_BUFFER_SIZE];
+	int ic;
+	char buf[MB_CUR_MAX + 1];
+	wchar_t wc[MAX_LINELEN];
+	ssize_t len, len_cmd;
+	mbstate_t mbst = { 0 };
+	smallint break_out = 0;
+	smallint break_out_search = 0;
+	smallint has_match = 0;
+	int i;
+	int cur = state->cur_history, cur_match = state->cur_history;
+	int cmdedit_y_add = 0, cmdedit_y_add_prev = 0, cmdedit_y_add_cmp = 0;
+
+	read_key_buffer[0] = 0;
+	prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), prmt);
+	/* save prompt */
+	prmt_mem_ptr_save = strcat(xrealloc(prmt_mem_ptr_save, cmdedit_prmt_len+1), cmdedit_prompt);
+	/* overwrite the prompt */
+	cmdedit_prompt = prmt_mem_ptr;
+	cmdedit_prmt_len = prmt_len;
+	/* save what's already typed */
+	match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
+	cmdline_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
+	save_string(match_buf, MAX_LINELEN);
+	command_len = load_string("", MAX_LINELEN);
+	redraw(cmdedit_y, 0);
+	fputs(match_buf, stdout);
+	fputs("': ", stdout);
+	fputs(match_buf, stdout);
+	cmdedit_y_add_cmp = (prmt_len+1 + 2 * strlen(match_buf)) / (cmdedit_termw);
+	cmdedit_y_add_prev = cmdedit_y_add_cmp;
+	/* switch to char-consumption mode... */
+	while (1) {
+		fflush_all();
+		ic = lineedit_read_key(read_key_buffer, -1);
+		switch (ic) {
+			case KEYCODE_RIGHT: /* left-right keys act differently from Enter */
+			case KEYCODE_LEFT:
+				if (has_match == 1) {
+					command_len = load_string(cmdline_buf, MAX_LINELEN);
+				} else {
+					command_len = load_string(match_buf, MAX_LINELEN);
+				}
+				cmdedit_prompt = prmt_mem_ptr_save;
+				cmdedit_prmt_len = strlen(cmdedit_prompt);
+				redraw(cmdedit_y + cmdedit_y_add, 0);
+				break_out = 0;
+				break;
+			case CTRL('R'): /* searching for the next match */
+				break_out = 2;
+				break;
+			case CTRL('C'):
+			case KEYCODE_UP:
+			case KEYCODE_DOWN:
+			case KEYCODE_HOME:
+			case KEYCODE_END:
+			case KEYCODE_DELETE:
+			case KEYCODE_CTRL_RIGHT:
+			case KEYCODE_CTRL_LEFT:
+			case '\x1b': /* ESC */
+				command_len = load_string("", MAX_LINELEN);
+				cmdedit_prompt = prmt_mem_ptr_save;
+				redraw(cmdedit_y + cmdedit_y_add, 0);
+				break_out = 0;
+				break;
+			case '\b':   /* ^H */
+			case '\x7f': /* DEL */
+				/* convert to wide char string,
+				 * delete char, then convert back */
+				len = mbstowcs(wc, match_buf, sizeof(wc));
+				if (len < 0)
+					len = 0;
+				wc[len-1] = '\0';
+				wcstombs(match_buf, wc, MAX_LINELEN);
+				break_out = 2;
+				has_match = 0;
+				break;
+			case '\r':
+			case '\n': /* Enter */
+				if (has_match == 1) {
+					command_len = load_string(cmdline_buf, MAX_LINELEN);
+				} else {
+					command_len = load_string(match_buf, MAX_LINELEN);
+				}
+				cmdedit_prompt = prmt_mem_ptr_save;
+				redraw(cmdedit_y + cmdedit_y_add, 0);
+				goto_new_line();
+				break_out = 1;
+				break;
+			default: /* process the char */
+				len = wcrtomb(buf, ic, &mbst);
+				if (len > 0) {
+					buf[len] = '\0';
+				}
+				strcat(match_buf, buf);
+				break_out = 2;
+				break;
+		}
+		if (break_out != 2)
+			break;
+		/* if there is something in type buffer,
+		 * do iterative search in history */
+		if (strlen(match_buf) > 0) {
+			/* save current position in history */
+			cur = state->cur_history;
+			if (ic != CTRL('R')) {
+				cur_match = state->cur_history;
+			} else {
+				/* if we hit Ctrl+r (again),
+				 * start searching from the last matched position */
+				state->cur_history = cur_match;
+			}
+			while(get_previous_history()) {
+				cmdline_buf = state->history[state->cur_history] ?
+					state->history[state->cur_history] : '\0';
+				has_match = 0;
+				break_out_search = 0;
+				for(i=0;i+strlen(match_buf)<=strlen(cmdline_buf);i++) {
+					if (strncmp(match_buf, cmdline_buf+i, strlen(match_buf)) == 0) {
+						has_match = 1;
+						break_out_search = 1;
+						/* save position of current match */
+						cur_match = state->cur_history;
+						break;
+					}
+				}
+				if (break_out_search == 1)
+					break;
+			}
+		}
+		/* TODO: this could be done better. There are still some (rare)
+		 * cases of wrapping miscalculation */
+		len = mbstowcs(wc, match_buf, sizeof(wc));
+		if (len < 0)
+			len = strlen(match_buf);
+
+		len_cmd = mbstowcs(wc, cmdline_buf, sizeof(wc));
+		if (len_cmd < 0)
+			len_cmd = strlen(cmdline_buf);
+
+		cmdedit_y_add = (prmt_len+1 + len + (has_match ? len_cmd : len)) /
+			(cmdedit_termw);
+
+		if (cmdedit_y_add > cmdedit_y_add_cmp) {
+			for (int j = 1; j <= (cmdedit_y_add-cmdedit_y_add_cmp); j++) {
+				/* scroll up */
+				printf(ESC"D");
+			}
+			if (cmdedit_y_add-cmdedit_y_add_cmp > 1)
+				redraw(cmdedit_y + cmdedit_y_add - cmdedit_y_add_cmp, 0);
+			else
+				redraw(cmdedit_y + cmdedit_y_add, 0);
+			cmdedit_y_add_cmp = cmdedit_y_add;
+		} else if (cmdedit_y_add != cmdedit_y_add_prev) {
+			redraw(cmdedit_y + cmdedit_y_add_prev, 0);
+		} else {
+			redraw(cmdedit_y + cmdedit_y_add, 0);
+		}
+
+		fputs(match_buf, stdout);
+		fputs("': ", stdout);
+		if (has_match == 0) {
+			fputs(match_buf, stdout);
+		} else {
+			fputs(cmdline_buf, stdout);
+		}
+		cmdedit_y_add_prev = cmdedit_y_add;
+		/* restore current position in history */
+		state->cur_history=cur;
+	} /* !while */
+	return break_out;
+}
+#endif
+
  /* maxsize must be >= 2.
   * Returns:
   * -1 on read errors or EOF, or on bare Ctrl-D,
@@ -2174,6 +2362,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
  			while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
  				input_backspace();
  			break;
+#if ENABLE_FEATURE_REVERSE_SEARCH
+		case CTRL('R'):
+			break_out = reverse_i_search();
+			break;
+#endif

  #if ENABLE_FEATURE_EDITING_VI
  		case 'i'|VI_CMDMODE_BIT:
-- 
1.7.1
_______________________________________________
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