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>
This commit is contained in:
parent
0b7412e66b
commit
b8909c52fe
@ -35,6 +35,16 @@ enum {
|
||||
# define LAST_SUPPORTED_WCHAR CONFIG_LAST_SUPPORTED_WCHAR
|
||||
# endif
|
||||
|
||||
# if LAST_SUPPORTED_WCHAR < 0x300
|
||||
# undef ENABLE_UNICODE_COMBINING_WCHARS
|
||||
# define ENABLE_UNICODE_COMBINING_WCHARS 0
|
||||
# endif
|
||||
|
||||
# if LAST_SUPPORTED_WCHAR < 0x1100
|
||||
# undef ENABLE_UNICODE_WIDE_WCHARS
|
||||
# define ENABLE_UNICODE_WIDE_WCHARS 0
|
||||
# endif
|
||||
|
||||
# if LAST_SUPPORTED_WCHAR < 0x590
|
||||
# undef ENABLE_UNICODE_BIDI_SUPPORT
|
||||
# define ENABLE_UNICODE_BIDI_SUPPORT 0
|
||||
@ -92,6 +102,7 @@ size_t wcrtomb(char *s, wchar_t wc, mbstate_t *ps) FAST_FUNC;
|
||||
int iswspace(wint_t wc) FAST_FUNC;
|
||||
int iswalnum(wint_t wc) FAST_FUNC;
|
||||
int iswpunct(wint_t wc) FAST_FUNC;
|
||||
int wcwidth(unsigned ucs) FAST_FUNC;
|
||||
# if ENABLE_UNICODE_BIDI_SUPPORT
|
||||
# undef unicode_bidi_isrtl
|
||||
int unicode_bidi_isrtl(wint_t wc) FAST_FUNC;
|
||||
|
122
libbb/lineedit.c
122
libbb/lineedit.c
@ -42,14 +42,10 @@
|
||||
#include "libbb.h"
|
||||
#include "unicode.h"
|
||||
|
||||
/* FIXME: obsolete CONFIG item? */
|
||||
#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
|
||||
|
||||
#ifdef TEST
|
||||
# define ENABLE_FEATURE_EDITING 0
|
||||
# define ENABLE_FEATURE_TAB_COMPLETION 0
|
||||
# define ENABLE_FEATURE_USERNAME_COMPLETION 0
|
||||
# define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
|
||||
#endif
|
||||
|
||||
|
||||
@ -97,10 +93,10 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
|
||||
|
||||
|
||||
# if ENABLE_UNICODE_PRESERVE_BROKEN
|
||||
# define unicode_mark_inv_wchar(wc) ((wc) | 0x20000000)
|
||||
# define unicode_is_inv_wchar(wc) ((wc) & 0x20000000)
|
||||
# define unicode_mark_raw_byte(wc) ((wc) | 0x20000000)
|
||||
# define unicode_is_raw_byte(wc) ((wc) & 0x20000000)
|
||||
# else
|
||||
# define unicode_is_inv_wchar(wc) 0
|
||||
# define unicode_is_raw_byte(wc) 0
|
||||
# endif
|
||||
|
||||
|
||||
@ -240,7 +236,7 @@ static unsigned save_string(char *dst, unsigned maxsize)
|
||||
wchar_t wc;
|
||||
int n = srcpos;
|
||||
while ((wc = command_ps[srcpos]) != 0
|
||||
&& !unicode_is_inv_wchar(wc)
|
||||
&& !unicode_is_raw_byte(wc)
|
||||
) {
|
||||
srcpos++;
|
||||
}
|
||||
@ -269,15 +265,45 @@ static void BB_PUTCHAR(wchar_t c)
|
||||
mbstate_t mbst = { 0 };
|
||||
ssize_t len;
|
||||
|
||||
if (unicode_is_inv_wchar(c))
|
||||
c = CONFIG_SUBST_WCHAR;
|
||||
len = wcrtomb(buf, c, &mbst);
|
||||
if (len > 0) {
|
||||
buf[len] = '\0';
|
||||
fputs(buf, stdout);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
|
||||
static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc)
|
||||
# else
|
||||
static wchar_t adjust_width_and_validate_wc(wchar_t wc)
|
||||
# define adjust_width_and_validate_wc(width_adj, wc) \
|
||||
((*(width_adj))++, adjust_width_and_validate_wc(wc))
|
||||
# endif
|
||||
{
|
||||
int w = 1;
|
||||
|
||||
if (unicode_status == UNICODE_ON) {
|
||||
if (unicode_is_raw_byte(wc)
|
||||
|| (CONFIG_LAST_SUPPORTED_WCHAR && wc > CONFIG_LAST_SUPPORTED_WCHAR)
|
||||
) {
|
||||
goto subst;
|
||||
}
|
||||
w = wcwidth(wc);
|
||||
if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0)
|
||||
|| (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
|
||||
|| (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
|
||||
) {
|
||||
subst:
|
||||
w = 1;
|
||||
wc = CONFIG_SUBST_WCHAR;
|
||||
}
|
||||
}
|
||||
|
||||
# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
|
||||
*width_adj += w;
|
||||
#endif
|
||||
return wc;
|
||||
}
|
||||
#else /* !UNICODE */
|
||||
static size_t load_string(const char *src, int maxsize)
|
||||
{
|
||||
safe_strncpy(command_ps, src, maxsize);
|
||||
@ -290,6 +316,8 @@ static void save_string(char *dst, unsigned maxsize)
|
||||
}
|
||||
# endif
|
||||
# define BB_PUTCHAR(c) bb_putchar(c)
|
||||
/* Should never be called: */
|
||||
int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
|
||||
#endif
|
||||
|
||||
|
||||
@ -300,6 +328,8 @@ static void save_string(char *dst, unsigned maxsize)
|
||||
static void put_cur_glyph_and_inc_cursor(void)
|
||||
{
|
||||
CHAR_T c = command_ps[cursor];
|
||||
unsigned width = 0;
|
||||
int ofs_to_right;
|
||||
|
||||
if (c == BB_NUL) {
|
||||
/* erase character after end of input string */
|
||||
@ -307,28 +337,23 @@ static void put_cur_glyph_and_inc_cursor(void)
|
||||
} else {
|
||||
/* advance cursor only if we aren't at the end yet */
|
||||
cursor++;
|
||||
if (unicode_status == UNICODE_ON) {
|
||||
IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
|
||||
c = adjust_width_and_validate_wc(&cmdedit_x, c);
|
||||
IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
|
||||
} else {
|
||||
cmdedit_x++;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
|
||||
/* Display non-printable characters in reverse */
|
||||
if (!BB_isprint(c)) {
|
||||
if (c >= 128)
|
||||
c -= 128;
|
||||
if (c < ' ')
|
||||
c += '@';
|
||||
if (c == 127)
|
||||
c = '?';
|
||||
printf("\033[7m%c\033[0m", c);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ofs_to_right = cmdedit_x - cmdedit_termw;
|
||||
if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
|
||||
/* c fits on this line */
|
||||
BB_PUTCHAR(c);
|
||||
}
|
||||
if (cmdedit_x >= cmdedit_termw) {
|
||||
/* terminal is scrolled down */
|
||||
cmdedit_y++;
|
||||
cmdedit_x = 0;
|
||||
|
||||
if (ofs_to_right >= 0) {
|
||||
/* we go to the next line */
|
||||
#if HACK_FOR_WRONG_WIDTH
|
||||
/* This works better if our idea of term width is wrong
|
||||
* and it is actually wider (often happens on serial lines).
|
||||
@ -351,6 +376,14 @@ static void put_cur_glyph_and_inc_cursor(void)
|
||||
BB_PUTCHAR(c);
|
||||
bb_putchar('\b');
|
||||
#endif
|
||||
cmdedit_y++;
|
||||
if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
|
||||
width = 0;
|
||||
} else { /* ofs_to_right > 0 */
|
||||
/* wide char c didn't fit on prev line */
|
||||
BB_PUTCHAR(c);
|
||||
}
|
||||
cmdedit_x = width;
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,10 +422,22 @@ static void input_backward(unsigned num)
|
||||
|
||||
if (num > cursor)
|
||||
num = cursor;
|
||||
if (!num)
|
||||
if (num == 0)
|
||||
return;
|
||||
cursor -= num;
|
||||
|
||||
if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS)
|
||||
&& unicode_status == UNICODE_ON
|
||||
) {
|
||||
/* correct NUM to be equal to _screen_ width */
|
||||
int n = num;
|
||||
num = 0;
|
||||
while (--n >= 0)
|
||||
adjust_width_and_validate_wc(&num, command_ps[cursor + n]);
|
||||
if (num == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmdedit_x >= num) {
|
||||
cmdedit_x -= num;
|
||||
if (num <= 4) {
|
||||
@ -412,6 +457,8 @@ static void input_backward(unsigned num)
|
||||
}
|
||||
|
||||
/* Need to go one or more lines up */
|
||||
//FIXME: this does not work correctly if prev line has one "unfilled" screen position
|
||||
//caused by wide unicode char not fitting in that one screen position.
|
||||
num -= cmdedit_x;
|
||||
{
|
||||
unsigned w = cmdedit_termw; /* volatile var */
|
||||
@ -765,21 +812,13 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes)
|
||||
}
|
||||
|
||||
/* mask \+symbol and convert '\t' to ' ' */
|
||||
for (i = j = 0; matchBuf[i]; i++, j++)
|
||||
for (i = j = 0; matchBuf[i]; i++, j++) {
|
||||
if (matchBuf[i] == '\\') {
|
||||
collapse_pos(j, j + 1);
|
||||
int_buf[j] |= QUOT;
|
||||
i++;
|
||||
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
|
||||
if (matchBuf[i] == '\t') /* algorithm equivalent */
|
||||
int_buf[j] = ' ' | QUOT;
|
||||
#endif
|
||||
}
|
||||
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
|
||||
else if (matchBuf[i] == '\t')
|
||||
int_buf[j] = ' ';
|
||||
#endif
|
||||
|
||||
}
|
||||
/* mask "symbols" or 'symbols' */
|
||||
c2 = 0;
|
||||
for (i = 0; int_buf[i]; i++) {
|
||||
@ -1774,7 +1813,7 @@ static int lineedit_read_key(char *read_key_buffer)
|
||||
# if !ENABLE_UNICODE_PRESERVE_BROKEN
|
||||
ic = CONFIG_SUBST_WCHAR;
|
||||
# else
|
||||
ic = unicode_mark_inv_wchar(unicode_buf[0]);
|
||||
ic = unicode_mark_raw_byte(unicode_buf[0]);
|
||||
# endif
|
||||
} else {
|
||||
/* Valid unicode char, return its code */
|
||||
@ -2384,9 +2423,6 @@ int main(int argc, char **argv)
|
||||
"% ";
|
||||
#endif
|
||||
|
||||
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
while (1) {
|
||||
int l;
|
||||
l = read_line_input(prompt, buff);
|
||||
|
@ -418,7 +418,7 @@ static int in_uint16_table(unsigned ucs, const uint16_t *table, unsigned max)
|
||||
* This implementation assumes that wchar_t characters are encoded
|
||||
* in ISO 10646.
|
||||
*/
|
||||
static int wcwidth(unsigned ucs)
|
||||
int FAST_FUNC wcwidth(unsigned ucs)
|
||||
{
|
||||
# if LAST_SUPPORTED_WCHAR >= 0x300
|
||||
/* sorted list of non-overlapping intervals of non-spacing characters */
|
||||
|
Loading…
Reference in New Issue
Block a user