]> git.lizzy.rs Git - linenoise.git/commitdiff
Bug fix. Fix fallback code for getting the terminal width.
authorAndreas Kupries <andreask@activestate.com>
Fri, 18 Oct 2013 20:43:06 +0000 (13:43 -0700)
committerSteve Bennett <steveb@workware.net.au>
Mon, 21 Oct 2013 01:03:40 +0000 (11:03 +1000)
When using vt100 control sequences, do not change the visible cursor location.
Instead of
- go far right
- query location
we now do
(1) query location
(2) go far right
(3) query location again
(4) move left to the original location as reported by (1).

Only if (1) succeeds and (3) fails will the visible cursor location change (moving to far left).
Placed the code to request cursor location and parse the response into its own helper function.

The old way of doing it left the cursor at far right, and could mess up an outer shell prompt.

linenoise.c

index 87513cc7e1e39d7efcd25d66e4ebac4262941f54..4dd99c85cf5fe2a1099dfc5f1ca9c6216970e856 100644 (file)
@@ -433,6 +433,49 @@ static int countColorControlChars(const char* prompt)
     return found;
 }
 
+/**
+ * Stores the current cursor column in '*cols'.
+ * Returns 1 if OK, or 0 if failed to determine cursor pos.
+ */
+static int queryCursor(int fd, int* cols)
+{
+    /* control sequence - report cursor location */
+    fd_printf(fd, "\x1b[6n");
+
+    /* Parse the response: ESC [ rows ; cols R */
+    if (fd_read_char(fd, 100) == 0x1b &&
+        fd_read_char(fd, 100) == '[') {
+
+        int n = 0;
+        while (1) {
+            int ch = fd_read_char(fd, 100);
+            if (ch == ';') {
+                /* Ignore rows */
+                n = 0;
+            }
+            else if (ch == 'R') {
+                /* Got cols */
+                if (n != 0 && n < 1000) {
+                    *cols = n;
+                }
+                break;
+            }
+            else if (ch >= 0 && ch <= '9') {
+                n = n * 10 + ch - '0';
+            }
+            else {
+                break;
+            }
+        }
+        return 1;
+    }
+
+    return 0;
+}
+
+/**
+ * Updates current->cols with the current window size (width)
+ */
 static int getWindowSize(struct current *current)
 {
     struct winsize ws;
@@ -447,38 +490,46 @@ static int getWindowSize(struct current *current)
      * and reading back the cursor position.
      * Note that this is only done once per call to linenoise rather than
      * every time the line is refreshed for efficiency reasons.
+     *
+     * In more detail, we:
+     * (a) request current cursor position,
+     * (b) move cursor far right,
+     * (c) request cursor position again,
+     * (d) at last move back to the old position.
+     * This gives us the width without messing with the externally
+     * visible cursor position.
      */
+
     if (current->cols == 0) {
+        int here;
+
         current->cols = 80;
 
-        /* Move cursor far right and report cursor position, then back to the left */
-        fd_printf(current->fd, "\x1b[999C" "\x1b[6n");
-
-        /* Parse the response: ESC [ rows ; cols R */
-        if (fd_read_char(current->fd, 100) == 0x1b && fd_read_char(current->fd, 100) == '[') {
-            int n = 0;
-            while (1) {
-                int ch = fd_read_char(current->fd, 100);
-                if (ch == ';') {
-                    /* Ignore rows */
-                    n = 0;
-                }
-                else if (ch == 'R') {
-                    /* Got cols */
-                    if (n != 0 && n < 1000) {
-                        current->cols = n;
-                    }
-                    break;
-                }
-                else if (ch >= 0 && ch <= '9') {
-                    n = n * 10 + ch - '0';
-                }
-                else {
-                    break;
+        /* (a) */
+        if (queryCursor (current->fd, &here)) {
+            /* (b) */
+            fd_printf(current->fd, "\x1b[999C");
+
+            /* (c). Note: If (a) succeeded, then (c) should as well.
+             * For paranoia we still check and have a fallback action
+             * for (d) in case of failure..
+             */
+            if (!queryCursor (current->fd, &current->cols)) {
+                /* (d') Unable to get accurate position data, reset
+                 * the cursor to the far left. While this may not
+                 * restore the exact original position it should not
+                 * be too bad.
+                 */
+                fd_printf(current->fd, "\r");
+            } else {
+                /* (d) Reset the cursor back to the original location. */
+                if (current->cols > here) {
+                    fd_printf(current->fd, "\x1b[%dD", current->cols - here);
                 }
             }
-        }
+        } /* 1st query failed, doing nothing => default 80 */
     }
+
     return 0;
 }