diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-07-10 18:40:49 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-07-10 18:40:49 +0200 |
commit | 2e6d4ef695c4be0ff1963e24315bc5bec6c5313b (patch) | |
tree | 25c38a76c639d630b8a6e9c415e4d9e1a5e770d9 /libbb/lineedit.c | |
parent | 860d2bbdda67acfec39cd28e38881633ec4e66d6 (diff) | |
download | busybox-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.c | 332 |
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 | |||
71 | enum { | 97 | enum { |
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 | ||
202 | static 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 | } | ||
210 | static 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... */ | ||
219 | static 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 | ||
231 | static size_t load_string(const char *src, int maxsize) | ||
232 | { | ||
233 | safe_strncpy(command_ps, src, maxsize); | ||
234 | return strlen(command_ps); | ||
235 | } | ||
236 | static 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) | |||
183 | static void cmdedit_set_out_char(int next_char) | 252 | static 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 | ||
979 | static void save_command_ps_at_cur_history(void) | 1053 | static 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 |
1201 | static void | 1284 | static void |
1202 | vi_Word_motion(char *command, int eat) | 1285 | vi_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 | ||
1210 | static void | 1295 | static void |
1211 | vi_word_motion(char *command, int eat) | 1296 | vi_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 | ||
1230 | static void | 1317 | static void |
1231 | vi_End_motion(char *command) | 1318 | vi_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 | ||
1240 | static void | 1329 | static void |
1241 | vi_end_motion(char *command) | 1330 | vi_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 | ||
1262 | static void | 1353 | static void |
1263 | vi_Back_motion(char *command) | 1354 | vi_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 | ||
1271 | static void | 1364 | static void |
1272 | vi_back_motion(char *command) | 1365 | vi_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 | /* |