aboutsummaryrefslogtreecommitdiff
path: root/libbb/lineedit.c
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2009-07-10 18:40:49 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2009-07-10 18:40:49 +0200
commit2e6d4ef695c4be0ff1963e24315bc5bec6c5313b (patch)
tree25c38a76c639d630b8a6e9c415e4d9e1a5e770d9 /libbb/lineedit.c
parent860d2bbdda67acfec39cd28e38881633ec4e66d6 (diff)
downloadbusybox-w32-2e6d4ef695c4be0ff1963e24315bc5bec6c5313b.tar.gz
busybox-w32-2e6d4ef695c4be0ff1963e24315bc5bec6c5313b.tar.bz2
busybox-w32-2e6d4ef695c4be0ff1963e24315bc5bec6c5313b.zip
lineedit: implement Unicode-aware line editing (optional)
When off: function old new delta input_delete 133 140 +7 vi_word_motion 236 240 +4 vi_Back_motion 89 92 +3 vi_end_motion 223 225 +2 cmdedit_set_out_char 74 76 +2 vi_back_motion 198 199 +1 vi_Word_motion 87 88 +1 vi_End_motion 92 86 -6 read_line_input 6293 6270 -23 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 7/2 up/down: 20/-29) Total: -9 bytes When on: lineedit_read_key 146 272 +126 BB_PUTCHAR - 94 +94 read_line_input 6293 6351 +58 save_string - 43 +43 wcstombs - 38 +38 mbstowcs - 38 +38 save_command_ps_at_cur_history 65 93 +28 input_delete 133 148 +15 vi_Back_motion 89 90 +1 vi_Word_motion 87 86 -1 cmdedit_set_out_char 74 73 -1 vi_End_motion 92 84 -8 vi_back_motion 198 187 -11 vi_word_motion 236 206 -30 vi_end_motion 223 184 -39 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 5/6 up/down: 441/-90) Total: 351 bytes There is one TODO and maybe some bugs :) Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'libbb/lineedit.c')
-rw-r--r--libbb/lineedit.c332
1 files changed, 235 insertions, 97 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index ccffedced..e5d0c1b6c 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -33,32 +33,26 @@
33 * 33 *
34 * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] ' 34 * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
35 */ 35 */
36
37#include "libbb.h" 36#include "libbb.h"
38 37#if ENABLE_FEATURE_ASSUME_UNICODE
38# include <wchar.h>
39# include <wctype.h>
40#endif
39 41
40/* FIXME: obsolete CONFIG item? */ 42/* FIXME: obsolete CONFIG item? */
41#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 43#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
42 44
43
44#ifdef TEST 45#ifdef TEST
45 46# define ENABLE_FEATURE_EDITING 0
46#define ENABLE_FEATURE_EDITING 0 47# define ENABLE_FEATURE_TAB_COMPLETION 0
47#define ENABLE_FEATURE_TAB_COMPLETION 0 48# define ENABLE_FEATURE_USERNAME_COMPLETION 0
48#define ENABLE_FEATURE_USERNAME_COMPLETION 0 49# define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
49#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 50#endif
50
51#endif /* TEST */
52 51
53 52
54/* Entire file (except TESTing part) sits inside this #if */ 53/* Entire file (except TESTing part) sits inside this #if */
55#if ENABLE_FEATURE_EDITING 54#if ENABLE_FEATURE_EDITING
56 55
57#if ENABLE_LOCALE_SUPPORT
58#define Isprint(c) isprint(c)
59#else
60#define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))
61#endif
62 56
63#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \ 57#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
64 (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT) 58 (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
@@ -68,6 +62,38 @@
68#define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__ 62#define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__
69#endif 63#endif
70 64
65
66#undef CHAR_T
67#if ENABLE_FEATURE_ASSUME_UNICODE
68# define BB_NUL L'\0'
69# define CHAR_T wchar_t
70# define BB_isspace(c) iswspace(c)
71# define BB_isalnum(c) iswalnum(c)
72# define BB_ispunct(c) iswpunct(c)
73# define BB_isprint(c) iswprint(c)
74/* this catches bugs */
75# undef isspace
76# undef isalnum
77# undef ispunct
78# undef isprint
79# define isspace isspace_must_not_be_used
80# define isalnum isalnum_must_not_be_used
81# define ispunct ispunct_must_not_be_used
82# define isprint isprint_must_not_be_used
83#else
84# define BB_NUL '\0'
85# define CHAR_T char
86# define BB_isspace(c) isspace(c)
87# define BB_isalnum(c) isalnum(c)
88# define BB_ispunct(c) ispunct(c)
89# if ENABLE_LOCALE_SUPPORT
90# define BB_isprint(c) isprint(c)
91# else
92# define BB_isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))
93# endif
94#endif
95
96
71enum { 97enum {
72 /* We use int16_t for positions, need to limit line len */ 98 /* We use int16_t for positions, need to limit line len */
73 MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 99 MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
@@ -92,7 +118,7 @@ struct lineedit_statics {
92 118
93 unsigned cursor; 119 unsigned cursor;
94 unsigned command_len; 120 unsigned command_len;
95 char *command_ps; 121 CHAR_T *command_ps;
96 122
97 const char *cmdedit_prompt; 123 const char *cmdedit_prompt;
98#if ENABLE_FEATURE_EDITING_FANCY_PROMPT 124#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
@@ -111,9 +137,9 @@ struct lineedit_statics {
111 137
112#if ENABLE_FEATURE_EDITING_VI 138#if ENABLE_FEATURE_EDITING_VI
113#define DELBUFSIZ 128 139#define DELBUFSIZ 128
114 char *delptr; 140 CHAR_T *delptr;
115 smallint newdelflag; /* whether delbuf should be reused yet */ 141 smallint newdelflag; /* whether delbuf should be reused yet */
116 char delbuf[DELBUFSIZ]; /* a place to store deleted characters */ 142 CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */
117#endif 143#endif
118 144
119 /* Formerly these were big buffers on stack: */ 145 /* Formerly these were big buffers on stack: */
@@ -172,6 +198,49 @@ static void deinit_S(void)
172#define DEINIT_S() deinit_S() 198#define DEINIT_S() deinit_S()
173 199
174 200
201#if ENABLE_FEATURE_ASSUME_UNICODE
202static size_t load_string(const char *src, int maxsize)
203{
204 ssize_t len = mbstowcs(command_ps, src, maxsize - 1);
205 if (len < 0)
206 len = 0;
207 command_ps[len] = L'\0';
208 return len;
209}
210static size_t save_string(char *dst, int maxsize)
211{
212 ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
213 if (len < 0)
214 len = 0;
215 dst[len] = '\0';
216 return len;
217}
218/* I thought just fputwc(c, stdout) would work. But no... */
219static void BB_PUTCHAR(wchar_t c)
220{
221 char buf[MB_CUR_MAX + 1];
222 mbstate_t mbst = { 0 };
223 ssize_t len = wcrtomb(buf, c, &mbst);
224
225 if (len > 0) {
226 buf[len] = '\0';
227 fputs(buf, stdout);
228 }
229}
230#else
231static size_t load_string(const char *src, int maxsize)
232{
233 safe_strncpy(command_ps, src, maxsize);
234 return strlen(command_ps);
235}
236static void save_string(char *dst, int maxsize)
237{
238 safe_strncpy(dst, command_ps, maxsize);
239}
240#define BB_PUTCHAR(c) bb_putchar(c)
241#endif
242
243
175/* Put 'command_ps[cursor]', cursor++. 244/* Put 'command_ps[cursor]', cursor++.
176 * Advance cursor on screen. If we reached right margin, scroll text up 245 * Advance cursor on screen. If we reached right margin, scroll text up
177 * and remove terminal margin effect by printing 'next_char' */ 246 * and remove terminal margin effect by printing 'next_char' */
@@ -183,15 +252,15 @@ static void cmdedit_set_out_char(void)
183static void cmdedit_set_out_char(int next_char) 252static void cmdedit_set_out_char(int next_char)
184#endif 253#endif
185{ 254{
186 int c = (unsigned char)command_ps[cursor]; 255 CHAR_T c = command_ps[cursor];
187 256
188 if (c == '\0') { 257 if (c == BB_NUL) {
189 /* erase character after end of input string */ 258 /* erase character after end of input string */
190 c = ' '; 259 c = ' ';
191 } 260 }
192#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 261#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
193 /* Display non-printable characters in reverse */ 262 /* Display non-printable characters in reverse */
194 if (!Isprint(c)) { 263 if (!BB_isprint(c)) {
195 if (c >= 128) 264 if (c >= 128)
196 c -= 128; 265 c -= 128;
197 if (c < ' ') 266 if (c < ' ')
@@ -202,7 +271,7 @@ static void cmdedit_set_out_char(int next_char)
202 } else 271 } else
203#endif 272#endif
204 { 273 {
205 bb_putchar(c); 274 BB_PUTCHAR(c);
206 } 275 }
207 if (++cmdedit_x >= cmdedit_termw) { 276 if (++cmdedit_x >= cmdedit_termw) {
208 /* terminal is scrolled down */ 277 /* terminal is scrolled down */
@@ -224,7 +293,7 @@ static void cmdedit_set_out_char(int next_char)
224 bb_putchar('\b'); 293 bb_putchar('\b');
225#endif 294#endif
226 } 295 }
227// Huh? What if command_ps[cursor] == '\0' (we are at the end already?) 296// Huh? What if command_ps[cursor] == BB_NUL (we are at the end already?)
228 cursor++; 297 cursor++;
229} 298}
230 299
@@ -357,7 +426,8 @@ static void input_delete(int save)
357 } 426 }
358#endif 427#endif
359 428
360 overlapping_strcpy(command_ps + j, command_ps + j + 1); 429 memmove(command_ps + j, command_ps + j + 1,
430 (command_len - j + 1) * sizeof(command_ps[0]));
361 command_len--; 431 command_len--;
362 input_end(); /* rewrite new line */ 432 input_end(); /* rewrite new line */
363 cmdedit_set_out_char(' '); /* erase char */ 433 cmdedit_set_out_char(' '); /* erase char */
@@ -374,8 +444,9 @@ static void put(void)
374 return; 444 return;
375 ocursor = cursor; 445 ocursor = cursor;
376 /* open hole and then fill it */ 446 /* open hole and then fill it */
377 memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1); 447 memmove(command_ps + cursor + j, command_ps + cursor,
378 strncpy(command_ps + cursor, delbuf, j); 448 (command_len - cursor + 1) * sizeof(command_ps[0]));
449 memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
379 command_len += j; 450 command_len += j;
380 input_end(); /* rewrite new line */ 451 input_end(); /* rewrite new line */
381 input_backward(cursor - ocursor - j + 1); /* at end of new text */ 452 input_backward(cursor - ocursor - j + 1); /* at end of new text */
@@ -857,10 +928,10 @@ static void input_tab(smallint *lastWasTab)
857 928
858 *lastWasTab = TRUE; /* flop trigger */ 929 *lastWasTab = TRUE; /* flop trigger */
859 930
860 /* Make a local copy of the string -- up 931 /* Make a local copy of the string --
861 * to the position of the cursor */ 932 * up to the position of the cursor */
862 tmp = strncpy(matchBuf, command_ps, cursor); 933 save_string(matchBuf, cursor + 1);
863 tmp[cursor] = '\0'; 934 tmp = matchBuf;
864 935
865 find_type = find_match(matchBuf, &recalc_pos); 936 find_type = find_match(matchBuf, &recalc_pos);
866 937
@@ -930,6 +1001,8 @@ static void input_tab(smallint *lastWasTab)
930 /* have space to placed match? */ 1001 /* have space to placed match? */
931 if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) { 1002 if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) {
932 /* before word for match */ 1003 /* before word for match */
1004//TODO:
1005#if !ENABLE_FEATURE_ASSUME_UNICODE
933 command_ps[cursor - recalc_pos] = '\0'; 1006 command_ps[cursor - recalc_pos] = '\0';
934 /* save tail line */ 1007 /* save tail line */
935 strcpy(matchBuf, command_ps + cursor); 1008 strcpy(matchBuf, command_ps + cursor);
@@ -944,6 +1017,7 @@ static void input_tab(smallint *lastWasTab)
944 /* new len */ 1017 /* new len */
945 command_len = strlen(command_ps); 1018 command_len = strlen(command_ps);
946 /* write out the matched command */ 1019 /* write out the matched command */
1020#endif
947 redraw(cmdedit_y, command_len - recalc_pos); 1021 redraw(cmdedit_y, command_len - recalc_pos);
948 } 1022 }
949 free(tmp); 1023 free(tmp);
@@ -978,10 +1052,19 @@ line_input_t* FAST_FUNC new_line_input_t(int flags)
978 1052
979static void save_command_ps_at_cur_history(void) 1053static void save_command_ps_at_cur_history(void)
980{ 1054{
981 if (command_ps[0] != '\0') { 1055 if (command_ps[0] != BB_NUL) {
982 int cur = state->cur_history; 1056 int cur = state->cur_history;
983 free(state->history[cur]); 1057 free(state->history[cur]);
1058
1059#if ENABLE_FEATURE_ASSUME_UNICODE
1060 {
1061 char tbuf[MAX_LINELEN];
1062 save_string(tbuf, sizeof(tbuf));
1063 state->history[cur] = xstrdup(tbuf);
1064 }
1065#else
984 state->history[cur] = xstrdup(command_ps); 1066 state->history[cur] = xstrdup(command_ps);
1067#endif
985 } 1068 }
986} 1069}
987 1070
@@ -1199,93 +1282,105 @@ static void remember_in_history(char *str)
1199 1282
1200#if ENABLE_FEATURE_EDITING_VI 1283#if ENABLE_FEATURE_EDITING_VI
1201static void 1284static void
1202vi_Word_motion(char *command, int eat) 1285vi_Word_motion(int eat)
1203{ 1286{
1204 while (cursor < command_len && !isspace(command[cursor])) 1287 CHAR_T *command = command_ps;
1288
1289 while (cursor < command_len && !BB_isspace(command[cursor]))
1205 input_forward(); 1290 input_forward();
1206 if (eat) while (cursor < command_len && isspace(command[cursor])) 1291 if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
1207 input_forward(); 1292 input_forward();
1208} 1293}
1209 1294
1210static void 1295static void
1211vi_word_motion(char *command, int eat) 1296vi_word_motion(int eat)
1212{ 1297{
1213 if (isalnum(command[cursor]) || command[cursor] == '_') { 1298 CHAR_T *command = command_ps;
1299
1300 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1214 while (cursor < command_len 1301 while (cursor < command_len
1215 && (isalnum(command[cursor+1]) || command[cursor+1] == '_')) 1302 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_'))
1216 input_forward(); 1303 input_forward();
1217 } else if (ispunct(command[cursor])) { 1304 } else if (BB_ispunct(command[cursor])) {
1218 while (cursor < command_len && ispunct(command[cursor+1])) 1305 while (cursor < command_len && BB_ispunct(command[cursor+1]))
1219 input_forward(); 1306 input_forward();
1220 } 1307 }
1221 1308
1222 if (cursor < command_len) 1309 if (cursor < command_len)
1223 input_forward(); 1310 input_forward();
1224 1311
1225 if (eat && cursor < command_len && isspace(command[cursor])) 1312 if (eat && cursor < command_len && BB_isspace(command[cursor]))
1226 while (cursor < command_len && isspace(command[cursor])) 1313 while (cursor < command_len && BB_isspace(command[cursor]))
1227 input_forward(); 1314 input_forward();
1228} 1315}
1229 1316
1230static void 1317static void
1231vi_End_motion(char *command) 1318vi_End_motion(void)
1232{ 1319{
1320 CHAR_T *command = command_ps;
1321
1233 input_forward(); 1322 input_forward();
1234 while (cursor < command_len && isspace(command[cursor])) 1323 while (cursor < command_len && BB_isspace(command[cursor]))
1235 input_forward(); 1324 input_forward();
1236 while (cursor < command_len-1 && !isspace(command[cursor+1])) 1325 while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
1237 input_forward(); 1326 input_forward();
1238} 1327}
1239 1328
1240static void 1329static void
1241vi_end_motion(char *command) 1330vi_end_motion(void)
1242{ 1331{
1332 CHAR_T *command = command_ps;
1333
1243 if (cursor >= command_len-1) 1334 if (cursor >= command_len-1)
1244 return; 1335 return;
1245 input_forward(); 1336 input_forward();
1246 while (cursor < command_len-1 && isspace(command[cursor])) 1337 while (cursor < command_len-1 && BB_isspace(command[cursor]))
1247 input_forward(); 1338 input_forward();
1248 if (cursor >= command_len-1) 1339 if (cursor >= command_len-1)
1249 return; 1340 return;
1250 if (isalnum(command[cursor]) || command[cursor] == '_') { 1341 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1251 while (cursor < command_len-1 1342 while (cursor < command_len-1
1252 && (isalnum(command[cursor+1]) || command[cursor+1] == '_') 1343 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1253 ) { 1344 ) {
1254 input_forward(); 1345 input_forward();
1255 } 1346 }
1256 } else if (ispunct(command[cursor])) { 1347 } else if (BB_ispunct(command[cursor])) {
1257 while (cursor < command_len-1 && ispunct(command[cursor+1])) 1348 while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
1258 input_forward(); 1349 input_forward();
1259 } 1350 }
1260} 1351}
1261 1352
1262static void 1353static void
1263vi_Back_motion(char *command) 1354vi_Back_motion(void)
1264{ 1355{
1265 while (cursor > 0 && isspace(command[cursor-1])) 1356 CHAR_T *command = command_ps;
1357
1358 while (cursor > 0 && BB_isspace(command[cursor-1]))
1266 input_backward(1); 1359 input_backward(1);
1267 while (cursor > 0 && !isspace(command[cursor-1])) 1360 while (cursor > 0 && !BB_isspace(command[cursor-1]))
1268 input_backward(1); 1361 input_backward(1);
1269} 1362}
1270 1363
1271static void 1364static void
1272vi_back_motion(char *command) 1365vi_back_motion(void)
1273{ 1366{
1367 CHAR_T *command = command_ps;
1368
1274 if (cursor <= 0) 1369 if (cursor <= 0)
1275 return; 1370 return;
1276 input_backward(1); 1371 input_backward(1);
1277 while (cursor > 0 && isspace(command[cursor])) 1372 while (cursor > 0 && BB_isspace(command[cursor]))
1278 input_backward(1); 1373 input_backward(1);
1279 if (cursor <= 0) 1374 if (cursor <= 0)
1280 return; 1375 return;
1281 if (isalnum(command[cursor]) || command[cursor] == '_') { 1376 if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1282 while (cursor > 0 1377 while (cursor > 0
1283 && (isalnum(command[cursor-1]) || command[cursor-1] == '_') 1378 && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
1284 ) { 1379 ) {
1285 input_backward(1); 1380 input_backward(1);
1286 } 1381 }
1287 } else if (ispunct(command[cursor])) { 1382 } else if (BB_ispunct(command[cursor])) {
1288 while (cursor > 0 && ispunct(command[cursor-1])) 1383 while (cursor > 0 && BB_ispunct(command[cursor-1]))
1289 input_backward(1); 1384 input_backward(1);
1290 } 1385 }
1291} 1386}
@@ -1446,6 +1541,11 @@ static int lineedit_read_key(char *read_key_buffer)
1446{ 1541{
1447 int64_t ic; 1542 int64_t ic;
1448 struct pollfd pfd; 1543 struct pollfd pfd;
1544 int delay = -1;
1545#if ENABLE_FEATURE_ASSUME_UNICODE
1546 char unicode_buf[MB_CUR_MAX + 1];
1547 int unicode_idx = 0;
1548#endif
1449 1549
1450 pfd.fd = STDIN_FILENO; 1550 pfd.fd = STDIN_FILENO;
1451 pfd.events = POLLIN; 1551 pfd.events = POLLIN;
@@ -1455,7 +1555,7 @@ static int lineedit_read_key(char *read_key_buffer)
1455 /* Wait for input. Can't just call read_key, 1555 /* Wait for input. Can't just call read_key,
1456 * it returns at once if stdin 1556 * it returns at once if stdin
1457 * is in non-blocking mode. */ 1557 * is in non-blocking mode. */
1458 safe_poll(&pfd, 1, -1); 1558 safe_poll(&pfd, 1, delay);
1459 } 1559 }
1460 /* Note: read_key sets errno to 0 on success: */ 1560 /* Note: read_key sets errno to 0 on success: */
1461 ic = read_key(STDIN_FILENO, read_key_buffer); 1561 ic = read_key(STDIN_FILENO, read_key_buffer);
@@ -1472,20 +1572,27 @@ static int lineedit_read_key(char *read_key_buffer)
1472 } 1572 }
1473 goto poll_again; 1573 goto poll_again;
1474 } 1574 }
1575
1576#if ENABLE_FEATURE_ASSUME_UNICODE
1577 {
1578 wchar_t wc;
1579
1580 if ((int32_t)ic < 0) /* KEYCODE_xxx */
1581 return ic;
1582 unicode_buf[unicode_idx++] = ic;
1583 unicode_buf[unicode_idx] = '\0';
1584 if (mbstowcs(&wc, unicode_buf, 1) < 1 && unicode_idx < MB_CUR_MAX) {
1585 delay = 50;
1586 goto poll_again;
1587 }
1588 ic = wc;
1589 }
1590#endif
1475 } while (errno == EAGAIN); 1591 } while (errno == EAGAIN);
1592
1476 return ic; 1593 return ic;
1477} 1594}
1478 1595
1479/*
1480 * The emacs and vi modes share much of the code in the big
1481 * command loop. Commands entered when in vi's command mode (aka
1482 * "escape mode") get an extra bit added to distinguish them --
1483 * this keeps them from being self-inserted. This clutters the
1484 * big switch a bit, but keeps all the code in one place.
1485 */
1486
1487#define VI_CMDMODE_BIT 0x100
1488
1489/* leave out the "vi-mode"-only case labels if vi editing isn't 1596/* leave out the "vi-mode"-only case labels if vi editing isn't
1490 * configured. */ 1597 * configured. */
1491#define vi_case(caselabel) IF_FEATURE_EDITING(case caselabel) 1598#define vi_case(caselabel) IF_FEATURE_EDITING(case caselabel)
@@ -1505,7 +1612,6 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1505#if ENABLE_FEATURE_TAB_COMPLETION 1612#if ENABLE_FEATURE_TAB_COMPLETION
1506 smallint lastWasTab = FALSE; 1613 smallint lastWasTab = FALSE;
1507#endif 1614#endif
1508 int ic;
1509 smallint break_out = 0; 1615 smallint break_out = 0;
1510#if ENABLE_FEATURE_EDITING_VI 1616#if ENABLE_FEATURE_EDITING_VI
1511 smallint vi_cmdmode = 0; 1617 smallint vi_cmdmode = 0;
@@ -1547,8 +1653,13 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1547 /* prepare before init handlers */ 1653 /* prepare before init handlers */
1548 cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ 1654 cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */
1549 command_len = 0; 1655 command_len = 0;
1656#if ENABLE_FEATURE_ASSUME_UNICODE
1657 command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
1658#else
1550 command_ps = command; 1659 command_ps = command;
1551 command[0] = '\0'; 1660 command[0] = '\0';
1661#endif
1662#define command command_must_not_be_used
1552 1663
1553 new_settings = initial_settings; 1664 new_settings = initial_settings;
1554 new_settings.c_lflag &= ~ICANON; /* unbuffered input */ 1665 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
@@ -1580,8 +1691,8 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1580#endif 1691#endif
1581 1692
1582#if 0 1693#if 0
1583 for (ic = 0; ic <= MAX_HISTORY; ic++) 1694 for (i = 0; i <= MAX_HISTORY; i++)
1584 bb_error_msg("history[%d]:'%s'", ic, state->history[ic]); 1695 bb_error_msg("history[%d]:'%s'", i, state->history[i]);
1585 bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history); 1696 bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
1586#endif 1697#endif
1587 1698
@@ -1590,6 +1701,20 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1590 1701
1591 read_key_buffer[0] = 0; 1702 read_key_buffer[0] = 0;
1592 while (1) { 1703 while (1) {
1704 /*
1705 * The emacs and vi modes share much of the code in the big
1706 * command loop. Commands entered when in vi's command mode
1707 * (aka "escape mode") get an extra bit added to distinguish
1708 * them - this keeps them from being self-inserted. This
1709 * clutters the big switch a bit, but keeps all the code
1710 * in one place.
1711 */
1712 enum {
1713 VI_CMDMODE_BIT = 0x40000000,
1714 /* 0x80000000 bit flags KEYCODE_xxx */
1715 };
1716 int32_t ic;
1717
1593 fflush(NULL); 1718 fflush(NULL);
1594 ic = lineedit_read_key(read_key_buffer); 1719 ic = lineedit_read_key(read_key_buffer);
1595 1720
@@ -1667,7 +1792,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1667#endif 1792#endif
1668 case CTRL('K'): 1793 case CTRL('K'):
1669 /* Control-k -- clear to end of line */ 1794 /* Control-k -- clear to end of line */
1670 command[cursor] = 0; 1795 command_ps[cursor] = BB_NUL;
1671 command_len = cursor; 1796 command_len = cursor;
1672 printf("\033[J"); 1797 printf("\033[J");
1673 break; 1798 break;
@@ -1697,17 +1822,18 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1697 vi_case(CTRL('U')|VI_CMDMODE_BIT:) 1822 vi_case(CTRL('U')|VI_CMDMODE_BIT:)
1698 /* Control-U -- Clear line before cursor */ 1823 /* Control-U -- Clear line before cursor */
1699 if (cursor) { 1824 if (cursor) {
1700 overlapping_strcpy(command, command + cursor);
1701 command_len -= cursor; 1825 command_len -= cursor;
1826 memmove(command_ps, command_ps + cursor,
1827 (command_len + 1) * sizeof(command_ps[0]));
1702 redraw(cmdedit_y, command_len); 1828 redraw(cmdedit_y, command_len);
1703 } 1829 }
1704 break; 1830 break;
1705 case CTRL('W'): 1831 case CTRL('W'):
1706 vi_case(CTRL('W')|VI_CMDMODE_BIT:) 1832 vi_case(CTRL('W')|VI_CMDMODE_BIT:)
1707 /* Control-W -- Remove the last word */ 1833 /* Control-W -- Remove the last word */
1708 while (cursor > 0 && isspace(command[cursor-1])) 1834 while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
1709 input_backspace(); 1835 input_backspace();
1710 while (cursor > 0 && !isspace(command[cursor-1])) 1836 while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
1711 input_backspace(); 1837 input_backspace();
1712 break; 1838 break;
1713 1839
@@ -1737,22 +1863,22 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1737 } 1863 }
1738 break; 1864 break;
1739 case 'W'|VI_CMDMODE_BIT: 1865 case 'W'|VI_CMDMODE_BIT:
1740 vi_Word_motion(command, 1); 1866 vi_Word_motion(1);
1741 break; 1867 break;
1742 case 'w'|VI_CMDMODE_BIT: 1868 case 'w'|VI_CMDMODE_BIT:
1743 vi_word_motion(command, 1); 1869 vi_word_motion(1);
1744 break; 1870 break;
1745 case 'E'|VI_CMDMODE_BIT: 1871 case 'E'|VI_CMDMODE_BIT:
1746 vi_End_motion(command); 1872 vi_End_motion();
1747 break; 1873 break;
1748 case 'e'|VI_CMDMODE_BIT: 1874 case 'e'|VI_CMDMODE_BIT:
1749 vi_end_motion(command); 1875 vi_end_motion();
1750 break; 1876 break;
1751 case 'B'|VI_CMDMODE_BIT: 1877 case 'B'|VI_CMDMODE_BIT:
1752 vi_Back_motion(command); 1878 vi_Back_motion();
1753 break; 1879 break;
1754 case 'b'|VI_CMDMODE_BIT: 1880 case 'b'|VI_CMDMODE_BIT:
1755 vi_back_motion(command); 1881 vi_back_motion();
1756 break; 1882 break;
1757 case 'C'|VI_CMDMODE_BIT: 1883 case 'C'|VI_CMDMODE_BIT:
1758 vi_cmdmode = 0; 1884 vi_cmdmode = 0;
@@ -1787,17 +1913,17 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1787 case 'E': 1913 case 'E':
1788 switch (ic) { 1914 switch (ic) {
1789 case 'w': /* "dw", "cw" */ 1915 case 'w': /* "dw", "cw" */
1790 vi_word_motion(command, vi_cmdmode); 1916 vi_word_motion(vi_cmdmode);
1791 break; 1917 break;
1792 case 'W': /* 'dW', 'cW' */ 1918 case 'W': /* 'dW', 'cW' */
1793 vi_Word_motion(command, vi_cmdmode); 1919 vi_Word_motion(vi_cmdmode);
1794 break; 1920 break;
1795 case 'e': /* 'de', 'ce' */ 1921 case 'e': /* 'de', 'ce' */
1796 vi_end_motion(command); 1922 vi_end_motion();
1797 input_forward(); 1923 input_forward();
1798 break; 1924 break;
1799 case 'E': /* 'dE', 'cE' */ 1925 case 'E': /* 'dE', 'cE' */
1800 vi_End_motion(command); 1926 vi_End_motion();
1801 input_forward(); 1927 input_forward();
1802 break; 1928 break;
1803 } 1929 }
@@ -1809,9 +1935,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1809 case 'b': /* "db", "cb" */ 1935 case 'b': /* "db", "cb" */
1810 case 'B': /* implemented as B */ 1936 case 'B': /* implemented as B */
1811 if (ic == 'b') 1937 if (ic == 'b')
1812 vi_back_motion(command); 1938 vi_back_motion();
1813 else 1939 else
1814 vi_Back_motion(command); 1940 vi_Back_motion();
1815 while (sc-- > cursor) 1941 while (sc-- > cursor)
1816 input_delete(1); 1942 input_delete(1);
1817 break; 1943 break;
@@ -1839,7 +1965,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1839 if (ic < ' ' || ic > 255) { 1965 if (ic < ' ' || ic > 255) {
1840 beep(); 1966 beep();
1841 } else { 1967 } else {
1842 command[cursor] = ic; 1968 command_ps[cursor] = ic;
1843 bb_putchar(ic); 1969 bb_putchar(ic);
1844 bb_putchar('\b'); 1970 bb_putchar('\b');
1845 } 1971 }
@@ -1865,7 +1991,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1865 rewrite_line: 1991 rewrite_line:
1866 /* Rewrite the line with the selected history item */ 1992 /* Rewrite the line with the selected history item */
1867 /* change command */ 1993 /* change command */
1868 command_len = strlen(strcpy(command, state->history[state->cur_history] ? : "")); 1994 command_len = load_string(state->history[state->cur_history] ? : "", maxsize);
1869 /* redraw and go to eol (bol, in vi) */ 1995 /* redraw and go to eol (bol, in vi) */
1870 redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); 1996 redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
1871 break; 1997 break;
@@ -1896,7 +2022,10 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1896// break; 2022// break;
1897// } 2023// }
1898// } 2024// }
1899 if (ic < ' ' || ic > 255) { 2025 if (ic < ' '
2026 || (!ENABLE_FEATURE_ASSUME_UNICODE && ic >= 256)
2027 || (ENABLE_FEATURE_ASSUME_UNICODE && ic >= VI_CMDMODE_BIT)
2028 ) {
1900 /* If VI_CMDMODE_BIT is set, ic is >= 256 2029 /* If VI_CMDMODE_BIT is set, ic is >= 256
1901 * and command mode ignores unexpected chars. 2030 * and command mode ignores unexpected chars.
1902 * Otherwise, we are here if ic is a 2031 * Otherwise, we are here if ic is a
@@ -1913,15 +2042,16 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1913 command_len++; 2042 command_len++;
1914 if (cursor == (command_len - 1)) { 2043 if (cursor == (command_len - 1)) {
1915 /* We are at the end, append */ 2044 /* We are at the end, append */
1916 command[cursor] = ic; 2045 command_ps[cursor] = ic;
1917 command[cursor + 1] = '\0'; 2046 command_ps[cursor + 1] = BB_NUL;
1918 cmdedit_set_out_char(' '); 2047 cmdedit_set_out_char(' ');
1919 } else { 2048 } else {
1920 /* In the middle, insert */ 2049 /* In the middle, insert */
1921 int sc = cursor; 2050 int sc = cursor;
1922 2051
1923 memmove(command + sc + 1, command + sc, command_len - sc); 2052 memmove(command_ps + sc + 1, command_ps + sc,
1924 command[sc] = ic; 2053 (command_len - sc) * sizeof(command_ps[0]));
2054 command_ps[sc] = ic;
1925 sc++; 2055 sc++;
1926 /* rewrite from cursor */ 2056 /* rewrite from cursor */
1927 input_end(); 2057 input_end();
@@ -1941,6 +2071,14 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1941#endif 2071#endif
1942 } /* while (1) */ 2072 } /* while (1) */
1943 2073
2074/* Stop bug catching using "command_must_not_be_used" trick */
2075#undef command
2076
2077#if ENABLE_FEATURE_ASSUME_UNICODE
2078 command_len = save_string(command, maxsize - 1);
2079 free(command_ps);
2080#endif
2081
1944 if (command_len > 0) 2082 if (command_len > 0)
1945 remember_in_history(command); 2083 remember_in_history(command);
1946 2084
@@ -1976,7 +2114,7 @@ int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
1976 return strlen(command); 2114 return strlen(command);
1977} 2115}
1978 2116
1979#endif /* FEATURE_COMMAND_EDITING */ 2117#endif /* FEATURE_EDITING */
1980 2118
1981 2119
1982/* 2120/*