If ioctl() fails get num of columns querying the terminal. This change makes linenoise both more robust if the ioctl() fails, and far more portable to non POSIX systems, since the ioclt() to query the terminal was probably one of the non trivial parts to replace. The code is inspired to Andreas Kupries commit 24186e99 on the win32 Linenoise port from Steve Bennett, but is a complete reimplementation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
diff --git a/linenoise.c b/linenoise.c
index e8cef0f..708e3c9 100644
--- a/linenoise.c
+++ b/linenoise.c
@@ -244,13 +244,64 @@ static void disableRawMode(int fd) {
rawmode = 0;
}
+/* Use the ESC [6n escape sequence to query the horizontal cursor position
+ * and return it. On error -1 is returned, on success the position of the
+ * cursor. */
+static int getCursorPosition(int ifd, int ofd) {
+ char buf[32];
+ int cols, rows;
+ unsigned int i = 0;
+
+ /* Report cursor location */
+ if (write(ofd, "\x1b[6n", 4) != 4) return -1;
+
+ /* Read the response: ESC [ rows ; cols R */
+ while (i < sizeof(buf)-1) {
+ read(ifd,buf+i,1);
+ if (buf[i] == 'R') break;
+ i++;
+ }
+ buf[i] = '\0';
+
+ /* Parse it. */
+ if (buf[0] != ESC || buf[1] != '[') return -1;
+ if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
+ return cols;
+}
+
/* Try to get the number of columns in the current terminal, or assume 80
* if it fails. */
-static int getColumns(void) {
+static int getColumns(int ifd, int ofd) {
struct winsize ws;
- if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) return 80;
- return ws.ws_col;
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+ /* ioctl() failed. Try to query the terminal itself. */
+ int start, cols;
+
+ /* Get the initial position so we can restore it later. */
+ start = getCursorPosition(ifd,ofd);
+ if (start == -1) goto failed;
+
+ /* Go to right margin and get position. */
+ if (write(ofd,"\x1b[999C",6) != 6) goto failed;
+ cols = getCursorPosition(ifd,ofd);
+ if (cols == -1) goto failed;
+
+ /* Restore position. */
+ if (cols > start) {
+ char seq[32];
+ snprintf(seq,32,"\x1b[%dD",cols-start);
+ if (write(ofd,seq,strlen(seq)) == -1) {
+ /* Can't recover... */
+ }
+ }
+ return cols;
+ } else {
+ return ws.ws_col;
+ }
+
+failed:
+ return 80;
}
/* Clear the screen. Used to handle ctrl+l */
@@ -654,7 +705,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
l.plen = strlen(prompt);
l.oldpos = l.pos = 0;
l.len = 0;
- l.cols = getColumns();
+ l.cols = getColumns(stdin_fd, stdout_fd);
l.maxrows = 0;
l.history_index = 0;