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)
6e82351130
This commit is contained in:
Jason Cox
2021-08-31 20:16:03 -06:00
parent 284199bc4b
commit 85ba41a4b3
4 changed files with 37 additions and 0 deletions

View File

@@ -8290,6 +8290,10 @@ shellescape({string} [, {special}]) *shellescape()*
- The <NL> character is escaped (twice if {special} is - The <NL> character is escaped (twice if {special} is
a ||non-zero-arg|). 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: > Example of use with a |:!| command: >
:exe '!dir ' . shellescape(expand('<cfile>'), 1) :exe '!dir ' . shellescape(expand('<cfile>'), 1)
< This results in a directory listing for the file under the < This results in a directory listing for the file under the

View File

@@ -7618,6 +7618,12 @@ int csh_like_shell(void)
return strstr((char *)path_tail(p_sh), "csh") != NULL; 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 /// Return the number of requested sign columns, based on current
/// buffer signs and on user configuration. /// buffer signs and on user configuration.
int win_signcol_count(win_T *wp) int win_signcol_count(win_T *wp)

View File

@@ -190,6 +190,7 @@ char_u *vim_strsave_shellescape(const char_u *string,
char_u *escaped_string; char_u *escaped_string;
size_t l; size_t l;
int csh_like; int csh_like;
bool fish_like;
/* Only csh and similar shells expand '!' within single quotes. For sh and /* 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 * 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 also needs to have "\n" escaped twice when do_special is set. */
csh_like = csh_like_shell(); 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. */ /* First count the number of extra bytes required. */
size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL
for (const char_u *p = string; *p != NUL; MB_PTR_ADV(p)) { 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 */ ++length; /* insert backslash */
p += l - 1; p += l - 1;
} }
if (*p == '\\' && fish_like) {
length++; // insert backslash
}
} }
/* Allocate memory for the result and fill it. */ /* Allocate memory for the result and fill it. */
@@ -267,6 +275,10 @@ char_u *vim_strsave_shellescape(const char_u *string,
*d++ = *p++; *d++ = *p++;
continue; continue;
} }
if (*p == '\\' && fish_like) {
*d++ = '\\';
*d++ = *p++;
}
MB_COPY_CHAR(p, d); MB_COPY_CHAR(p, d);
} }

View File

@@ -1160,6 +1160,21 @@ func Test_shellescape()
call assert_equal("'te\\\nxt'", shellescape("te\nxt")) call assert_equal("'te\\\nxt'", shellescape("te\nxt"))
call assert_equal("'te\\\\\nxt'", shellescape("te\nxt", 1)) 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 let &shell = save_shell
endfunc endfunc