Skip to content

Commit

Permalink
patch 8.2.3385: escaping for fish shell does not work properly
Browse files Browse the repository at this point in the history
Problem:    Escaping for fish shell does not work properly.
Solution:   Insert a backslash before a backslash. (Jason Cox, closes #8810)
  • Loading branch information
jasonccox authored and brammool committed Aug 29, 2021
1 parent 9dcd349 commit 6e82351
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 8 deletions.
4 changes: 4 additions & 0 deletions runtime/doc/eval.txt
Expand Up @@ -10111,6 +10111,10 @@ shellescape({string} [, {special}]) *shellescape()*
escaped. When 'shell' containing "csh" in the tail it's
escaped a second time.

The "\" character will be escaped when 'shell' contains "fish"
in the tail. That is because for fish "\" is used as an escape
character inside single quotes.

Example of use with a |:!| command: >
:exe '!dir ' . shellescape(expand('<cfile>'), 1)
< This results in a directory listing for the file under the
Expand Down
21 changes: 21 additions & 0 deletions src/strings.c
Expand Up @@ -124,6 +124,15 @@ csh_like_shell(void)
return (strstr((char *)gettail(p_sh), "csh") != NULL);
}

/*
* Return TRUE when 'shell' has "fish" in the tail.
*/
int
fish_like_shell(void)
{
return (strstr((char *)gettail(p_sh), "fish") != NULL);
}

/*
* Escape "string" for use as a shell argument with system().
* This uses single quotes, except when we know we need to use double quotes
Expand All @@ -145,6 +154,7 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
char_u *escaped_string;
int l;
int csh_like;
int fish_like;
char_u *shname;
int powershell;
# ifdef MSWIN
Expand All @@ -157,6 +167,10 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
// 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();

// PowerShell uses it's own version for quoting single quotes
shname = gettail(p_sh);
powershell = strstr((char *)shname, "pwsh") != NULL;
Expand Down Expand Up @@ -197,6 +211,8 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
++length; // insert backslash
p += l - 1;
}
if (*p == '\\' && fish_like)
++length; // insert backslash
}

// Allocate memory for the result and fill it.
Expand Down Expand Up @@ -261,6 +277,11 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
*d++ = *p++;
continue;
}
if (*p == '\\' && fish_like)
{
*d++ = '\\';
*d++ = *p++;
}

MB_COPY_CHAR(p, d);
}
Expand Down
19 changes: 11 additions & 8 deletions src/testdir/test_shell.vim
Expand Up @@ -61,18 +61,21 @@ func Test_shell_options()
for e in shells
exe 'set shell=' .. e[0]
if e[0] =~# '.*csh$' || e[0] =~# '.*csh.exe$'
let str1 = "'cmd \"arg1\" '\\''arg2'\\'' \\!%#'"
let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\\\!\\%\\#'"
let str1 = "'cmd \"arg1\" '\\''arg2'\\'' \\!%# \\'"
let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\\\!\\%\\# \\'"
elseif e[0] =~# '.*powershell$' || e[0] =~# '.*powershell.exe$'
\ || e[0] =~# '.*pwsh$' || e[0] =~# '.*pwsh.exe$'
let str1 = "'cmd \"arg1\" ''arg2'' !%#'"
let str2 = "'cmd \"arg1\" ''arg2'' \\!\\%\\#'"
let str1 = "'cmd \"arg1\" ''arg2'' !%# \\'"
let str2 = "'cmd \"arg1\" ''arg2'' \\!\\%\\# \\'"
elseif e[0] =~# '.*fish$' || e[0] =~# '.*fish.exe$'
let str1 = "'cmd \"arg1\" '\\''arg2'\\'' !%# \\\\'"
let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\!\\%\\# \\\\'"
else
let str1 = "'cmd \"arg1\" '\\''arg2'\\'' !%#'"
let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\!\\%\\#'"
let str1 = "'cmd \"arg1\" '\\''arg2'\\'' !%# \\'"
let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\!\\%\\# \\'"
endif
call assert_equal(str1, shellescape("cmd \"arg1\" 'arg2' !%#"), e[0])
call assert_equal(str2, shellescape("cmd \"arg1\" 'arg2' !%#", 1), e[0])
call assert_equal(str1, shellescape("cmd \"arg1\" 'arg2' !%# \\"), e[0])
call assert_equal(str2, shellescape("cmd \"arg1\" 'arg2' !%# \\", 1), e[0])

" Try running an external command with the shell.
if executable(e[0])
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Expand Up @@ -755,6 +755,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
3385,
/**/
3384,
/**/
Expand Down

0 comments on commit 6e82351

Please sign in to comment.