diff options
author | Tomas Heinrich <heinrich.tomas@gmail.com> | 2010-05-16 20:46:53 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-16 20:46:53 +0200 |
commit | b8909c52fe850d2731326534822164c2f5258bf5 (patch) | |
tree | 2e33d71a3caf8861b2380d8e07841dd0eab09a8a | |
parent | 0b7412e66b3d702557a2bf214752ff68d80fcda3 (diff) | |
download | busybox-w32-b8909c52fe850d2731326534822164c2f5258bf5.tar.gz busybox-w32-b8909c52fe850d2731326534822164c2f5258bf5.tar.bz2 busybox-w32-b8909c52fe850d2731326534822164c2f5258bf5.zip |
lineedit: partially fix wide and combining chars editing
Signed-off-by: Tomas Heinrich <heinrich.tomas@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | include/unicode.h | 11 | ||||
-rw-r--r-- | libbb/lineedit.c | 124 | ||||
-rw-r--r-- | libbb/unicode.c | 2 |
3 files changed, 92 insertions, 45 deletions
diff --git a/include/unicode.h b/include/unicode.h index 4e2927297..747026abe 100644 --- a/include/unicode.h +++ b/include/unicode.h | |||
@@ -35,6 +35,16 @@ enum { | |||
35 | # define LAST_SUPPORTED_WCHAR CONFIG_LAST_SUPPORTED_WCHAR | 35 | # define LAST_SUPPORTED_WCHAR CONFIG_LAST_SUPPORTED_WCHAR |
36 | # endif | 36 | # endif |
37 | 37 | ||
38 | # if LAST_SUPPORTED_WCHAR < 0x300 | ||
39 | # undef ENABLE_UNICODE_COMBINING_WCHARS | ||
40 | # define ENABLE_UNICODE_COMBINING_WCHARS 0 | ||
41 | # endif | ||
42 | |||
43 | # if LAST_SUPPORTED_WCHAR < 0x1100 | ||
44 | # undef ENABLE_UNICODE_WIDE_WCHARS | ||
45 | # define ENABLE_UNICODE_WIDE_WCHARS 0 | ||
46 | # endif | ||
47 | |||
38 | # if LAST_SUPPORTED_WCHAR < 0x590 | 48 | # if LAST_SUPPORTED_WCHAR < 0x590 |
39 | # undef ENABLE_UNICODE_BIDI_SUPPORT | 49 | # undef ENABLE_UNICODE_BIDI_SUPPORT |
40 | # define ENABLE_UNICODE_BIDI_SUPPORT 0 | 50 | # define ENABLE_UNICODE_BIDI_SUPPORT 0 |
@@ -92,6 +102,7 @@ size_t wcrtomb(char *s, wchar_t wc, mbstate_t *ps) FAST_FUNC; | |||
92 | int iswspace(wint_t wc) FAST_FUNC; | 102 | int iswspace(wint_t wc) FAST_FUNC; |
93 | int iswalnum(wint_t wc) FAST_FUNC; | 103 | int iswalnum(wint_t wc) FAST_FUNC; |
94 | int iswpunct(wint_t wc) FAST_FUNC; | 104 | int iswpunct(wint_t wc) FAST_FUNC; |
105 | int wcwidth(unsigned ucs) FAST_FUNC; | ||
95 | # if ENABLE_UNICODE_BIDI_SUPPORT | 106 | # if ENABLE_UNICODE_BIDI_SUPPORT |
96 | # undef unicode_bidi_isrtl | 107 | # undef unicode_bidi_isrtl |
97 | int unicode_bidi_isrtl(wint_t wc) FAST_FUNC; | 108 | int unicode_bidi_isrtl(wint_t wc) FAST_FUNC; |
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 7fffe7ba2..9f2d65717 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -42,14 +42,10 @@ | |||
42 | #include "libbb.h" | 42 | #include "libbb.h" |
43 | #include "unicode.h" | 43 | #include "unicode.h" |
44 | 44 | ||
45 | /* FIXME: obsolete CONFIG item? */ | ||
46 | #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 | ||
47 | |||
48 | #ifdef TEST | 45 | #ifdef TEST |
49 | # define ENABLE_FEATURE_EDITING 0 | 46 | # define ENABLE_FEATURE_EDITING 0 |
50 | # define ENABLE_FEATURE_TAB_COMPLETION 0 | 47 | # define ENABLE_FEATURE_TAB_COMPLETION 0 |
51 | # define ENABLE_FEATURE_USERNAME_COMPLETION 0 | 48 | # define ENABLE_FEATURE_USERNAME_COMPLETION 0 |
52 | # define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 | ||
53 | #endif | 49 | #endif |
54 | 50 | ||
55 | 51 | ||
@@ -97,10 +93,10 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } | |||
97 | 93 | ||
98 | 94 | ||
99 | # if ENABLE_UNICODE_PRESERVE_BROKEN | 95 | # if ENABLE_UNICODE_PRESERVE_BROKEN |
100 | # define unicode_mark_inv_wchar(wc) ((wc) | 0x20000000) | 96 | # define unicode_mark_raw_byte(wc) ((wc) | 0x20000000) |
101 | # define unicode_is_inv_wchar(wc) ((wc) & 0x20000000) | 97 | # define unicode_is_raw_byte(wc) ((wc) & 0x20000000) |
102 | # else | 98 | # else |
103 | # define unicode_is_inv_wchar(wc) 0 | 99 | # define unicode_is_raw_byte(wc) 0 |
104 | # endif | 100 | # endif |
105 | 101 | ||
106 | 102 | ||
@@ -240,7 +236,7 @@ static unsigned save_string(char *dst, unsigned maxsize) | |||
240 | wchar_t wc; | 236 | wchar_t wc; |
241 | int n = srcpos; | 237 | int n = srcpos; |
242 | while ((wc = command_ps[srcpos]) != 0 | 238 | while ((wc = command_ps[srcpos]) != 0 |
243 | && !unicode_is_inv_wchar(wc) | 239 | && !unicode_is_raw_byte(wc) |
244 | ) { | 240 | ) { |
245 | srcpos++; | 241 | srcpos++; |
246 | } | 242 | } |
@@ -269,15 +265,45 @@ static void BB_PUTCHAR(wchar_t c) | |||
269 | mbstate_t mbst = { 0 }; | 265 | mbstate_t mbst = { 0 }; |
270 | ssize_t len; | 266 | ssize_t len; |
271 | 267 | ||
272 | if (unicode_is_inv_wchar(c)) | ||
273 | c = CONFIG_SUBST_WCHAR; | ||
274 | len = wcrtomb(buf, c, &mbst); | 268 | len = wcrtomb(buf, c, &mbst); |
275 | if (len > 0) { | 269 | if (len > 0) { |
276 | buf[len] = '\0'; | 270 | buf[len] = '\0'; |
277 | fputs(buf, stdout); | 271 | fputs(buf, stdout); |
278 | } | 272 | } |
279 | } | 273 | } |
280 | #else | 274 | # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS |
275 | static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc) | ||
276 | # else | ||
277 | static wchar_t adjust_width_and_validate_wc(wchar_t wc) | ||
278 | # define adjust_width_and_validate_wc(width_adj, wc) \ | ||
279 | ((*(width_adj))++, adjust_width_and_validate_wc(wc)) | ||
280 | # endif | ||
281 | { | ||
282 | int w = 1; | ||
283 | |||
284 | if (unicode_status == UNICODE_ON) { | ||
285 | if (unicode_is_raw_byte(wc) | ||
286 | || (CONFIG_LAST_SUPPORTED_WCHAR && wc > CONFIG_LAST_SUPPORTED_WCHAR) | ||
287 | ) { | ||
288 | goto subst; | ||
289 | } | ||
290 | w = wcwidth(wc); | ||
291 | if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0) | ||
292 | || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0) | ||
293 | || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1) | ||
294 | ) { | ||
295 | subst: | ||
296 | w = 1; | ||
297 | wc = CONFIG_SUBST_WCHAR; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS | ||
302 | *width_adj += w; | ||
303 | #endif | ||
304 | return wc; | ||
305 | } | ||
306 | #else /* !UNICODE */ | ||
281 | static size_t load_string(const char *src, int maxsize) | 307 | static size_t load_string(const char *src, int maxsize) |
282 | { | 308 | { |
283 | safe_strncpy(command_ps, src, maxsize); | 309 | safe_strncpy(command_ps, src, maxsize); |
@@ -290,6 +316,8 @@ static void save_string(char *dst, unsigned maxsize) | |||
290 | } | 316 | } |
291 | # endif | 317 | # endif |
292 | # define BB_PUTCHAR(c) bb_putchar(c) | 318 | # define BB_PUTCHAR(c) bb_putchar(c) |
319 | /* Should never be called: */ | ||
320 | int adjust_width_and_validate_wc(unsigned *width_adj, int wc); | ||
293 | #endif | 321 | #endif |
294 | 322 | ||
295 | 323 | ||
@@ -300,6 +328,8 @@ static void save_string(char *dst, unsigned maxsize) | |||
300 | static void put_cur_glyph_and_inc_cursor(void) | 328 | static void put_cur_glyph_and_inc_cursor(void) |
301 | { | 329 | { |
302 | CHAR_T c = command_ps[cursor]; | 330 | CHAR_T c = command_ps[cursor]; |
331 | unsigned width = 0; | ||
332 | int ofs_to_right; | ||
303 | 333 | ||
304 | if (c == BB_NUL) { | 334 | if (c == BB_NUL) { |
305 | /* erase character after end of input string */ | 335 | /* erase character after end of input string */ |
@@ -307,28 +337,23 @@ static void put_cur_glyph_and_inc_cursor(void) | |||
307 | } else { | 337 | } else { |
308 | /* advance cursor only if we aren't at the end yet */ | 338 | /* advance cursor only if we aren't at the end yet */ |
309 | cursor++; | 339 | cursor++; |
310 | cmdedit_x++; | 340 | if (unicode_status == UNICODE_ON) { |
341 | IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;) | ||
342 | c = adjust_width_and_validate_wc(&cmdedit_x, c); | ||
343 | IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;) | ||
344 | } else { | ||
345 | cmdedit_x++; | ||
346 | } | ||
311 | } | 347 | } |
312 | 348 | ||
313 | #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT | 349 | ofs_to_right = cmdedit_x - cmdedit_termw; |
314 | /* Display non-printable characters in reverse */ | 350 | if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) { |
315 | if (!BB_isprint(c)) { | 351 | /* c fits on this line */ |
316 | if (c >= 128) | ||
317 | c -= 128; | ||
318 | if (c < ' ') | ||
319 | c += '@'; | ||
320 | if (c == 127) | ||
321 | c = '?'; | ||
322 | printf("\033[7m%c\033[0m", c); | ||
323 | } else | ||
324 | #endif | ||
325 | { | ||
326 | BB_PUTCHAR(c); | 352 | BB_PUTCHAR(c); |
327 | } | 353 | } |
328 | if (cmdedit_x >= cmdedit_termw) { | 354 | |
329 | /* terminal is scrolled down */ | 355 | if (ofs_to_right >= 0) { |
330 | cmdedit_y++; | 356 | /* we go to the next line */ |
331 | cmdedit_x = 0; | ||
332 | #if HACK_FOR_WRONG_WIDTH | 357 | #if HACK_FOR_WRONG_WIDTH |
333 | /* This works better if our idea of term width is wrong | 358 | /* This works better if our idea of term width is wrong |
334 | * and it is actually wider (often happens on serial lines). | 359 | * and it is actually wider (often happens on serial lines). |
@@ -351,6 +376,14 @@ static void put_cur_glyph_and_inc_cursor(void) | |||
351 | BB_PUTCHAR(c); | 376 | BB_PUTCHAR(c); |
352 | bb_putchar('\b'); | 377 | bb_putchar('\b'); |
353 | #endif | 378 | #endif |
379 | cmdedit_y++; | ||
380 | if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { | ||
381 | width = 0; | ||
382 | } else { /* ofs_to_right > 0 */ | ||
383 | /* wide char c didn't fit on prev line */ | ||
384 | BB_PUTCHAR(c); | ||
385 | } | ||
386 | cmdedit_x = width; | ||
354 | } | 387 | } |
355 | } | 388 | } |
356 | 389 | ||
@@ -389,10 +422,22 @@ static void input_backward(unsigned num) | |||
389 | 422 | ||
390 | if (num > cursor) | 423 | if (num > cursor) |
391 | num = cursor; | 424 | num = cursor; |
392 | if (!num) | 425 | if (num == 0) |
393 | return; | 426 | return; |
394 | cursor -= num; | 427 | cursor -= num; |
395 | 428 | ||
429 | if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS) | ||
430 | && unicode_status == UNICODE_ON | ||
431 | ) { | ||
432 | /* correct NUM to be equal to _screen_ width */ | ||
433 | int n = num; | ||
434 | num = 0; | ||
435 | while (--n >= 0) | ||
436 | adjust_width_and_validate_wc(&num, command_ps[cursor + n]); | ||
437 | if (num == 0) | ||
438 | return; | ||
439 | } | ||
440 | |||
396 | if (cmdedit_x >= num) { | 441 | if (cmdedit_x >= num) { |
397 | cmdedit_x -= num; | 442 | cmdedit_x -= num; |
398 | if (num <= 4) { | 443 | if (num <= 4) { |
@@ -412,6 +457,8 @@ static void input_backward(unsigned num) | |||
412 | } | 457 | } |
413 | 458 | ||
414 | /* Need to go one or more lines up */ | 459 | /* Need to go one or more lines up */ |
460 | //FIXME: this does not work correctly if prev line has one "unfilled" screen position | ||
461 | //caused by wide unicode char not fitting in that one screen position. | ||
415 | num -= cmdedit_x; | 462 | num -= cmdedit_x; |
416 | { | 463 | { |
417 | unsigned w = cmdedit_termw; /* volatile var */ | 464 | unsigned w = cmdedit_termw; /* volatile var */ |
@@ -765,21 +812,13 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | |||
765 | } | 812 | } |
766 | 813 | ||
767 | /* mask \+symbol and convert '\t' to ' ' */ | 814 | /* mask \+symbol and convert '\t' to ' ' */ |
768 | for (i = j = 0; matchBuf[i]; i++, j++) | 815 | for (i = j = 0; matchBuf[i]; i++, j++) { |
769 | if (matchBuf[i] == '\\') { | 816 | if (matchBuf[i] == '\\') { |
770 | collapse_pos(j, j + 1); | 817 | collapse_pos(j, j + 1); |
771 | int_buf[j] |= QUOT; | 818 | int_buf[j] |= QUOT; |
772 | i++; | 819 | i++; |
773 | #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
774 | if (matchBuf[i] == '\t') /* algorithm equivalent */ | ||
775 | int_buf[j] = ' ' | QUOT; | ||
776 | #endif | ||
777 | } | 820 | } |
778 | #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT | 821 | } |
779 | else if (matchBuf[i] == '\t') | ||
780 | int_buf[j] = ' '; | ||
781 | #endif | ||
782 | |||
783 | /* mask "symbols" or 'symbols' */ | 822 | /* mask "symbols" or 'symbols' */ |
784 | c2 = 0; | 823 | c2 = 0; |
785 | for (i = 0; int_buf[i]; i++) { | 824 | for (i = 0; int_buf[i]; i++) { |
@@ -1774,7 +1813,7 @@ static int lineedit_read_key(char *read_key_buffer) | |||
1774 | # if !ENABLE_UNICODE_PRESERVE_BROKEN | 1813 | # if !ENABLE_UNICODE_PRESERVE_BROKEN |
1775 | ic = CONFIG_SUBST_WCHAR; | 1814 | ic = CONFIG_SUBST_WCHAR; |
1776 | # else | 1815 | # else |
1777 | ic = unicode_mark_inv_wchar(unicode_buf[0]); | 1816 | ic = unicode_mark_raw_byte(unicode_buf[0]); |
1778 | # endif | 1817 | # endif |
1779 | } else { | 1818 | } else { |
1780 | /* Valid unicode char, return its code */ | 1819 | /* Valid unicode char, return its code */ |
@@ -2384,9 +2423,6 @@ int main(int argc, char **argv) | |||
2384 | "% "; | 2423 | "% "; |
2385 | #endif | 2424 | #endif |
2386 | 2425 | ||
2387 | #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
2388 | setlocale(LC_ALL, ""); | ||
2389 | #endif | ||
2390 | while (1) { | 2426 | while (1) { |
2391 | int l; | 2427 | int l; |
2392 | l = read_line_input(prompt, buff); | 2428 | l = read_line_input(prompt, buff); |
diff --git a/libbb/unicode.c b/libbb/unicode.c index d1c6167c7..eb0ea6129 100644 --- a/libbb/unicode.c +++ b/libbb/unicode.c | |||
@@ -418,7 +418,7 @@ static int in_uint16_table(unsigned ucs, const uint16_t *table, unsigned max) | |||
418 | * This implementation assumes that wchar_t characters are encoded | 418 | * This implementation assumes that wchar_t characters are encoded |
419 | * in ISO 10646. | 419 | * in ISO 10646. |
420 | */ | 420 | */ |
421 | static int wcwidth(unsigned ucs) | 421 | int FAST_FUNC wcwidth(unsigned ucs) |
422 | { | 422 | { |
423 | # if LAST_SUPPORTED_WCHAR >= 0x300 | 423 | # if LAST_SUPPORTED_WCHAR >= 0x300 |
424 | /* sorted list of non-overlapping intervals of non-spacing characters */ | 424 | /* sorted list of non-overlapping intervals of non-spacing characters */ |