From 3b9674999e0c96b8bbfe60441d0823cf9d6e1cd3 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Thu, 10 Aug 2023 17:21:41 +0200 Subject: [PATCH 1/2] rework handling of input escape sequences this: 1. prevents unknown escape sequences from leaking over as real input in some situations 2. makes it easier to handle longer escape sequences in the future --- linenoise.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/linenoise.c b/linenoise.c index 5e8aee57..3c97355a 100644 --- a/linenoise.c +++ b/linenoise.c @@ -931,7 +931,7 @@ char *linenoiseEditFeed(struct linenoiseState *l) { char c; int nread; - char seq[3]; + char seq[32]; nread = read(l->ifd,&c,1); if (nread <= 0) return NULL; @@ -1001,24 +1001,21 @@ char *linenoiseEditFeed(struct linenoiseState *l) { linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT); break; case ESC: /* escape sequence */ - /* Read the next two bytes representing the escape sequence. - * Use two calls to handle slow terminals returning the two - * chars at different times. */ if (read(l->ifd,seq,1) == -1) break; - if (read(l->ifd,seq+1,1) == -1) break; - - /* ESC [ sequences. */ - if (seq[0] == '[') { - if (seq[1] >= '0' && seq[1] <= '9') { - /* Extended escape, read additional byte. */ - if (read(l->ifd,seq+2,1) == -1) break; - if (seq[2] == '~') { - switch(seq[1]) { - case '3': /* Delete key. */ - linenoiseEditDelete(l); - break; - } + if (seq[0] == '[') { /* ESC [ */ + /* terminated by a byte in \x40-\x7E inclusive */ + memset(seq, 0, sizeof(seq)); + for (size_t pos = 1; pos < sizeof(seq); pos++) { + if (read(l->ifd,seq+pos,1) == -1) { + /* The next read will be fucked, but continue anyways. + * The same goes for if it runs out of space in seq. */ + return linenoiseEditMore; } + if (0x40 <= seq[pos] && seq[pos] <= 0x7E) break; + } + + if (seq[1] == '3' && seq[2] == '~') { + linenoiseEditDelete(l); } else { switch(seq[1]) { case 'A': /* Up */ @@ -1041,10 +1038,8 @@ char *linenoiseEditFeed(struct linenoiseState *l) { break; } } - } - - /* ESC O sequences. */ - else if (seq[0] == 'O') { + } else if (seq[0] == 'O') { /* ESC O */ + if (read(l->ifd,seq+1,1) == -1) break; switch(seq[1]) { case 'H': /* Home */ linenoiseEditMoveHome(l); From 5a17d744e70f066714ba10ac8e4a1cb0843aa773 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Thu, 10 Aug 2023 17:54:15 +0200 Subject: [PATCH 2/2] support Ctrl+Left and Ctrl+Right yhirose's patch didn't make linenoiseEditDeletePrevWord() use the encoding abstractions, so i'm being consistent here. it works with utf8 anyways furthermore, this makes it easier to cherrypick this onto a separate branch and send as a PR upstream --- linenoise.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/linenoise.c b/linenoise.c index 3c97355a..4e7e9fa8 100644 --- a/linenoise.c +++ b/linenoise.c @@ -756,6 +756,15 @@ void linenoiseEditMoveLeft(struct linenoiseState *l) { } } +/* Move cursor to the beginning of the previous word. */ +void linenoiseEditMoveLeftWord(struct linenoiseState *l) { + while (l->pos > 0 && l->buf[l->pos-1] == ' ') + l->pos--; + while (l->pos > 0 && l->buf[l->pos-1] != ' ') + l->pos--; + refreshLine(l); +} + /* Move cursor on the right. */ void linenoiseEditMoveRight(struct linenoiseState *l) { if (l->pos != l->len) { @@ -764,6 +773,15 @@ void linenoiseEditMoveRight(struct linenoiseState *l) { } } +/* Move cursor to the end of the next word. */ +void linenoiseEditMoveRightWord(struct linenoiseState *l) { + while (l->pos < l->len && l->buf[l->pos] == ' ') + l->pos++; + while (l->pos < l->len && l->buf[l->pos] != ' ') + l->pos++; + refreshLine(l); +} + /* Move cursor to the start of the line. */ void linenoiseEditMoveHome(struct linenoiseState *l) { if (l->pos != 0) { @@ -1016,6 +1034,15 @@ char *linenoiseEditFeed(struct linenoiseState *l) { if (seq[1] == '3' && seq[2] == '~') { linenoiseEditDelete(l); + } else if (seq[1] == '1' && seq[2] == ';' && seq[3] == '5') { + switch(seq[4]) { + case 'C': /* Ctrl+Right */ + linenoiseEditMoveRightWord(l); + break; + case 'D': /* Ctrl+Left */ + linenoiseEditMoveLeftWord(l); + break; + } } else { switch(seq[1]) { case 'A': /* Up */