Merge pull request #25398 from zeertzjq/vim-9.0.1946

vim-patch:9.0.{0607,1946,1947}: filename expansion using ** in bash may fail
This commit is contained in:
zeertzjq
2023-09-29 06:51:08 +08:00
committed by GitHub
5 changed files with 65 additions and 7 deletions

View File

@@ -356,7 +356,9 @@ as a wildcard when "[" is in the 'isfname' option. A simple way to avoid this
is to use "path\[[]abc]", this matches the file "path\[abc]". is to use "path\[[]abc]", this matches the file "path\[abc]".
*starstar-wildcard* *starstar-wildcard*
Expanding "**" is possible on Unix, Win32, macOS and a few other systems. Expanding "**" is possible on Unix, Win32, macOS and a few other systems (but
it may depend on your 'shell' setting. It's known to work correctly for zsh; for
bash this requires at least bash version >= 4.X).
This allows searching a directory tree. This goes up to 100 directories deep. This allows searching a directory tree. This goes up to 100 directories deep.
Note there are some commands where this works slightly differently, see Note there are some commands where this works slightly differently, see
|file-searching|. |file-searching|.

View File

@@ -134,6 +134,8 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
#define STYLE_VIMGLOB 2 // use "vimglob", for Posix sh #define STYLE_VIMGLOB 2 // use "vimglob", for Posix sh
#define STYLE_PRINT 3 // use "print -N", for zsh #define STYLE_PRINT 3 // use "print -N", for zsh
#define STYLE_BT 4 // `cmd` expansion, execute the pattern directly #define STYLE_BT 4 // `cmd` expansion, execute the pattern directly
#define STYLE_GLOBSTAR 5 // use extended shell glob for bash (this uses extended
// globbing functionality with globstar, needs bash > 4)
int shell_style = STYLE_ECHO; int shell_style = STYLE_ECHO;
int check_spaces; int check_spaces;
static bool did_find_nul = false; static bool did_find_nul = false;
@@ -141,6 +143,9 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// vimglob() function to define for Posix shell // vimglob() function to define for Posix shell
static char *sh_vimglob_func = static char *sh_vimglob_func =
"vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >"; "vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >";
// vimglob() function with globstar setting enabled, only for bash >= 4.X
static char *sh_globstar_opt =
"[[ ${BASH_VERSINFO[0]} -ge 4 ]] && shopt -s globstar; ";
bool is_fish_shell = bool is_fish_shell =
#if defined(UNIX) #if defined(UNIX)
@@ -190,6 +195,8 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// If we use *zsh, "print -N" will work better than "glob". // If we use *zsh, "print -N" will work better than "glob".
// STYLE_VIMGLOB: NL separated // STYLE_VIMGLOB: NL separated
// If we use *sh*, we define "vimglob()". // If we use *sh*, we define "vimglob()".
// STYLE_GLOBSTAR: NL separated
// If we use *bash*, we define "vimglob() and enable globstar option".
// STYLE_ECHO: space separated. // STYLE_ECHO: space separated.
// A shell we don't know, stay safe and use "echo". // A shell we don't know, stay safe and use "echo".
if (num_pat == 1 && *pat[0] == '`' if (num_pat == 1 && *pat[0] == '`'
@@ -203,10 +210,13 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
shell_style = STYLE_PRINT; shell_style = STYLE_PRINT;
} }
} }
if (shell_style == STYLE_ECHO if (shell_style == STYLE_ECHO) {
&& strstr(path_tail(p_sh), "sh") != NULL) { if (strstr(path_tail(p_sh), "bash") != NULL) {
shell_style = STYLE_GLOBSTAR;
} else if (strstr(path_tail(p_sh), "sh") != NULL) {
shell_style = STYLE_VIMGLOB; shell_style = STYLE_VIMGLOB;
} }
}
// Compute the length of the command. We need 2 extra bytes: for the // Compute the length of the command. We need 2 extra bytes: for the
// optional '&' and for the NUL. // optional '&' and for the NUL.
@@ -214,6 +224,8 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
len = strlen(tempname) + 29; len = strlen(tempname) + 29;
if (shell_style == STYLE_VIMGLOB) { if (shell_style == STYLE_VIMGLOB) {
len += strlen(sh_vimglob_func); len += strlen(sh_vimglob_func);
} else if (shell_style == STYLE_GLOBSTAR) {
len += strlen(sh_vimglob_func) + strlen(sh_globstar_opt);
} }
for (i = 0; i < num_pat; i++) { for (i = 0; i < num_pat; i++) {
@@ -281,6 +293,9 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
STRCAT(command, "print -N >"); STRCAT(command, "print -N >");
} else if (shell_style == STYLE_VIMGLOB) { } else if (shell_style == STYLE_VIMGLOB) {
STRCAT(command, sh_vimglob_func); STRCAT(command, sh_vimglob_func);
} else if (shell_style == STYLE_GLOBSTAR) {
STRCAT(command, sh_globstar_opt);
STRCAT(command, sh_vimglob_func);
} else { } else {
STRCAT(command, "echo >"); STRCAT(command, "echo >");
} }
@@ -430,7 +445,9 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
p = skipwhite(p); // skip to next entry p = skipwhite(p); // skip to next entry
} }
// file names are separated with NL // file names are separated with NL
} else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) { } else if (shell_style == STYLE_BT
|| shell_style == STYLE_VIMGLOB
|| shell_style == STYLE_GLOBSTAR) {
buffer[len] = NUL; // make sure the buffer ends in NUL buffer[len] = NUL; // make sure the buffer ends in NUL
p = buffer; p = buffer;
for (i = 0; *p != NUL; i++) { // count number of entries for (i = 0; *p != NUL; i++) { // count number of entries
@@ -496,7 +513,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
(*file)[i] = p; (*file)[i] = p;
// Space or NL separates // Space or NL separates
if (shell_style == STYLE_ECHO || shell_style == STYLE_BT if (shell_style == STYLE_ECHO || shell_style == STYLE_BT
|| shell_style == STYLE_VIMGLOB) { || shell_style == STYLE_VIMGLOB || shell_style == STYLE_GLOBSTAR) {
while (!(shell_style == STYLE_ECHO && *p == ' ') while (!(shell_style == STYLE_ECHO && *p == ' ')
&& *p != '\n' && *p != NUL) { && *p != '\n' && *p != NUL) {
p++; p++;

View File

@@ -108,6 +108,22 @@ func CheckNotBSD()
endif endif
endfunc endfunc
" Command to check for not running on a MacOS
command CheckNotMac call CheckNotMac()
func CheckNotMac()
if has('mac')
throw 'Skipped: does not work on MacOS'
endif
endfunc
" Command to check for not running on a MacOS M1 system.
command CheckNotMacM1 call CheckNotMacM1()
func CheckNotMacM1()
if has('mac') && system('uname -a') =~ '\<arm64\>'
throw 'Skipped: does not work on MacOS M1'
endif
endfunc
" Command to check that making screendumps is supported. " Command to check that making screendumps is supported.
" Caller must source screendump.vim " Caller must source screendump.vim
command CheckScreendump call CheckScreendump() command CheckScreendump call CheckScreendump()

View File

@@ -3281,4 +3281,26 @@ func Test_fullcommand()
call assert_equal('', fullcommand(10)) call assert_equal('', fullcommand(10))
endfunc endfunc
" Test for glob() with shell special patterns
func Test_glob_extended_bash()
CheckExecutable bash
CheckNotMSWindows
CheckNotMac " The default version of bash is old on macOS.
let _shell = &shell
set shell=bash
call mkdir('Xtestglob/foo/bar/src', 'p')
call writefile([], 'Xtestglob/foo/bar/src/foo.sh')
call writefile([], 'Xtestglob/foo/bar/src/foo.h')
call writefile([], 'Xtestglob/foo/bar/src/foo.cpp')
" Sort output of glob() otherwise we end up with different
" ordering depending on whether file system is case-sensitive.
let expected = ['Xtestglob/foo/bar/src/foo.cpp', 'Xtestglob/foo/bar/src/foo.h']
call assert_equal(expected, sort(glob('Xtestglob/**/foo.{h,cpp}', 0, 1)))
call delete('Xtestglob', 'rf')
let &shell=_shell
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@@ -379,7 +379,8 @@ endfunc
" Test verbose message before echo command " Test verbose message before echo command
func Test_echo_verbose_system() func Test_echo_verbose_system()
CheckRunVimInTerminal CheckRunVimInTerminal
CheckUnix CheckUnix " needs the "seq" command
CheckNotMac " doesn't use /tmp
let buf = RunVimInTerminal('', {'rows': 10}) let buf = RunVimInTerminal('', {'rows': 10})
call term_sendkeys(buf, ":4 verbose echo system('seq 20')\<CR>") call term_sendkeys(buf, ":4 verbose echo system('seq 20')\<CR>")