]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0418: wildcards in expanded env vars reinterpreted by glob v9.2.0418
authorglepnir <glephunter@gmail.com>
Wed, 29 Apr 2026 19:10:43 +0000 (19:10 +0000)
committerChristian Brabandt <cb@256bit.org>
Wed, 29 Apr 2026 19:10:43 +0000 (19:10 +0000)
Problem:  With $d='[dir]', `:e $d/file.txt` opens the wrong file,
          `:e $d/<Tab>` fails to complete, and `glob('$d/*')` returns
          nothing. Wildcard characters inside expanded environment
          variables get picked up by globbing again.
Solution: Turn the 4th parameter of expand_env_esc() from a bool into a
          string of characters to escape in each expanded value. Callers
          that pass the result to wildcard expansion should include
          PATH_ESC_WILDCARDS in addition to " \t" (glepnir).

closes: #20053

Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
12 files changed:
runtime/doc/todo.txt
src/cmdexpand.c
src/ex_docmd.c
src/filepath.c
src/findfile.c
src/misc1.c
src/option.c
src/profiler.c
src/proto/misc1.pro
src/testdir/test_cmdline.vim
src/version.c
src/vim.h

index 34dacefd6efe2e9910efeaebcc3b8c73b0207bea..40a961f32831c123a6d0118d473f2a0f7483f36b 100644 (file)
@@ -2113,7 +2113,6 @@ es_ES.utf-8" gives an error and doesn't switch messages. (Dominique Pelle,
 
 When $HOME contains special characters, such as a comma, escape them when used
 in an option. (Michael Hordijk, 2009 May 5)
-Turn "esc" argument of expand_env_esc() into string of chars to be escaped.
 
 Should make 'ignorecase' global-local, so that it makes sense setting it from
 a modeline.
