aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
authorAnthony Ramine <[email protected]>2013-02-09 13:46:52 +0100
committerAnthony Ramine <[email protected]>2013-04-27 14:59:31 +0200
commitb9345b4f9c44ea7dea994cb3b06d639f9bf66a8d (patch)
tree690b28de76164a356ee544d6f2edab71826f9a60 /erts
parent01824e46557807f21df4db973e7f814e73bd36f8 (diff)
downloadotp-b9345b4f9c44ea7dea994cb3b06d639f9bf66a8d.tar.gz
otp-b9345b4f9c44ea7dea994cb3b06d639f9bf66a8d.tar.bz2
otp-b9345b4f9c44ea7dea994cb3b06d639f9bf66a8d.zip
Support wide characters in the shell through wcwidth()
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
Diffstat (limited to 'erts')
-rw-r--r--erts/configure.in11
-rw-r--r--erts/emulator/drivers/unix/ttsl_drv.c37
2 files changed, 47 insertions, 1 deletions
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 <wchar.h>], [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 <locale.h>
#include <unistd.h>
#include <termios.h>
+#ifdef HAVE_WCWIDTH
+#include <wchar.h>
+#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#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;