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
|
# define LAST_SUPPORTED_WCHAR CONFIG_LAST_SUPPORTED_WCHAR
|
||||||
# endif
|
# 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
|
# if LAST_SUPPORTED_WCHAR < 0x590
|
||||||
# undef ENABLE_UNICODE_BIDI_SUPPORT
|
# undef ENABLE_UNICODE_BIDI_SUPPORT
|
||||||
# define ENABLE_UNICODE_BIDI_SUPPORT 0
|
# 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 iswspace(wint_t wc) FAST_FUNC;
|
||||||
int iswalnum(wint_t wc) FAST_FUNC;
|
int iswalnum(wint_t wc) FAST_FUNC;
|
||||||
int iswpunct(wint_t wc) FAST_FUNC;
|
int iswpunct(wint_t wc) FAST_FUNC;
|
||||||
|
int wcwidth(unsigned ucs) FAST_FUNC;
|
||||||
# if ENABLE_UNICODE_BIDI_SUPPORT
|
# if ENABLE_UNICODE_BIDI_SUPPORT
|
||||||
# undef unicode_bidi_isrtl
|
# undef unicode_bidi_isrtl
|
||||||
int unicode_bidi_isrtl(wint_t wc) FAST_FUNC;
|
int unicode_bidi_isrtl(wint_t wc) FAST_FUNC;
|
||||||
|
124
libbb/lineedit.c
124
libbb/lineedit.c
@ -42,14 +42,10 @@
|
|||||||
#include "libbb.h"
|
#include "libbb.h"
|
||||||
#include "unicode.h"
|
#include "unicode.h"
|
||||||
|
|
||||||
/* FIXME: obsolete CONFIG item? */
|
|
||||||
#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
|
|
||||||
|
|
||||||
#ifdef TEST
|
#ifdef TEST
|
||||||
# define ENABLE_FEATURE_EDITING 0
|
# define ENABLE_FEATURE_EDITING 0
|
||||||
# define ENABLE_FEATURE_TAB_COMPLETION 0
|
# define ENABLE_FEATURE_TAB_COMPLETION 0
|
||||||
# define ENABLE_FEATURE_USERNAME_COMPLETION 0
|
# define ENABLE_FEATURE_USERNAME_COMPLETION 0
|
||||||
# define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -97,10 +93,10 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
|
|||||||
|
|
||||||
|
|
||||||
# if ENABLE_UNICODE_PRESERVE_BROKEN
|
# if ENABLE_UNICODE_PRESERVE_BROKEN
|
||||||
# define unicode_mark_inv_wchar(wc) ((wc) | 0x20000000)
|
# define unicode_mark_raw_byte(wc) ((wc) | 0x20000000)
|
||||||
# define unicode_is_inv_wchar(wc) ((wc) & 0x20000000)
|
# define unicode_is_raw_byte(wc) ((wc) & 0x20000000)
|
||||||
# else
|
# else
|
||||||
# define unicode_is_inv_wchar(wc) 0
|
# define unicode_is_raw_byte(wc) 0
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
|
||||||
@ -240,7 +236,7 @@ static unsigned save_string(char *dst, unsigned maxsize)
|
|||||||
wchar_t wc;
|
wchar_t wc;
|
||||||
int n = srcpos;
|
int n = srcpos;
|
||||||
while ((wc = command_ps[srcpos]) != 0
|
while ((wc = command_ps[srcpos]) != 0
|
||||||
&& !unicode_is_inv_wchar(wc)
|
&& !unicode_is_raw_byte(wc)
|
||||||
) {
|
) {
|
||||||
srcpos++;
|
srcpos++;
|
||||||
}
|
}
|
||||||
@ -269,15 +265,45 @@ static void BB_PUTCHAR(wchar_t c)
|
|||||||
mbstate_t mbst = { 0 };
|
mbstate_t mbst = { 0 };
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
if (unicode_is_inv_wchar(c))
|
|
||||||
c = CONFIG_SUBST_WCHAR;
|
|
||||||
len = wcrtomb(buf, c, &mbst);
|
len = wcrtomb(buf, c, &mbst);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
fputs(buf, stdout);
|
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)
|
static size_t load_string(const char *src, int maxsize)
|
||||||
{
|
{
|
||||||
safe_strncpy(command_ps, src, maxsize);
|
safe_strncpy(command_ps, src, maxsize);
|
||||||
@ -290,6 +316,8 @@ static void save_string(char *dst, unsigned maxsize)
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
# define BB_PUTCHAR(c) bb_putchar(c)
|
# define BB_PUTCHAR(c) bb_putchar(c)
|
||||||
|
/* Should never be called: */
|
||||||
|
int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -300,6 +328,8 @@ static void save_string(char *dst, unsigned maxsize)
|
|||||||
static void put_cur_glyph_and_inc_cursor(void)
|
static void put_cur_glyph_and_inc_cursor(void)
|
||||||
{
|
{
|
||||||
CHAR_T c = command_ps[cursor];
|
CHAR_T c = command_ps[cursor];
|
||||||
|
unsigned width = 0;
|
||||||
|
int ofs_to_right;
|
||||||
|
|
||||||
if (c == BB_NUL) {
|
if (c == BB_NUL) {
|
||||||
/* erase character after end of input string */
|
/* erase character after end of input string */
|
||||||
@ -307,28 +337,23 @@ static void put_cur_glyph_and_inc_cursor(void)
|
|||||||
} else {
|
} else {
|
||||||
/* advance cursor only if we aren't at the end yet */
|
/* advance cursor only if we aren't at the end yet */
|
||||||
cursor++;
|
cursor++;
|
||||||
cmdedit_x++;
|
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
|
ofs_to_right = cmdedit_x - cmdedit_termw;
|
||||||
/* Display non-printable characters in reverse */
|
if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
|
||||||
if (!BB_isprint(c)) {
|
/* c fits on this line */
|
||||||
if (c >= 128)
|
|
||||||
c -= 128;
|
|
||||||
if (c < ' ')
|
|
||||||
c += '@';
|
|
||||||
if (c == 127)
|
|
||||||
c = '?';
|
|
||||||
printf("\033[7m%c\033[0m", c);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
BB_PUTCHAR(c);
|
BB_PUTCHAR(c);
|
||||||
}
|
}
|
||||||
if (cmdedit_x >= cmdedit_termw) {
|
|
||||||
/* terminal is scrolled down */
|
if (ofs_to_right >= 0) {
|
||||||
cmdedit_y++;
|
/* we go to the next line */
|
||||||
cmdedit_x = 0;
|
|
||||||
#if HACK_FOR_WRONG_WIDTH
|
#if HACK_FOR_WRONG_WIDTH
|
||||||
/* This works better if our idea of term width is wrong
|
/* This works better if our idea of term width is wrong
|
||||||
* and it is actually wider (often happens on serial lines).
|
* 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(c);
|
||||||
bb_putchar('\b');
|
bb_putchar('\b');
|
||||||
#endif
|
#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)
|
if (num > cursor)
|
||||||
num = cursor;
|
num = cursor;
|
||||||
if (!num)
|
if (num == 0)
|
||||||
return;
|
return;
|
||||||
cursor -= num;
|
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) {
|
if (cmdedit_x >= num) {
|
||||||
cmdedit_x -= num;
|
cmdedit_x -= num;
|
||||||
if (num <= 4) {
|
if (num <= 4) {
|
||||||
@ -412,6 +457,8 @@ static void input_backward(unsigned num)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Need to go one or more lines up */
|
/* 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;
|
num -= cmdedit_x;
|
||||||
{
|
{
|
||||||
unsigned w = cmdedit_termw; /* volatile var */
|
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 ' ' */
|
/* 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] == '\\') {
|
if (matchBuf[i] == '\\') {
|
||||||
collapse_pos(j, j + 1);
|
collapse_pos(j, j + 1);
|
||||||
int_buf[j] |= QUOT;
|
int_buf[j] |= QUOT;
|
||||||
i++;
|
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' */
|
/* mask "symbols" or 'symbols' */
|
||||||
c2 = 0;
|
c2 = 0;
|
||||||
for (i = 0; int_buf[i]; i++) {
|
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
|
# if !ENABLE_UNICODE_PRESERVE_BROKEN
|
||||||
ic = CONFIG_SUBST_WCHAR;
|
ic = CONFIG_SUBST_WCHAR;
|
||||||
# else
|
# else
|
||||||
ic = unicode_mark_inv_wchar(unicode_buf[0]);
|
ic = unicode_mark_raw_byte(unicode_buf[0]);
|
||||||
# endif
|
# endif
|
||||||
} else {
|
} else {
|
||||||
/* Valid unicode char, return its code */
|
/* Valid unicode char, return its code */
|
||||||
@ -2384,9 +2423,6 @@ int main(int argc, char **argv)
|
|||||||
"% ";
|
"% ";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
|
|
||||||
setlocale(LC_ALL, "");
|
|
||||||
#endif
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int l;
|
int l;
|
||||||
l = read_line_input(prompt, buff);
|
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
|
* This implementation assumes that wchar_t characters are encoded
|
||||||
* in ISO 10646.
|
* in ISO 10646.
|
||||||
*/
|
*/
|
||||||
static int wcwidth(unsigned ucs)
|
int FAST_FUNC wcwidth(unsigned ucs)
|
||||||
{
|
{
|
||||||
# if LAST_SUPPORTED_WCHAR >= 0x300
|
# if LAST_SUPPORTED_WCHAR >= 0x300
|
||||||
/* sorted list of non-overlapping intervals of non-spacing characters */
|
/* sorted list of non-overlapping intervals of non-spacing characters */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user