index 58a59bef679fe30ebea0f5693bf3f35d5e30cac5..b7a17bbdda1ce3c6a093293219ebd608d38db353 100644 (file)
@@ -1254,7 +1254,7 @@ showmatches_oneline(
                // Expansion was done before and special characters
                // were escaped, need to halve backslashes.  Also
                // $HOME has been replaced with ~/.
-               exp_path = expand_env_save_opt(matches[j], TRUE);
+               exp_path = expand_env_save_opt(matches[j], TRUE, NULL);
                path = exp_path != NULL ? exp_path : matches[j];
                halved_slash = backslash_halve_save(path);
                isdir = mch_isdir(halved_slash != NULL ? halved_slash
index 74e32a74ed1226ec6687ec9feab2c5ad913ac713..7ea20b262803c436930b1ff248be5aede87c62bf 100644 (file)
@@ -5246,7 +5246,8 @@ expand_filename(
                            || vim_strchr(eap->arg, '~') != NULL)
                    {
                        expand_env_esc(eap->arg, NameBuff, MAXPATHL,
-                                                           TRUE, TRUE, NULL);
+                                       (char_u *)(" \t" PATH_ESC_WILDCARDS),
+                                       TRUE, NULL);
                        has_wildcards = mch_has_wildcard(NameBuff);
                        p = NameBuff;
                    }
index 492e0ad3ad72a764128512de2daf7f9ccbc019f2..a41f8c27fceca98ce3bb9b74ca5c5de2576a89fd 100644 (file)
@@ -4158,7 +4158,7 @@ gen_expand_wildcards(
             */
            if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~')
            {
-               p = expand_env_save_opt(p, TRUE);
+               p = expand_env_save_opt(p, TRUE, (char_u *)PATH_ESC_WILDCARDS);
                if (p == NULL)
                    p = pat[i];
 #ifdef UNIX
index 0c5d1cf252bd6375e58ae216c8286d9ba2b18ca7..ed0ee76c49a036c5bc36f999c3d06098b49f3712 100644 (file)
@@ -1808,7 +1808,7 @@ find_file_in_path_option(
        // copy file name into NameBuff, expanding environment variables
        save_char = ptr[len];
        ptr[len] = NUL;
-       file_to_findlen = expand_env_esc(ptr, NameBuff, MAXPATHL, FALSE, TRUE, NULL);
+       file_to_findlen = expand_env_esc(ptr, NameBuff, MAXPATHL, NULL, TRUE, NULL);
        ptr[len] = save_char;
 
        vim_free(*file_to_find);
index a0f9f799e2abe8624121b0819436892c1f3c580b..88e872d766d489325a490c18faa4fe2118021772 100644 (file)
@@ -1398,7 +1398,7 @@ init_vimdir(void)
     char_u *
 expand_env_save(char_u *src)
 {
-    return expand_env_save_opt(src, FALSE);
+    return expand_env_save_opt(src, FALSE, NULL);
 }
 
 /*
@@ -1406,13 +1406,13 @@ expand_env_save(char_u *src)
  * expand "~" at the start.
  */
     char_u *
-expand_env_save_opt(char_u *src, int one)
+expand_env_save_opt(char_u *src, int one, char_u *esc_chars)
 {
     char_u     *p;
 
     p = alloc(MAXPATHL);
     if (p != NULL)
-       expand_env_esc(src, p, MAXPATHL, FALSE, one, NULL);
+       expand_env_esc(src, p, MAXPATHL, esc_chars, one, NULL);
     return p;
 }
 
@@ -1428,7 +1428,7 @@ expand_env(
     char_u     *dst,           // where to put the result
     int                dstlen)         // maximum length of the result
 {
-    return expand_env_esc(src, dst, dstlen, FALSE, FALSE, NULL);
+    return expand_env_esc(src, dst, dstlen, NULL, FALSE, NULL);
 }
 
     size_t
@@ -1436,7 +1436,7 @@ expand_env_esc(
     char_u     *srcp,          // input string e.g. "$HOME/vim.hlp"
     char_u     *dst,           // where to put the result
     int                dstlen,         // maximum length of the result
-    int                esc,            // escape spaces in expanded variables
+    char_u     *esc_chars,     // chars to escape in expanded vars
     int                one,            // "srcp" is one file name
     char_u     *startstr)      // start again after this (can be NULL)
 {
@@ -1655,11 +1655,13 @@ expand_env_esc(
            }
 #endif
 
-           // If "var" contains white space, escape it with a backslash.
-           // Required for ":e ~/tt" when $HOME includes a space.
-           if (esc && var != NULL && vim_strpbrk(var, (char_u *)" \t") != NULL)
+           // If "var" contains any character from "esc_chars", escape it
+           // with a backslash.  The historical use is escaping spaces so
+           // that ":e ~/tt" works when $HOME contains a space.
+           if (esc_chars != NULL && var != NULL
+                   && vim_strpbrk(var, esc_chars) != NULL)
            {
-               char_u  *p = vim_strsave_escaped(var, (char_u *)" \t");
+               char_u  *p = vim_strsave_escaped(var, esc_chars);
 
                if (p != NULL)
                {
index a1138a0e17b947698f263d52976920cf696011a5..b44a3990ad3a46e8f4d78e96d9a57b9e5266c3f9 100644 (file)
@@ -3264,7 +3264,8 @@ option_expand(int opt_idx, char_u *val)
     char_u ** var = (char_u **)options[opt_idx].var;
     int esc = var == &p_tags || var == &p_path;
 
-    expand_env_esc(val, NameBuff, MAXPATHL, esc, FALSE,
+    expand_env_esc(val, NameBuff, MAXPATHL,
+           esc ? (char_u *)" \t" : NULL, FALSE,
 #ifdef FEAT_SPELL
            var == &p_sps ? (char_u *)"file:" :
 #endif
index aa127a419f1ad1e47a0721ce9e1605dc8713e7ce..2aa5aa50ac5a30d81dc263b9dcf87454c3944aa0 100644 (file)
@@ -392,7 +392,7 @@ ex_profile(exarg_T *eap)
     if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL)
     {
        VIM_CLEAR(profile_fname);
-       profile_fname = expand_env_save_opt(e, TRUE);
+       profile_fname = expand_env_save_opt(e, TRUE, NULL);
        do_profiling = PROF_YES;
        profile_zero(&prof_wait_time);
        set_vim_var_nr(VV_PROFILING, 1L);
index d24a61d15186d96e24f9d39e4cad61886a0a4b28..706629490da24e6161fa68afc3237a76bd03fbd0 100644 (file)
@@ -29,9 +29,9 @@ void free_homedir(void);
 void free_users(void);
 void init_vimdir(void);
 char_u *expand_env_save(char_u *src);
-char_u *expand_env_save_opt(char_u *src, int one);
+char_u *expand_env_save_opt(char_u *src, int one, char_u *esc_chars);
 size_t expand_env(char_u *src, char_u *dst, int dstlen);
-size_t expand_env_esc(char_u *srcp, char_u *dst, int dstlen, int esc, int one, char_u *startstr);
+size_t expand_env_esc(char_u *srcp, char_u *dst, int dstlen, char_u *esc_chars, int one, char_u *startstr);
 char_u *vim_getenv(char_u *name, int *mustfree);
 void vim_unsetenv(char_u *var);
 void vim_unsetenv_ext(char_u *var);
index 80a023d2c1c71f93f1b109a4dad9bf088bbe43ee..ee5f668bb2aa564c028e4b60a5ff5fd7fb151ae2 100644 (file)
@@ -5506,4 +5506,31 @@ func Test_wildmode_noinsert()
   delfunc T
 endfunc
 
+func Test_cmdline_compl_env_var_wildcard()
+  CheckUnix
+
+  let d = tempname()
+  call mkdir(d .. '/[x]', 'pR')
+  call writefile(['hello'], d .. '/[x]/file.txt')
+  let $XWILD = d .. '/[x]'
+
+  call feedkeys(":e $XWILD/fi\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_match('\[x\]/file\.txt$', @:)
+  call assert_equal([d .. '/[x]/file.txt'], glob('$XWILD/*', 0, 1))
+
+  edit $XWILD/file.txt
+  call assert_equal('hello', getline(1))
+  bwipe!
+
+  if has('profile')
+    let prof = d .. '/[x]/prof.out'
+    profile start $XWILD/prof.out
+    profile stop
+    call assert_true(filereadable(prof))
+    call delete(prof)
+  endif
+
+  unlet $XWILD
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 86f3c4ffd1bff0b08cd9a7718deb862f54a96ede..23de968780e9266806677c8737e35eb36c3615de 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    418,
 /**/
     417,
 /**/
index c0c811d1d459dd07e232edce4fa00c87d7fbb850..37183f130ae1e0ea2e7f0e720fdcf77c87c9303a 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -3098,4 +3098,17 @@ long elapsed(DWORD start_tick);
 // Flags used by getvcol()
 #define GETVCOL_END_EXCL_LBR   1
 
+// Used by expand_env_esc() callers that feed the result to
+// wildcard expansion, so that such characters embedded in
+// environment variable values are treated as literal.
+#ifdef VMS
+# define PATH_ESC_WILDCARDS    "*?%"
+#else
+# ifdef MSWIN
+#  define PATH_ESC_WILDCARDS   "*?["
+# else
+#  define PATH_ESC_WILDCARDS   "*?[{"
+# endif
+#endif
+
 #endif // VIM__H