diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2011-07-11 07:36:59 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2011-07-11 07:36:59 +0200 |
commit | a669eca3a230e35e4a6894a30168a047000f3b75 (patch) | |
tree | 3de2585731b58cf8da6f376324369e49dd243fd8 | |
parent | 98f213ed7940d70a5ba7ea502469e51da8b0a2b0 (diff) | |
download | busybox-w32-a669eca3a230e35e4a6894a30168a047000f3b75.tar.gz busybox-w32-a669eca3a230e35e4a6894a30168a047000f3b75.tar.bz2 busybox-w32-a669eca3a230e35e4a6894a30168a047000f3b75.zip |
libbb/lineedit: implement optional Ctrl-R history search
function old new delta
read_line_input 3433 3957 +524
load_string 77 90 +13
input_tab 1086 1069 -17
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 537/-17) Total: 520 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | libbb/Config.src | 8 | ||||
-rw-r--r-- | libbb/lineedit.c | 165 |
2 files changed, 161 insertions, 12 deletions
diff --git a/libbb/Config.src b/libbb/Config.src index 0ea8f43ab..aa442365a 100644 --- a/libbb/Config.src +++ b/libbb/Config.src | |||
@@ -94,6 +94,14 @@ config FEATURE_EDITING_SAVEHISTORY | |||
94 | help | 94 | help |
95 | Enable history saving in shells. | 95 | Enable history saving in shells. |
96 | 96 | ||
97 | config FEATURE_REVERSE_SEARCH | ||
98 | bool "Reverse history search" | ||
99 | default y | ||
100 | depends on FEATURE_EDITING_SAVEHISTORY | ||
101 | help | ||
102 | Enable readline-like Ctrl-R combination for reverse history search. | ||
103 | Increases code by about 0.5k. | ||
104 | |||
97 | config FEATURE_TAB_COMPLETION | 105 | config FEATURE_TAB_COMPLETION |
98 | bool "Tab completion" | 106 | bool "Tab completion" |
99 | default y | 107 | default y |
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 4e3bc0ef7..10265192e 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -207,18 +207,21 @@ static void deinit_S(void) | |||
207 | 207 | ||
208 | 208 | ||
209 | #if ENABLE_UNICODE_SUPPORT | 209 | #if ENABLE_UNICODE_SUPPORT |
210 | static size_t load_string(const char *src, int maxsize) | 210 | static size_t load_string(const char *src) |
211 | { | 211 | { |
212 | if (unicode_status == UNICODE_ON) { | 212 | if (unicode_status == UNICODE_ON) { |
213 | ssize_t len = mbstowcs(command_ps, src, maxsize - 1); | 213 | ssize_t len = mbstowcs(command_ps, src, S.maxsize - 1); |
214 | if (len < 0) | 214 | if (len < 0) |
215 | len = 0; | 215 | len = 0; |
216 | command_ps[len] = BB_NUL; | 216 | command_ps[len] = BB_NUL; |
217 | return len; | 217 | return len; |
218 | } else { | 218 | } else { |
219 | unsigned i = 0; | 219 | unsigned i = 0; |
220 | while ((command_ps[i] = src[i]) != 0) | 220 | while (src[i] && i < S.maxsize - 1) { |
221 | command_ps[i] = src[i]; | ||
221 | i++; | 222 | i++; |
223 | } | ||
224 | command_ps[i] = BB_NUL; | ||
222 | return i; | 225 | return i; |
223 | } | 226 | } |
224 | } | 227 | } |
@@ -319,9 +322,9 @@ static wchar_t adjust_width_and_validate_wc(wchar_t wc) | |||
319 | return wc; | 322 | return wc; |
320 | } | 323 | } |
321 | #else /* !UNICODE */ | 324 | #else /* !UNICODE */ |
322 | static size_t load_string(const char *src, int maxsize) | 325 | static size_t load_string(const char *src) |
323 | { | 326 | { |
324 | safe_strncpy(command_ps, src, maxsize); | 327 | safe_strncpy(command_ps, src, S.maxsize); |
325 | return strlen(command_ps); | 328 | return strlen(command_ps); |
326 | } | 329 | } |
327 | # if ENABLE_FEATURE_TAB_COMPLETION | 330 | # if ENABLE_FEATURE_TAB_COMPLETION |
@@ -1224,10 +1227,10 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1224 | strcpy(match_buf, &command[cursor_mb]); | 1227 | strcpy(match_buf, &command[cursor_mb]); |
1225 | /* where do we want to have cursor after all? */ | 1228 | /* where do we want to have cursor after all? */ |
1226 | strcpy(&command[cursor_mb], chosen_match + match_pfx_len); | 1229 | strcpy(&command[cursor_mb], chosen_match + match_pfx_len); |
1227 | len = load_string(command, S.maxsize); | 1230 | len = load_string(command); |
1228 | /* add match and tail */ | 1231 | /* add match and tail */ |
1229 | sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf); | 1232 | sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf); |
1230 | command_len = load_string(command, S.maxsize); | 1233 | command_len = load_string(command); |
1231 | /* write out the matched command */ | 1234 | /* write out the matched command */ |
1232 | /* paranoia: load_string can return 0 on conv error, | 1235 | /* paranoia: load_string can return 0 on conv error, |
1233 | * prevent passing pos = (0 - 12) to redraw */ | 1236 | * prevent passing pos = (0 - 12) to redraw */ |
@@ -1948,6 +1951,140 @@ static int isrtl_str(void) | |||
1948 | #undef CTRL | 1951 | #undef CTRL |
1949 | #define CTRL(a) ((a) & ~0x40) | 1952 | #define CTRL(a) ((a) & ~0x40) |
1950 | 1953 | ||
1954 | enum { | ||
1955 | VI_CMDMODE_BIT = 0x40000000, | ||
1956 | /* 0x80000000 bit flags KEYCODE_xxx */ | ||
1957 | }; | ||
1958 | |||
1959 | #if ENABLE_FEATURE_REVERSE_SEARCH | ||
1960 | /* Mimic readline Ctrl-R reverse history search. | ||
1961 | * When invoked, it shows the following prompt: | ||
1962 | * (reverse-i-search)'': user_input [cursor pos unchanged by Ctrl-R] | ||
1963 | * and typing results in search being performed: | ||
1964 | * (reverse-i-search)'tmp': cd /tmp [cursor under t in /tmp] | ||
1965 | * Search is performed by looking at progressively older lines in history. | ||
1966 | * Ctrl-R again searches for the next match in history. | ||
1967 | * Backspace deletes last matched char. | ||
1968 | * Control keys exit search and return to normal editing (at current history line). | ||
1969 | */ | ||
1970 | static int32_t reverse_i_search(void) | ||
1971 | { | ||
1972 | char match_buf[128]; /* for user input */ | ||
1973 | char read_key_buffer[KEYCODE_BUFFER_SIZE]; | ||
1974 | const char *matched_history_line; | ||
1975 | const char *saved_prompt; | ||
1976 | int32_t ic; | ||
1977 | |||
1978 | matched_history_line = NULL; | ||
1979 | read_key_buffer[0] = 0; | ||
1980 | match_buf[0] = '\0'; | ||
1981 | |||
1982 | /* Save and replace the prompt */ | ||
1983 | saved_prompt = cmdedit_prompt; | ||
1984 | goto set_prompt; | ||
1985 | |||
1986 | while (1) { | ||
1987 | int h; | ||
1988 | unsigned match_buf_len = strlen(match_buf); | ||
1989 | |||
1990 | fflush_all(); | ||
1991 | //FIXME: correct timeout? | ||
1992 | ic = lineedit_read_key(read_key_buffer, -1); | ||
1993 | |||
1994 | switch (ic) { | ||
1995 | case CTRL('R'): /* searching for the next match */ | ||
1996 | break; | ||
1997 | |||
1998 | case '\b': | ||
1999 | case '\x7f': | ||
2000 | /* Backspace */ | ||
2001 | if (unicode_status == UNICODE_ON) { | ||
2002 | while (match_buf_len != 0) { | ||
2003 | uint8_t c = match_buf[--match_buf_len]; | ||
2004 | if ((c & 0xc0) != 0x80) /* start of UTF-8 char? */ | ||
2005 | break; /* yes */ | ||
2006 | } | ||
2007 | } else { | ||
2008 | if (match_buf_len != 0) | ||
2009 | match_buf_len--; | ||
2010 | } | ||
2011 | match_buf[match_buf_len] = '\0'; | ||
2012 | break; | ||
2013 | |||
2014 | default: | ||
2015 | if (ic < ' ' | ||
2016 | || (!ENABLE_UNICODE_SUPPORT && ic >= 256) | ||
2017 | || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT) | ||
2018 | ) { | ||
2019 | goto ret; | ||
2020 | } | ||
2021 | |||
2022 | /* Append this char */ | ||
2023 | #if ENABLE_UNICODE_SUPPORT | ||
2024 | if (unicode_status == UNICODE_ON) { | ||
2025 | mbstate_t mbstate = { 0 }; | ||
2026 | char buf[MB_CUR_MAX + 1]; | ||
2027 | int len = wcrtomb(buf, ic, &mbstate); | ||
2028 | if (len > 0) { | ||
2029 | buf[len] = '\0'; | ||
2030 | if (match_buf_len + len < sizeof(match_buf)) | ||
2031 | strcpy(match_buf + match_buf_len, buf); | ||
2032 | } | ||
2033 | } else | ||
2034 | #endif | ||
2035 | if (match_buf_len < sizeof(match_buf) - 1) { | ||
2036 | match_buf[match_buf_len] = ic; | ||
2037 | match_buf[match_buf_len + 1] = '\0'; | ||
2038 | } | ||
2039 | break; | ||
2040 | } /* switch (ic) */ | ||
2041 | |||
2042 | /* Search in history for match_buf */ | ||
2043 | h = state->cur_history; | ||
2044 | if (ic == CTRL('R')) | ||
2045 | h--; | ||
2046 | while (h >= 0) { | ||
2047 | if (state->history[h]) { | ||
2048 | char *match = strstr(state->history[h], match_buf); | ||
2049 | if (match) { | ||
2050 | state->cur_history = h; | ||
2051 | matched_history_line = state->history[h]; | ||
2052 | command_len = load_string(matched_history_line); | ||
2053 | cursor = match - matched_history_line; | ||
2054 | //FIXME: cursor position for Unicode case | ||
2055 | |||
2056 | free((char*)cmdedit_prompt); | ||
2057 | set_prompt: | ||
2058 | cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf); | ||
2059 | cmdedit_prmt_len = strlen(cmdedit_prompt); | ||
2060 | goto do_redraw; | ||
2061 | } | ||
2062 | } | ||
2063 | h--; | ||
2064 | } | ||
2065 | |||
2066 | /* Not found */ | ||
2067 | match_buf[match_buf_len] = '\0'; | ||
2068 | beep(); | ||
2069 | continue; | ||
2070 | |||
2071 | do_redraw: | ||
2072 | redraw(cmdedit_y, command_len - cursor); | ||
2073 | } /* while (1) */ | ||
2074 | |||
2075 | ret: | ||
2076 | if (matched_history_line) | ||
2077 | command_len = load_string(matched_history_line); | ||
2078 | |||
2079 | free((char*)cmdedit_prompt); | ||
2080 | cmdedit_prompt = saved_prompt; | ||
2081 | cmdedit_prmt_len = strlen(cmdedit_prompt); | ||
2082 | redraw(cmdedit_y, command_len - cursor); | ||
2083 | |||
2084 | return ic; | ||
2085 | } | ||
2086 | #endif | ||
2087 | |||
1951 | /* maxsize must be >= 2. | 2088 | /* maxsize must be >= 2. |
1952 | * Returns: | 2089 | * Returns: |
1953 | * -1 on read errors or EOF, or on bare Ctrl-D, | 2090 | * -1 on read errors or EOF, or on bare Ctrl-D, |
@@ -2062,15 +2199,14 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2062 | * clutters the big switch a bit, but keeps all the code | 2199 | * clutters the big switch a bit, but keeps all the code |
2063 | * in one place. | 2200 | * in one place. |
2064 | */ | 2201 | */ |
2065 | enum { | ||
2066 | VI_CMDMODE_BIT = 0x40000000, | ||
2067 | /* 0x80000000 bit flags KEYCODE_xxx */ | ||
2068 | }; | ||
2069 | int32_t ic, ic_raw; | 2202 | int32_t ic, ic_raw; |
2070 | 2203 | ||
2071 | fflush_all(); | 2204 | fflush_all(); |
2072 | ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); | 2205 | ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); |
2073 | 2206 | ||
2207 | #if ENABLE_FEATURE_REVERSE_SEARCH | ||
2208 | again: | ||
2209 | #endif | ||
2074 | #if ENABLE_FEATURE_EDITING_VI | 2210 | #if ENABLE_FEATURE_EDITING_VI |
2075 | newdelflag = 1; | 2211 | newdelflag = 1; |
2076 | if (vi_cmdmode) { | 2212 | if (vi_cmdmode) { |
@@ -2174,6 +2310,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2174 | while (cursor > 0 && !BB_isspace(command_ps[cursor-1])) | 2310 | while (cursor > 0 && !BB_isspace(command_ps[cursor-1])) |
2175 | input_backspace(); | 2311 | input_backspace(); |
2176 | break; | 2312 | break; |
2313 | #if ENABLE_FEATURE_REVERSE_SEARCH | ||
2314 | case CTRL('R'): | ||
2315 | ic = ic_raw = reverse_i_search(); | ||
2316 | goto again; | ||
2317 | #endif | ||
2177 | 2318 | ||
2178 | #if ENABLE_FEATURE_EDITING_VI | 2319 | #if ENABLE_FEATURE_EDITING_VI |
2179 | case 'i'|VI_CMDMODE_BIT: | 2320 | case 'i'|VI_CMDMODE_BIT: |
@@ -2327,7 +2468,7 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2327 | /* Rewrite the line with the selected history item */ | 2468 | /* Rewrite the line with the selected history item */ |
2328 | /* change command */ | 2469 | /* change command */ |
2329 | command_len = load_string(state->history[state->cur_history] ? | 2470 | command_len = load_string(state->history[state->cur_history] ? |
2330 | state->history[state->cur_history] : "", maxsize); | 2471 | state->history[state->cur_history] : ""); |
2331 | /* redraw and go to eol (bol, in vi) */ | 2472 | /* redraw and go to eol (bol, in vi) */ |
2332 | redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); | 2473 | redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); |
2333 | break; | 2474 | break; |