From b9345b4f9c44ea7dea994cb3b06d639f9bf66a8d Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 9 Feb 2013 13:46:52 +0100 Subject: Support wide characters in the shell through wcwidth() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is one remaining bug where ttsl_drv's state ends up inconsistent with the terminal own state; when a wide character is entered on the last column of the terminal. Reported-by: Loïc Hoguin --- erts/configure.in | 11 +++++++++++ erts/emulator/drivers/unix/ttsl_drv.c | 37 ++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'erts') diff --git a/erts/configure.in b/erts/configure.in index 2ee907b6e4..f96af03eba 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1342,6 +1342,17 @@ if test "x$TERMCAP_LIB" != "x"; then AC_DEFINE(HAVE_TERMCAP, 1, [Define if termcap functions exists]) fi +if test "X$host" != "Xwin32"; then + AC_MSG_CHECKING(for wcwidth) + AC_TRY_LINK([#include ], [wcwidth(0);], + have_wcwidth=yes, have_wcwidth=no) + if test $have_wcwidth = yes; then + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_WCWIDTH, [1], + [Define to 1 if you have a `wcwidth' function.]) + fi +fi + dnl ------------- dnl zlib dnl ------------- diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 2c851101b6..1db5425246 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -42,6 +42,9 @@ static ErlDrvData ttysl_start(ErlDrvPort, char*); #include #include #include +#ifdef HAVE_WCWIDTH +#include +#endif #ifdef HAVE_FCNTL_H #include #endif @@ -95,6 +98,9 @@ static int lpos; /* The current "cursor position" in the line buf */ #define CONTROL_TAG 0x10000000U /* Control character, value in first position */ #define ESCAPED_TAG 0x01000000U /* Escaped character, value in first position */ +#ifdef HAVE_WCWIDTH +#define WIDE_TAG 0x02000000U /* Wide character, value in first position */ +#endif #define TAG_MASK 0xFF000000U #define MAXSIZE (1 << 16) @@ -597,8 +603,16 @@ static int check_buf_size(byte *s, int n) } if (utf8_mode) { /* That is, terminal is UTF8 compliant */ if (ch >= 128 || isprint(ch)) { +#ifdef HAVE_WCWIDTH + int width; +#endif DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch)); - size++; /* Buffer contains wide characters... */ + size++; +#ifdef HAVE_WCWIDTH + if ((width = wcwidth(ch)) > 1) { + size += width - 1; + } +#endif } else if (ch == '\t') { size += 8; } else { @@ -868,12 +882,22 @@ static int step_over_chars(int n) end = lbuf + llen; c = lbuf + lpos; for ( ; n > 0 && c < end; --n) { +#ifdef HAVE_WCWIDTH + while (*c & WIDE_TAG) { + c++; + } +#endif c++; while (c < end && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0)) c++; } for ( ; n < 0 && c > beg; n++) { --c; +#ifdef HAVE_WCWIDTH + while (c > beg + 1 && (c[-1] & WIDE_TAG)) { + --c; + } +#endif while (c > beg && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0)) --c; } @@ -899,6 +923,15 @@ static int insert_buf(byte *s, int n) ++pos; } if ((utf8_mode && (ch >= 128 || isprint(ch))) || (ch <= 255 && isprint(ch))) { +#ifdef HAVE_WCWIDTH + int width; + if ((width = wcwidth(ch)) > 1) { + while (--width) { + DEBUGLOG(("insert_buf: Wide(UTF-8):%d,%d",width,ch)); + lbuf[lpos++] = (WIDE_TAG | ((Uint32) ch)); + } + } +#endif DEBUGLOG(("insert_buf: Printable(UTF-8):%d",ch)); lbuf[lpos++] = (Uint32) ch; } else if (ch >= 128) { /* not utf8 mode */ @@ -1006,6 +1039,8 @@ static int write_buf(Uint32 *s, int n) if (octbuff != octtmp) { driver_free(octbuff); } + } else if (*s & WIDE_TAG) { + --n; s++; } else { DEBUGLOG(("Very unexpected character %d",(int) *s)); ++n; -- cgit v1.2.3