From 85ba41a4b343d0a8fe6d3d5af90a74640220f5cb Mon Sep 17 00:00:00 2001 From: Jason Cox Date: Tue, 31 Aug 2021 20:16:03 -0600 Subject: [PATCH 1/2] vim-patch:8.2.3385: escaping for fish shell does not work properly Problem: Escaping for fish shell does not work properly. Solution: Insert a backslash before a backslash. (Jason Cox, closes vim/vim#8810) https://github.com/vim/vim/commit/6e82351130ddb8d13cf3748b47f07cae77886fc7 --- runtime/doc/eval.txt | 4 ++++ src/nvim/option.c | 6 ++++++ src/nvim/strings.c | 12 ++++++++++++ src/nvim/testdir/test_functions.vim | 15 +++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 87240831a2..9430d4ab58 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -8290,6 +8290,10 @@ shellescape({string} [, {special}]) *shellescape()* - The character is escaped (twice if {special} is a ||non-zero-arg|). + If 'shell' contains "fish" in the tail, the "\" character will + be escaped because in fish it is used as an escape character + inside single quotes. + Example of use with a |:!| command: > :exe '!dir ' . shellescape(expand(''), 1) < This results in a directory listing for the file under the diff --git a/src/nvim/option.c b/src/nvim/option.c index d11bbc8ecc..fbf19ab9ff 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -7618,6 +7618,12 @@ int csh_like_shell(void) return strstr((char *)path_tail(p_sh), "csh") != NULL; } +/// Return true when 'shell' has "fish" in the tail. +bool fish_like_shell(void) +{ + return strstr((char *)path_tail(p_sh), "fish") != NULL; +} + /// Return the number of requested sign columns, based on current /// buffer signs and on user configuration. int win_signcol_count(win_T *wp) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 0363afe02d..33310701d5 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -190,6 +190,7 @@ char_u *vim_strsave_shellescape(const char_u *string, char_u *escaped_string; size_t l; int csh_like; + bool fish_like; /* Only csh and similar shells expand '!' within single quotes. For sh and * the like we must not put a backslash before it, it will be taken @@ -197,6 +198,10 @@ char_u *vim_strsave_shellescape(const char_u *string, * Csh also needs to have "\n" escaped twice when do_special is set. */ csh_like = csh_like_shell(); + // Fish shell uses '\' as an escape character within single quotes, so '\' + // itself must be escaped to get a literal '\'. + fish_like = fish_like_shell(); + /* First count the number of extra bytes required. */ size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL for (const char_u *p = string; *p != NUL; MB_PTR_ADV(p)) { @@ -220,6 +225,9 @@ char_u *vim_strsave_shellescape(const char_u *string, ++length; /* insert backslash */ p += l - 1; } + if (*p == '\\' && fish_like) { + length++; // insert backslash + } } /* Allocate memory for the result and fill it. */ @@ -267,6 +275,10 @@ char_u *vim_strsave_shellescape(const char_u *string, *d++ = *p++; continue; } + if (*p == '\\' && fish_like) { + *d++ = '\\'; + *d++ = *p++; + } MB_COPY_CHAR(p, d); } diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 6cb3e24201..c964f7aea4 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1160,6 +1160,21 @@ func Test_shellescape() call assert_equal("'te\\\nxt'", shellescape("te\nxt")) call assert_equal("'te\\\\\nxt'", shellescape("te\nxt", 1)) + set shell=fish + call assert_equal("'text'", shellescape('text')) + call assert_equal("'te\"xt'", shellescape('te"xt')) + call assert_equal("'te'\\''xt'", shellescape("te'xt")) + + call assert_equal("'te%xt'", shellescape("te%xt")) + call assert_equal("'te\\%xt'", shellescape("te%xt", 1)) + call assert_equal("'te#xt'", shellescape("te#xt")) + call assert_equal("'te\\#xt'", shellescape("te#xt", 1)) + call assert_equal("'te!xt'", shellescape("te!xt")) + call assert_equal("'te\\!xt'", shellescape("te!xt", 1)) + + call assert_equal("'te\\\\xt'", shellescape("te\\xt")) + call assert_equal("'te\\\\xt'", shellescape("te\\xt", 1)) + let &shell = save_shell endfunc From d3c6f1ebbb528dd526daa6b3cbf3007d65f2af17 Mon Sep 17 00:00:00 2001 From: Jason Cox Date: Wed, 1 Sep 2021 21:46:27 -0600 Subject: [PATCH 2/2] vim-patch:8.2.3393: escaping for fish shell is skipping some characters Problem: Escaping for fish shell is skipping some characters. Solution: Escape character after backslash if needed. (Jason Cox, closes vim/vim#8827) https://github.com/vim/vim/commit/6631597452d4644f485a09e4036d117e5f91de70 --- src/nvim/strings.c | 1 + src/nvim/testdir/test_functions.vim | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 33310701d5..79a3db4843 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -278,6 +278,7 @@ char_u *vim_strsave_shellescape(const char_u *string, if (*p == '\\' && fish_like) { *d++ = '\\'; *d++ = *p++; + continue; } MB_COPY_CHAR(p, d); diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index c964f7aea4..b35a210055 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1174,6 +1174,14 @@ func Test_shellescape() call assert_equal("'te\\\\xt'", shellescape("te\\xt")) call assert_equal("'te\\\\xt'", shellescape("te\\xt", 1)) + call assert_equal("'te\\\\'\\''xt'", shellescape("te\\'xt")) + call assert_equal("'te\\\\'\\''xt'", shellescape("te\\'xt", 1)) + call assert_equal("'te\\\\!xt'", shellescape("te\\!xt")) + call assert_equal("'te\\\\\\!xt'", shellescape("te\\!xt", 1)) + call assert_equal("'te\\\\%xt'", shellescape("te\\%xt")) + call assert_equal("'te\\\\\\%xt'", shellescape("te\\%xt", 1)) + call assert_equal("'te\\\\#xt'", shellescape("te\\#xt")) + call assert_equal("'te\\\\\\#xt'", shellescape("te\\#xt", 1)) let &shell = save_shell endfunc