From 1e57f7471d60d60d9c837051d4625a35d779c520 Mon Sep 17 00:00:00 2001
From: Glauber Campinho <glauber.campinho@soundcloud.com>
Date: Tue, 29 Aug 2017 01:22:54 +0200
Subject: Make cp_pos_to_col function aware of the ANSI escape codes

This fixes the issue with the function `move_cursor` described in #1536 and that causes the bug described in https://github.com/elixir-lang/elixir/issues/6504.

The function `cp_pos_to_col` maps the current position in the buffer to the correct column on the screen, but it didn't handle ANSI escape codes. Since the ANSI escape codes aren't visible, the `cp_pos_to_col` now skips them when executing the calculations using `ansi_escape_width`. This new function only considers color escape codes, but also handles invalid codes.
---
 erts/emulator/drivers/unix/ttsl_drv.c | 68 +++++++++++++++++++++++++++--------
 1 file changed, 53 insertions(+), 15 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c
index f3c1aa1c4a..3d007173c4 100644
--- a/erts/emulator/drivers/unix/ttsl_drv.c
+++ b/erts/emulator/drivers/unix/ttsl_drv.c
@@ -1095,6 +1095,7 @@ static int insert_buf(byte *s, int n)
 		ch = 0;
 	    } while (lpos % 8);
 	} else if (ch == '\e') {
+	    DEBUGLOG(("insert_buf: ANSI Escape: \\e"));
 	    lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch));
 	} else if (ch == '\n' || ch == '\r') {
 	    write_buf(lbuf + buffpos, lpos - buffpos);
@@ -1156,7 +1157,7 @@ static int write_buf(Uint32 *s, int n)
 		--n; s++;
 	    }
 	} else if (*s == (CONTROL_TAG | ((Uint32) '\e'))) {
-	    outc('\e');
+	    outc(lastput = '\e');
 	    --n;
 	    ++s;
 	} else if (*s & CONTROL_TAG) {
@@ -1251,26 +1252,63 @@ static int move_cursor(int from_pos, int to_pos)
     return to_col-from_col;
 }
 
-static int cp_pos_to_col(int cp_pos)
+/*
+ * Returns the length of an ANSI escape code in a buffer, this function only consider
+ * color escape sequences like `\e[33m` or `\e[21;33m`. If a sequence has no valid
+ * terminator, the length is equal the number of characters between `\e` and the first
+ * invalid character, inclusive.
+ */
+
+static int ansi_escape_width(Uint32 *s, int max_length)
 {
-#ifdef HAVE_WCWIDTH
     int i;
-    int col = 0;
-
-    for (i = 0; i < cp_pos; i++) {
-        int w = wcwidth(lbuf[i]);
-        if (w > 0) {
-            col += w;
-        }
+    
+    if (*s != (CONTROL_TAG | ((Uint32) '\e'))) {
+       return 0;
+    } else if (max_length <= 1) {
+       return 1;
+    } else if (s[1] != '[') {
+       return 2;
     }
-    return col;
-#else
+    
+    for (i = 2; i < max_length && (s[i] == ';' || (s[i] >= '0' && s[i] <= '9')); i++);
+
+    return i + 1;
+}
+
+static int cp_pos_to_col(int cp_pos)
+{
     /*
-     * We dont' have any character width information. Assume that
-     * code points are one column wide.
+     * If we don't have any character width information. Assume that
+     * code points are one column wide
      */
-    return cp_pos;
+    int w = 1;
+    int col = 0;
+    int i = 0;
+    int j;
+
+    if (cp_pos > llen) {
+        col += cp_pos - llen;
+	cp_pos = llen;
+    }
+
+    while (i < cp_pos) {
+       j = ansi_escape_width(lbuf + i, llen - i);
+
+       if (j > 0) {
+           i += j;
+       } else {
+#ifdef HAVE_WCWIDTH
+           w = wcwidth(lbuf[i]);
 #endif
+           if (w > 0) {
+              col += w;
+           }
+           i++;
+       }
+    }
+
+    return col;    
 }
 
 static int start_termcap(void)
-- 
cgit v1.2.3


From c128db95440cc4caf34cbad5e91e1e26bb5fa6a3 Mon Sep 17 00:00:00 2001
From: Glauber Campinho <glauber.campinho@soundcloud.com>
Date: Fri, 1 Sep 2017 15:12:48 +0200
Subject: Fix del_chars not considering wide chars and update buffer length
 before calling write_buf

After deleting the chars the function `del_chars` was considering the code points to move the cursor back and not the graphemes
---
 erts/emulator/drivers/unix/ttsl_drv.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

(limited to 'erts')

diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c
index 3d007173c4..2a508b02eb 100644
--- a/erts/emulator/drivers/unix/ttsl_drv.c
+++ b/erts/emulator/drivers/unix/ttsl_drv.c
@@ -936,10 +936,10 @@ static int put_chars(byte *s, int l)
     int n;
 
     n = insert_buf(s, l);
+    if (lpos > llen)
+        llen = lpos;
     if (n > 0)
       write_buf(lbuf + lpos - n, n);
-    if (lpos > llen)
-      llen = lpos;
     return TRUE;
 }
 
@@ -1016,7 +1016,7 @@ static int del_chars(int n)
 	   outc(' ');
 	   move_left(1);
 	}
-	move_cursor(llen + l, lpos);
+	move_cursor(llen + gcs, lpos);
     }
     else if (pos < lpos) {
 	l = lpos - pos;		/* Buffer characters */
@@ -1036,7 +1036,7 @@ static int del_chars(int n)
           outc(' ');
           move_left(1);
 	}
-        move_cursor(llen + l, lpos);
+        move_cursor(llen + gcs, lpos);
     }
     return TRUE;
 }
@@ -1289,7 +1289,7 @@ static int cp_pos_to_col(int cp_pos)
 
     if (cp_pos > llen) {
         col += cp_pos - llen;
-	cp_pos = llen;
+        cp_pos = llen;
     }
 
     while (i < cp_pos) {
-- 
cgit v1.2.3