]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1946: filename expansion using ** in bash may fail v9.0.1946
authorChristian Brabandt <cb@256bit.org>
Wed, 27 Sep 2023 17:08:25 +0000 (19:08 +0200)
committerChristian Brabandt <cb@256bit.org>
Wed, 27 Sep 2023 17:08:25 +0000 (19:08 +0200)
Problem:  filename expansion using ** in bash may fail
Solution: Try to enable the globstar setting

Starting with bash 4.0 it supports extended globbing using the globstar
shell option. This makes matching recursively below a certain directory
using the ** pattern work as expected nowadays.  However, we need to
explicitly enable this using the 'shopt -s globstar' bash command.

So let's check the bash environment variable $BASH_VERSINFO (which is
supported since bash 3.0 and conditionally enable the globstar option,
if the major version is at least 4. For older bashs, this at least
shouldn't cause errors (unless one is using really ancient bash 2.X or
something).

closes: #13002
closes: #13144

Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/editing.txt
src/os_unix.c
src/testdir/test_functions.vim
src/version.c

index da9aeb7feaa5048d9ea5bf4a92fdeba7a873427b..46279110a7238ca32d6a274a2693a5b6fbbfa4b9 100644 (file)
@@ -1,4 +1,4 @@
-*editing.txt*   For Vim version 9.0.  Last change: 2023 Apr 23
+*editing.txt*   For Vim version 9.0.  Last change: 2023 Sep 22
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -385,7 +385,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]".
 
                                        *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.
 Note there are some commands where this works slightly differently, see
 |file-searching|.
index 45c6b440a5b7632b8c0e5f3130f848ec191275ce..c5a54e419e7cb92677d0b38b7ce33b1a957cae93 100644 (file)
@@ -6701,14 +6701,17 @@ mch_expand_wildcards(
 #define STYLE_GLOB     1       // use "glob", for csh
 #define STYLE_VIMGLOB  2       // use "vimglob", for Posix sh
 #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 using globstar, needs bash > 4)
     int                shell_style = STYLE_ECHO;
     int                check_spaces;
     static int did_find_nul = FALSE;
     int                ampersand = FALSE;
                // vimglob() function to define for Posix shell
     static char *sh_vimglob_func = "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; ";
 
     *num_file = 0;     // default: no files found
     *file = NULL;
@@ -6755,6 +6758,8 @@ mch_expand_wildcards(
      *     If we use *zsh, "print -N" will work better than "glob".
      * STYLE_VIMGLOB:  NL separated
      *     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.
      *     A shell we don't know, stay safe and use "echo".
      */
@@ -6769,9 +6774,13 @@ mch_expand_wildcards(
        else if (STRCMP(p_sh + len - 3, "zsh") == 0)
            shell_style = STYLE_PRINT;
     }
-    if (shell_style == STYLE_ECHO && strstr((char *)gettail(p_sh),
-                                                               "sh") != NULL)
-       shell_style = STYLE_VIMGLOB;
+    if (shell_style == STYLE_ECHO)
+    {
+       if (strstr((char *)gettail(p_sh), "bash") != NULL)
+           shell_style = STYLE_GLOBSTAR;
+       else if (strstr((char *)gettail(p_sh), "sh") != NULL)
+           shell_style = STYLE_VIMGLOB;
+    }
 
     // Compute the length of the command.  We need 2 extra bytes: for the
     // optional '&' and for the NUL.
@@ -6779,6 +6788,9 @@ mch_expand_wildcards(
     len = STRLEN(tempname) + 29;
     if (shell_style == STYLE_VIMGLOB)
        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)
     {
@@ -6847,6 +6859,11 @@ mch_expand_wildcards(
            STRCAT(command, "print -N >");
        else if (shell_style == STYLE_VIMGLOB)
            STRCAT(command, sh_vimglob_func);
+       else if (shell_style == STYLE_GLOBSTAR)
+       {
+           STRCAT(command, sh_globstar_opt);
+           STRCAT(command, sh_vimglob_func);
+       }
        else
            STRCAT(command, "echo >");
     }
@@ -7031,7 +7048,9 @@ mch_expand_wildcards(
        }
     }
     // 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
        p = buffer;
@@ -7112,7 +7131,7 @@ mch_expand_wildcards(
        (*file)[i] = p;
        // Space or NL separates
        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 == ' ')
                                                   && *p != '\n' && *p != NUL)
index 2cc57163dd818c6d4e98109c1eba286f17678cbb..4ec466685588cc10af6461a152d14d16b66d3a47 100644 (file)
@@ -3625,4 +3625,23 @@ func Test_fullcommand()
   call assert_equal('', fullcommand(10))
 endfunc
 
+" Test for glob() with shell special patterns
+func Test_glob_extended_bash()
+  CheckExecutable bash
+  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
index aef4b1a6410af4c7d4bde9a686ea0af564657097..d54df70ba5ef0ca5b041feb85da5f6b89d5f82c0 100644 (file)
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1946,
 /**/
     1945,
 /**/