From 7439e3dffeb6a4e9f29df7204b5cc215b4962c95 Mon Sep 17 00:00:00 2001 From: Govind Kamat Date: Fri, 30 Apr 2021 11:45:50 -0700 Subject: [PATCH] Parse quotes when extracting words from command history (#291) This avoids splitting on quoted whitespace when extracting words from the command history using the emacs M-. or vi _ command. Example: if the prior command is $ ls Stairway\ To\ Heaven.mp3 then, M-. in Emacs editing mode (and _ in vi mode) now inserts Stairway\ To\ Heaven.mp3 instead of Heaven.mp3. The behavior is similar for 'Stairway To Heaven.mp3' and "Stairway To Heaven.mp3". src/cmd/ksh93/edit/history.c: hist_word(): - Skip over single-quoted and double-quoted strings and backslash-escaped characters. src/cmd/ksh93/tests/pty.sh: - Add regression test for this feature in vi mode. Since emacs and vi both use the same code for this, that should be good enough. Co-authored-by: Martijn Dekker --- NEWS | 5 +++++ src/cmd/ksh93/edit/history.c | 21 +++++++++++++++++---- src/cmd/ksh93/tests/pty.sh | 14 ++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index b81d7e5d0..6beb4f674 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,11 @@ Any uppercase BUG_* names are modernish shell bug IDs. 2021-04-30: +- The emacs 'ESC .' (M-.) and vi '_' commands now take shell quoting into + account when repeating a word from the previous command line. For example, if + the previous command is 'ls Stairway\ To\ Heaven.mp3', then they now insert + 'Stairway\ To\ Heaven.mp3' instead of 'Heaven.mp3'. Thanks to Govind Kamat. + - Fixed a bug introduced on 2020-09-05 that caused "echo ${var:+'{}'}" to be misparsed. diff --git a/src/cmd/ksh93/edit/history.c b/src/cmd/ksh93/edit/history.c index 8856f146f..e7cd8540a 100644 --- a/src/cmd/ksh93/edit/history.c +++ b/src/cmd/ksh93/edit/history.c @@ -1057,6 +1057,8 @@ int hist_copy(char *s1,int size,int command,int line) char *hist_word(char *string,int size,int word) { register int c; + register int is_space; + register int quoted; register char *s1 = string; register unsigned char *cp = (unsigned char*)s1; register int flag = 0; @@ -1064,21 +1066,32 @@ char *hist_word(char *string,int size,int word) if(!hp) return(NIL(char*)); hist_copy(string,size,(int)hp->histind-1,-1); - for(;c = *cp;cp++) + for(quoted=0;c = *cp;cp++) { - c = isspace(c); - if(c && flag) + is_space = isspace(c) && !quoted; + if(is_space && flag) { *cp = 0; if(--word==0) break; flag = 0; } - else if(c==0 && flag==0) + else if(is_space==0 && flag==0) { s1 = (char*)cp; flag++; } + if (c=='\'' && !quoted) + { + for(cp++;*cp && *cp != c;cp++) + ; + } + else if (c=='\"' && !quoted) + { + for(cp++;*cp && (*cp != c || quoted);cp++) + quoted = *cp=='\\' ? !quoted : 0; + } + quoted = *cp=='\\' ? !quoted : 0; } *cp = 0; if(s1 != string) diff --git a/src/cmd/ksh93/tests/pty.sh b/src/cmd/ksh93/tests/pty.sh index 5b22376b3..ac897196f 100755 --- a/src/cmd/ksh93/tests/pty.sh +++ b/src/cmd/ksh93/tests/pty.sh @@ -812,5 +812,19 @@ r ^:test-1: echo ok\r\n$ r ^ok\r\n$ ! +# err_exit # +((SHOPT_VSH)) && tst $LINENO <<"!" +L split on quoted whitespace when extracting words from command history +# https://github.com/ksh93/ksh/pull/291 + +d 15 +p :test-1: +w true ls One\\ "Two Three"$'Four Five'.mp3 +r ^:test-1: true ls One\\ "Two Three"\$'Four Five'\.mp3\r\n$ +p :test-2: +w :\E_ +r ^:test-2: : One\\ "Two Three"\$'Four Five'\.mp3\r\n$ +! + # ====== exit $((Errors<125?Errors:125))