Implemented readline's mimic for reverse history search (Ctrl+r)
Signed-off-by: kyak <[email protected]> --- 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 [email protected] http://lists.busybox.net/mailman/listinfo/busybox
