From a0dcdeeadec61acb3de0fa12334491e133e574ba Mon Sep 17 00:00:00 2001 From: Johnothan King Date: Fri, 3 Jul 2020 12:08:00 -0700 Subject: [PATCH] Fix bugs with backslash escaping in interactive vi mode (#57) This commit fixes the following bugs in the 'vi' editing mode backslash escape feature. Ref.: Bolsky & Korn (1995), p. 113, which states for \: "Similar to Control+V [...] except that it escapes only the next Erase or Kill charactrer". 1. The vi mode now only escapes the next character if the last character input was a backslash, fixing the bug demonstrated at: https://asciinema.org/a/E3Rq3et07MMQG5BaF7vkXQTg0 2. Escaping backslashes are now disabled in vi.c if the vi mode is disabled (note that vi.c handles raw editing mode in UTF-8 locales). This makes the behavior of the raw editing mode consistent in C/POSIX and UTF-8 locales. 3. An odd interaction with Backspace when the character prior to a separate buffer entered with Shift-C was a backslash has been fixed. Demonstration at: https://asciinema.org/a/314833 ^? will no longer be output repeatedly when attempting to erase a separate buffer with a Backspace, although, to be consistent with vi(1), you still cannot backspace past it before escaping out of it. Ref.: https://github.com/ksh93/ksh/issues/56#issuecomment-653586994 src/cmd/ksh93/edit/vi.c: - Prevent a backslash from escaping the next input if the previous input wasn't a backslash. This is done by unsetting a variable named backslash if a backslash escaped a character. backslash is set to the result of c == '\\' when the user enters a new character. - Disable escaping backslashes in the raw editing mode because it should not be enabled there. src/cmd/ksh93/tests/pty.sh: - Add some tests for how ksh handles backslashes in each editing mode to test for the bugs fixed by this commit. Fixes #56. --- NEWS | 18 ++++++++++++++++++ src/cmd/ksh93/edit/vi.c | 9 ++++++--- src/cmd/ksh93/tests/pty.sh | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 5f1c4a75a..89dd4a0c5 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,24 @@ For full details, see the git log at: https://github.com/ksh93/ksh Any uppercase BUG_* names are modernish shell bug IDs. +2020-07-03: + +- Backslashes are no longer escaped in the raw Bourne Shell-like editing + mode in multibyte locales, i.e. backslashes are no longer treated like + Control-V if the emacs and vi modes are disabled. + +- Deleting a backslash in vi mode with Control-H or Backspace now only + escapes a backslash if it was the previous input. This means erasing a + string such as 'ab\\\' will only cause the first backslash to escape a + Backspace as '^?', like in emacs mode. + +- An odd interaction with Backspace when the last character of a separate + buffer created with Shift-C was '\' has been fixed. '^?' will no longer + be output repeatedly when attempting to erase a separate buffer with + a Backspace. Note that buffers created with Shift-C are not meant to be + erasable: + https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html#tag_20_152_13_49 + 2020-07-02: - Fixed a crash that occurred if a directory named '.paths' existed in any diff --git a/src/cmd/ksh93/edit/vi.c b/src/cmd/ksh93/edit/vi.c index 99d2e20ff..f2ac65604 100644 --- a/src/cmd/ksh93/edit/vi.c +++ b/src/cmd/ksh93/edit/vi.c @@ -1363,7 +1363,7 @@ static void getline(register Vi_t* vp,register int mode) { register int c; register int tmp; - int max_virt=0, last_save=0; + int max_virt=0, last_save=0, backslash=0; genchar saveline[MAXLINE]; vp->addnl = 1; @@ -1460,8 +1460,9 @@ static void getline(register Vi_t* vp,register int mode) /*** treat as backspace ***/ case '\b': /** backspace **/ - if( virtual[cur_virt] == '\\' ) + if( sh_isoption(SH_VI) && backslash && virtual[cur_virt] == '\\' ) { + backslash = 0; cdelete(vp,1, BAD); append(vp,usrerase, mode); } @@ -1505,8 +1506,9 @@ static void getline(register Vi_t* vp,register int mode) break; case UKILL: /** user kill line char **/ - if( virtual[cur_virt] == '\\' ) + if( sh_isoption(SH_VI) && backslash && virtual[cur_virt] == '\\' ) { + backslash = 0; cdelete(vp,1, BAD); append(vp,usrkill, mode); } @@ -1580,6 +1582,7 @@ static void getline(register Vi_t* vp,register int mode) mode = APPEND; max_virt = last_virt+3; } + backslash = (c == '\\'); append(vp,c, mode); break; } diff --git a/src/cmd/ksh93/tests/pty.sh b/src/cmd/ksh93/tests/pty.sh index ddae54cce..8221158df 100755 --- a/src/cmd/ksh93/tests/pty.sh +++ b/src/cmd/ksh93/tests/pty.sh @@ -505,5 +505,38 @@ r ^:test-2: true (/de\tv/nu\tl\tl|/de v/nu l l)\r\n$ p :test-3: ! +LC_ALL=C.UTF8 tst $LINENO <<"!" +L raw Bourne mode backslash handling + +# The escaping backslash feature should be disabled in the raw Bourne mode. +# This is tested with both erase and kill characters. + +d 10 +p :test-1: +w set +o vi +o emacs; stty erase ^H kill ^X +p :test-2: +w true string\\\\\cH\cH +r ^:test-2: true string\r\n$ +p :test-3: +w true incorrect\\\cXtrue correct +r ^:test-3: true correct\r\n$ +! + +for mode in emacs vi; do +tst $LINENO << ! +L escaping backslashes in $mode mode + +# Backslashes should only be escaped if the previous input was a backslash. +# Other backslashes stored in the input buffer should be erased normally. + +d 10 +p :test-1: +w set -o $mode; stty erase ^H +p :test-2: +w true string\\\\\\\\\\cH\\cH\\cH +r ^:test-2: true string\\r\\n$ +! +done + # ====== exit $((Errors<125?Errors:125))