From 24186e9905a1d686516359b02551a9c887a7de3b Mon Sep 17 00:00:00 2001 From: Andreas Kupries Date: Fri, 18 Oct 2013 13:43:06 -0700 Subject: [PATCH] Bug fix. Fix fallback code for getting the terminal width. 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 | 101 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/linenoise.c b/linenoise.c index 87513cc..4dd99c8 100644 --- a/linenoise.c +++ b/linenoise.c @@ -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, ¤t->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; } -- 2.44.0