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