aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2011-07-11 07:36:59 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2011-07-11 07:36:59 +0200
commita669eca3a230e35e4a6894a30168a047000f3b75 (patch)
tree3de2585731b58cf8da6f376324369e49dd243fd8
parent98f213ed7940d70a5ba7ea502469e51da8b0a2b0 (diff)
downloadbusybox-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.src8
-rw-r--r--libbb/lineedit.c165
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
97config 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
97config FEATURE_TAB_COMPLETION 105config 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
210static size_t load_string(const char *src, int maxsize) 210static 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 */
322static size_t load_string(const char *src, int maxsize) 325static 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
1954enum {
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 */
1970static 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;