synchronize history patches with busybox git
[busybox-power] / debian / patches / obselete / ctrl_r-history-search.patch
1 libbb/lineedit: implement optional Ctrl-R history search
2 Source: http://git.busybox.net/busybox/commit/?id=a669eca3a230e35e4a6894a30168a047000f3b75
3 ---
4
5 --- a/libbb/Config.src
6 +++ b/libbb/Config.src
7 @@ -93,6 +93,14 @@ config FEATURE_EDITING_SAVEHISTORY
8         help
9           Enable history saving in shells.
10  
11 +config FEATURE_REVERSE_SEARCH
12 +       bool "Reverse history search"
13 +       default y
14 +       depends on FEATURE_EDITING_SAVEHISTORY
15 +       help
16 +         Enable readline-like Ctrl-R combination for reverse history search.
17 +         Increases code by about 0.5k.
18 +
19  config FEATURE_TAB_COMPLETION
20         bool "Tab completion"
21         default y
22 --- a/libbb/lineedit.c
23 +++ b/libbb/lineedit.c
24 @@ -202,9 +202,9 @@ static void deinit_S(void)
25  
26  
27  #if ENABLE_UNICODE_SUPPORT
28 -static size_t load_string(const char *src, int maxsize)
29 +static size_t load_string(const char *src)
30  {
31 -       ssize_t len = mbstowcs(command_ps, src, maxsize - 1);
32 +       ssize_t len = mbstowcs(command_ps, src, S.maxsize - 1);
33         if (len < 0)
34                 len = 0;
35         command_ps[len] = BB_NUL;
36 @@ -297,9 +297,9 @@ static wchar_t adjust_width_and_validate
37         return wc;
38  }
39  #else /* !UNICODE */
40 -static size_t load_string(const char *src, int maxsize)
41 +static size_t load_string(const char *src)
42  {
43 -       safe_strncpy(command_ps, src, maxsize);
44 +       safe_strncpy(command_ps, src, S.maxsize);
45         return strlen(command_ps);
46  }
47  # if ENABLE_FEATURE_TAB_COMPLETION
48 @@ -1202,10 +1202,10 @@ static NOINLINE void input_tab(smallint 
49                         strcpy(match_buf, &command[cursor_mb]);
50                         /* where do we want to have cursor after all? */
51                         strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
52 -                       len = load_string(command, S.maxsize);
53 +                       len = load_string(command);
54                         /* add match and tail */
55                         sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf);
56 -                       command_len = load_string(command, S.maxsize);
57 +                       command_len = load_string(command);
58                         /* write out the matched command */
59                         /* paranoia: load_string can return 0 on conv error,
60                          * prevent passing pos = (0 - 12) to redraw */
61 @@ -1911,6 +1911,140 @@ static int isrtl_str(void)
62  #undef CTRL
63  #define CTRL(a) ((a) & ~0x40)
64  
65 +enum {
66 +       VI_CMDMODE_BIT = 0x40000000,
67 +       /* 0x80000000 bit flags KEYCODE_xxx */
68 +};
69 +
70 +#if ENABLE_FEATURE_REVERSE_SEARCH
71 +/* Mimic readline Ctrl-R reverse history search.
72 + * When invoked, it shows the following prompt:
73 + * (reverse-i-search)'': user_input [cursor pos unchanged by Ctrl-R]
74 + * and typing results in search being performed:
75 + * (reverse-i-search)'tmp': cd /tmp [cursor under t in /tmp]
76 + * Search is performed by looking at progressively older lines in history.
77 + * Ctrl-R again searches for the next match in history.
78 + * Backspace deletes last matched char.
79 + * Control keys exit search and return to normal editing (at current history line).
80 + */
81 +static int32_t reverse_i_search(void)
82 +{
83 +       char match_buf[128]; /* for user input */
84 +       char read_key_buffer[KEYCODE_BUFFER_SIZE];
85 +       const char *matched_history_line;
86 +       const char *saved_prompt;
87 +       int32_t ic;
88 +
89 +       matched_history_line = NULL;
90 +       read_key_buffer[0] = 0;
91 +       match_buf[0] = '\0';
92 +
93 +       /* Save and replace the prompt */
94 +       saved_prompt = cmdedit_prompt;
95 +       goto set_prompt;
96 +
97 +       while (1) {
98 +               int h;
99 +               unsigned match_buf_len = strlen(match_buf);
100 +
101 +               fflush_all();
102 +//FIXME: correct timeout?
103 +               ic = lineedit_read_key(read_key_buffer);
104 +
105 +               switch (ic) {
106 +               case CTRL('R'): /* searching for the next match */
107 +                       break;
108 +
109 +               case '\b':
110 +               case '\x7f':
111 +                       /* Backspace */
112 +                       if (unicode_status == UNICODE_ON) {
113 +                               while (match_buf_len != 0) {
114 +                                       uint8_t c = match_buf[--match_buf_len];
115 +                                       if ((c & 0xc0) != 0x80) /* start of UTF-8 char? */
116 +                                               break; /* yes */
117 +                               }
118 +                       } else {
119 +                               if (match_buf_len != 0)
120 +                                       match_buf_len--;
121 +                       }
122 +                       match_buf[match_buf_len] = '\0';
123 +                       break;
124 +
125 +               default:
126 +                       if (ic < ' '
127 +                        || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
128 +                        || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
129 +                       ) {
130 +                               goto ret;
131 +                       }
132 +
133 +                       /* Append this char */
134 +#if ENABLE_UNICODE_SUPPORT
135 +                       if (unicode_status == UNICODE_ON) {
136 +                               mbstate_t mbstate = { 0 };
137 +                               char buf[MB_CUR_MAX + 1];
138 +                               int len = wcrtomb(buf, ic, &mbstate);
139 +                               if (len > 0) {
140 +                                       buf[len] = '\0';
141 +                                       if (match_buf_len + len < sizeof(match_buf))
142 +                                               strcpy(match_buf + match_buf_len, buf);
143 +                               }
144 +                       } else
145 +#endif
146 +                       if (match_buf_len < sizeof(match_buf) - 1) {
147 +                               match_buf[match_buf_len] = ic;
148 +                               match_buf[match_buf_len + 1] = '\0';
149 +                       }
150 +                       break;
151 +               } /* switch (ic) */
152 +
153 +               /* Search in history for match_buf */
154 +               h = state->cur_history;
155 +               if (ic == CTRL('R'))
156 +                       h--;
157 +               while (h >= 0) {
158 +                       if (state->history[h]) {
159 +                               char *match = strstr(state->history[h], match_buf);
160 +                               if (match) {
161 +                                       state->cur_history = h;
162 +                                       matched_history_line = state->history[h];
163 +                                       command_len = load_string(matched_history_line);
164 +                                       cursor = match - matched_history_line;
165 +//FIXME: cursor position for Unicode case
166 +
167 +                                       free((char*)cmdedit_prompt);
168 + set_prompt:
169 +                                       cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf);
170 +                                       cmdedit_prmt_len = strlen(cmdedit_prompt);
171 +                                       goto do_redraw;
172 +                               }
173 +                       }
174 +                       h--;
175 +               }
176 +
177 +               /* Not found */
178 +               match_buf[match_buf_len] = '\0';
179 +               beep();
180 +               continue;
181 +
182 + do_redraw:
183 +               redraw(cmdedit_y, command_len - cursor);
184 +       } /* while (1) */
185 +
186 + ret:
187 +       if (matched_history_line)
188 +               command_len = load_string(matched_history_line);
189 +
190 +       free((char*)cmdedit_prompt);
191 +       cmdedit_prompt = saved_prompt;
192 +       cmdedit_prmt_len = strlen(cmdedit_prompt);
193 +       redraw(cmdedit_y, command_len - cursor);
194 +
195 +       return ic;
196 +}
197 +#endif
198 +
199  /* maxsize must be >= 2.
200   * Returns:
201   * -1 on read errors or EOF, or on bare Ctrl-D,
202 @@ -2026,15 +2160,14 @@ int FAST_FUNC read_line_input(const char
203                  * clutters the big switch a bit, but keeps all the code
204                  * in one place.
205                  */
206 -               enum {
207 -                       VI_CMDMODE_BIT = 0x40000000,
208 -                       /* 0x80000000 bit flags KEYCODE_xxx */
209 -               };
210                 int32_t ic, ic_raw;
211  
212                 fflush_all();
213                 ic = ic_raw = lineedit_read_key(read_key_buffer);
214  
215 +#if ENABLE_FEATURE_REVERSE_SEARCH
216 + again:
217 +#endif
218  #if ENABLE_FEATURE_EDITING_VI
219                 newdelflag = 1;
220                 if (vi_cmdmode) {
221 @@ -2138,6 +2271,11 @@ int FAST_FUNC read_line_input(const char
222                         while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
223                                 input_backspace();
224                         break;
225 +#if ENABLE_FEATURE_REVERSE_SEARCH
226 +               case CTRL('R'):
227 +                       ic = ic_raw = reverse_i_search();
228 +                       goto again;
229 +#endif
230  
231  #if ENABLE_FEATURE_EDITING_VI
232                 case 'i'|VI_CMDMODE_BIT:
233 @@ -2291,7 +2429,7 @@ int FAST_FUNC read_line_input(const char
234                         /* Rewrite the line with the selected history item */
235                         /* change command */
236                         command_len = load_string(state->history[state->cur_history] ?
237 -                                       state->history[state->cur_history] : "", maxsize);
238 +                                       state->history[state->cur_history] : "");
239                         /* redraw and go to eol (bol, in vi) */
240                         redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
241                         break;