3

When I run shopt -s globstar; ls -1 tests/**/Helpers/**/*.php in bash I get:

tests/Helpers/Layout.php
tests/Helpers/Link.php
tests/Helpers/LinksPage.php
tests/Helpers/LiqpayRedirect.php
tests/Helpers/LoginPage.php
tests/Helpers/Project.php
tests/Helpers/ProjectsPage.php
tests/Helpers/QueriesPage.php
tests/Helpers/Query.php
tests/Helpers/User.php
tests/Selenium/Helpers/Layout.php
tests/Selenium/Helpers/LinksPage.php
tests/Selenium/Helpers/ProjectsPage.php
tests/Selenium/Helpers/QueriesPage.php
tests/Selenium/Helpers/RegisterPage.php
tests/Selenium/Helpers/User.php

But doing args tests/**/Helpers/**/*.php in vim defines argument list consisting only of files with Selenium in path. Am I doing it wrong? What exactly are the rules? From the docs it sounds like it should work.

5

You can check with echo glob2regpat('tests/**/Helpers/**/*.php') to what regular expression this will be translated. This returns: ^tests[\/].*[\/]Helpers[\/].*[\/].*\.php$, so it will look only for a Helpers directory which is located below any directory underneath the tests directory.

  • So this is not particularly true: "** matches anything, including nothing, recurses into directories" More importantly, where's glob2regpath() coming from? My vim says: "Unknown function." – x-yuri Jan 10 '17 at 12:13
  • update your vim – Christian Brabandt Jan 10 '17 at 12:21
  • My vim is VIM - Vi IMproved 8.0 (2016 Sep 12, compiled Oct 30 2016 15:29:26) Included patches: 1-55. Maybe that is some distributive-specific function? I'm running Arch Linux. And I can't find it in the docs and Google. – x-yuri Jan 10 '17 at 13:57
  • the correct function name is glob2regpat (without the h). I had an error in the first version of the answer and have since then updated it. – Christian Brabandt Jan 10 '17 at 14:01
  • 1
    @x-yuri no, /.*/ should match an empty path - in POSIX-following environments, any number of consecutive / (except a leading //) is equivalent to a single /. Part of the problem is that while glob2regpat returns a similar looking regex, I don't think the code actually uses regular expressions to check paths. The main problem is apparently the check for expanding ** to an empty path - it only allows it to be done once for a pattern. – muru Jan 12 '17 at 2:49
1

tl;dr

a bug in vim

how to reproduce/inspect

  1. Build vim from sources with the following patch:

    diff --git a/src/misc1.c b/src/misc1.c
    index 2d635d677..44ccf4a7a 100644
    --- a/src/misc1.c
    +++ b/src/misc1.c
    @@ -10171,8 +10171,18 @@ unix_expandpath(
         char_u *path,
         int        wildoff,
         int        flags,      /* EW_* flags */
    -    int        didstar)    /* expanded "**" once already */
    -{
    +    int        didstar, int __l)   /* expanded "**" once already */
    +{
    +FILE *__fp;
    +int __i;
    +__fp = fopen("../log", "a");
    +fprintf(__fp, "%*sunix_expandpath: path: %s, wildoff: %i, flags: %x, didstar: %i\n", __l * 4, "", path, wildoff, flags, didstar);
    +if (gap->ga_len) {
    +    fprintf(__fp, "%*s    gap:\n", __l * 4, "");
    +    for (__i = 0; __i < gap->ga_len; __i++)
    +   fprintf(__fp, "%*s        %s\n", __l * 4, "", ((char **)gap->ga_data)[__i]);
    +}
    +fclose(__fp);
         char_u *buf;
         char_u *path_end;
         char_u *p, *s, *e;
    @@ -10274,6 +10284,9 @@ unix_expandpath(
        regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */
         if (flags & (EW_NOERROR | EW_NOTWILD))
        ++emsg_silent;
    +__fp = fopen("../log", "a");
    +fprintf(__fp, "%*s    pat: %s\n", __l * 4, "", pat);
    +fclose(__fp);
         regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
         if (flags & (EW_NOERROR | EW_NOTWILD))
        --emsg_silent;
    @@ -10292,12 +10305,15 @@ unix_expandpath(
         {
        STRCPY(s, path_end + 1);
        ++stardepth;
    -   (void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE);
    +   (void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE, __l + 1);
        --stardepth;
         }
    
         /* open the directory for scanning */
         *s = NUL;
    +__fp = fopen("../log", "a");
    +fprintf(__fp, "%*s    inspecting: %s\n", __l * 4, "", *buf == NUL ? "." : buf);
    +fclose(__fp);
         dirp = opendir(*buf == NUL ? "." : (char *)buf);
    
         /* Find all matching entries */
    @@ -10308,6 +10324,9 @@ unix_expandpath(
            dp = readdir(dirp);
            if (dp == NULL)
            break;
    +__fp = fopen("../log", "a");
    +fprintf(__fp, "%*s    entry: %s\n", __l * 4, "", dp->d_name);
    +fclose(__fp);
            if ((dp->d_name[0] != '.' || starts_with_dot
                || ((flags & EW_DODOT)
                    && dp->d_name[1] != NUL
    @@ -10327,7 +10346,7 @@ unix_expandpath(
                STRCPY(buf + len, "/**");
                STRCPY(buf + len + 3, path_end);
                ++stardepth;
    -           (void)unix_expandpath(gap, buf, len + 1, flags, TRUE);
    +           (void)unix_expandpath(gap, buf, len + 1, flags, TRUE, __l + 1);
                --stardepth;
            }
    
    @@ -10336,12 +10355,15 @@ unix_expandpath(
            {
                /* need to expand another component of the path */
                /* remove backslashes for the remaining components only */
    -           (void)unix_expandpath(gap, buf, len + 1, flags, FALSE);
    +           (void)unix_expandpath(gap, buf, len + 1, flags, FALSE, __l + 1);
            }
            else
            {
                stat_T  sb;
    
    +__fp = fopen("../log", "a");
    +fprintf(__fp, "%*s    found: %s\n", __l * 4, "", buf);
    +fclose(__fp);
                /* no more wildcards, check if there is a match */
                /* remove backslashes for the remaining components only */
                if (*path_end != NUL)
    @@ -10362,6 +10384,9 @@ unix_expandpath(
                }
     #endif
                addfile(gap, buf, flags);
    +__fp = fopen("../log", "a");
    +fprintf(__fp, "%*s    added: %s\n", __l * 4, "", buf);
    +fclose(__fp);
                }
            }
            }
    diff --git a/src/os_unix.c b/src/os_unix.c
    index ff0b0e88d..40c1236fe 100644
    --- a/src/os_unix.c
    +++ b/src/os_unix.c
    @@ -6255,7 +6255,7 @@ mch_expandpath(
         char_u *path,
         int        flags)      /* EW_* flags */
     {
    -    return unix_expandpath(gap, path, 0, flags, FALSE);
    +    return unix_expandpath(gap, path, 0, flags, FALSE, 0);
     }
     #endif
    
    diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro
    index 4e299e5f9..50319b6d5 100644
    --- a/src/proto/misc1.pro
    +++ b/src/proto/misc1.pro
    @@ -98,7 +98,7 @@ void fast_breakcheck(void);
     int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, int flags);
     int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, int flags);
     int match_suffix(char_u *fname);
    -int unix_expandpath(garray_T *gap, char_u *path, int wildoff, int flags, int didstar);
    +int unix_expandpath(garray_T *gap, char_u *path, int wildoff, int flags, int didstar, int __l);
     void remove_duplicates(garray_T *gap);
     int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags);
     void addfile(garray_T *gap, char_u *f, int flags);
    
    $ ./configure CFLAGS='-g -O0 -gdwarf-2 -g3' CXXFLAGS='-g -O0 -gdwarf-2 -g3'
    $ make
    

    -g - add debugging information
    -O0 - reduce optimizations
    -gdwarf-2 -g3 - include information about preprocessor macros

  2. Create test directory:

    $ mkdir -p a/b b
    $ touch a/b/c b/c
    $ tree
    .
    ├── a
    │   └── b
    │       └── c
    └── b
        └── c
    
  3. Run gdb:

    $ rm ../log; gdb --args ../vim/src/vim -u NONE
    (gdb) break mch_expandpath
    (gdb) r
    :args **/b/**/*
    (gdb) n
    
  4. cat ../log:

    unix_expandpath: path: **/b/**/*, wildoff: 0, flags: f, didstar: 0
        pat: .*
        (1) unix_expandpath: path: b/**/*, wildoff: 0, flags: f, didstar: 1
            pat: .*
            inspecting: b/
            entry: ..
            entry: c
            unix_expandpath: path: b/c/**/*, wildoff: 4, flags: f, didstar: 1
                pat: .*
                inspecting: b/c/
            unix_expandpath: path: b/c/*, wildoff: 4, flags: f, didstar: 0
                pat: .*
                inspecting: b/c/
            entry: .
        inspecting: .
        entry: a
        unix_expandpath: path: a/**/b/**/*, wildoff: 2, flags: f, didstar: 1
            pat: .*
            inspecting: a/
            entry: b
            unix_expandpath: path: a/b/**/b/**/*, wildoff: 4, flags: f, didstar: 1
                pat: .*
                inspecting: a/b/
                entry: ..
                entry: c
                unix_expandpath: path: a/b/c/**/b/**/*, wildoff: 6, flags: f, didstar: 1
                    pat: .*
                    inspecting: a/b/c/
                unix_expandpath: path: a/b/c/b/**/*, wildoff: 6, flags: f, didstar: 0
                    pat: .*
                    unix_expandpath: path: a/b/c/b/*, wildoff: 8, flags: f, didstar: 1
                        pat: .*
                        inspecting: a/b/c/b/
                    inspecting: a/b/c/b/
                entry: .
            unix_expandpath: path: a/b/b/**/*, wildoff: 4, flags: f, didstar: 0
                pat: .*
                unix_expandpath: path: a/b/b/*, wildoff: 6, flags: f, didstar: 1
                    pat: .*
                    inspecting: a/b/b/
                inspecting: a/b/b/
            entry: ..
            entry: .
        (2) unix_expandpath: path: a/b/**/*, wildoff: 2, flags: f, didstar: 0
            pat: .*
            unix_expandpath: path: a/b/*, wildoff: 4, flags: f, didstar: 1
                pat: .*
                inspecting: a/b/
                entry: ..
                entry: c
                found: a/b/c
                added: a/b/c
                entry: .
            inspecting: a/b/
            entry: ..
            entry: c
            unix_expandpath: path: a/b/c/**/*, wildoff: 6, flags: f, didstar: 1
                gap:
                    a/b/c
                pat: .*
                inspecting: a/b/c/
            unix_expandpath: path: a/b/c/*, wildoff: 6, flags: f, didstar: 0
                gap:
                    a/b/c
                pat: .*
                inspecting: a/b/c/
            entry: .
        entry: b
        unix_expandpath: path: b/**/b/**/*, wildoff: 2, flags: f, didstar: 1
            gap:
                a/b/c
            pat: .*
            inspecting: b/
            entry: ..
            entry: c
            unix_expandpath: path: b/c/**/b/**/*, wildoff: 4, flags: f, didstar: 1
                gap:
                    a/b/c
                pat: .*
                inspecting: b/c/
            unix_expandpath: path: b/c/b/**/*, wildoff: 4, flags: f, didstar: 0
                gap:
                    a/b/c
                pat: .*
                unix_expandpath: path: b/c/b/*, wildoff: 6, flags: f, didstar: 1
                    gap:
                        a/b/c
                    pat: .*
                    inspecting: b/c/b/
                inspecting: b/c/b/
            entry: .
        unix_expandpath: path: b/b/**/*, wildoff: 2, flags: f, didstar: 0
            gap:
                a/b/c
            pat: .*
            unix_expandpath: path: b/b/*, wildoff: 4, flags: f, didstar: 1
                gap:
                    a/b/c
                pat: .*
                inspecting: b/b/
            inspecting: b/b/
        entry: ..
        entry: .
    

Here we can see that if unix_expandpath (1) had received didstar == 0, as unix_expandpath (2) did, b/c would have matched.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.