diff --git a/lib/glob.c b/lib/glob.c index 80233fdec..5a014c94c 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -487,6 +487,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), if (__glibc_unlikely (pattern[0] == '\0')) { dirs.gl_pathv = NULL; + dirname = (char *) ""; goto no_matches; } @@ -506,6 +507,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), } else { + /* "pattern/" or "dir/path". */ char *newp; dirlen = filename - pattern; @@ -539,6 +541,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), } *((char *) mempcpy (newp, pattern, dirlen)) = '\0'; dirname = newp; + assert (strcmp (dirname, pattern) != 0); ++filename; #if defined __MSDOS__ || defined WINDOWS32 @@ -553,7 +556,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), if (filename[0] == '\0' && dirlen > 1 && !drive_root) /* "pattern/". Expand "pattern", appending slashes. */ { - int orig_flags = flags; if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\') { /* "pattern\\/". Remove the final backslash if it hasn't @@ -562,20 +564,25 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), while (p > dirname && p[-1] == '\\') --p; if ((&dirname[dirlen] - p) & 1) - { *(char *) &dirname[--dirlen] = '\0'; - flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC); - } } - int val = glob (dirname, flags | GLOB_MARK, errfunc, pglob); + /* dirname does not have a trailing slash while pattern does. + This recursive call to glob won't be able to do GLOB_NOCHECK + correctly because dirname != pattern. + Reset GLOB_NOCHECK | GLOB_NOMAGIC for the recursive call and in + the case of no match let the code under no_matches handle + GLOB_NOCHECK | GLOB_NOMAGIC. */ + int val = glob (dirname, + (flags | GLOB_MARK) & ~(GLOB_NOCHECK | GLOB_NOMAGIC), + errfunc, pglob); if (val == 0) pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK) | (flags & GLOB_MARK)); - else if (val == GLOB_NOMATCH && flags != orig_flags) + else if (val == GLOB_NOMATCH && + (flags & (GLOB_NOCHECK | GLOB_NOMAGIC))) { /* Make sure globfree (&dirs); is a nop. */ dirs.gl_pathv = NULL; - flags = orig_flags; oldcount = pglob->gl_pathc + pglob->gl_offs; goto no_matches; } @@ -1007,7 +1014,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), { no_matches: /* No matches. */ - if (flags & GLOB_NOCHECK) + meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE)); + if (flags & GLOB_NOCHECK || + (meta == GLOBPAT_NONE && (flags & GLOB_NOMAGIC))) { size_t newcount = pglob->gl_pathc + pglob->gl_offs; char **new_gl_pathv; @@ -1041,6 +1050,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), pglob->gl_pathv[newcount] = NULL; pglob->gl_flags = flags; + globfree (&dirs); + goto out; } else { @@ -1113,10 +1124,11 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), if (flags & GLOB_MARK) { - /* Append slashes to directory names. */ - size_t i; + /* Append slashes to directory names. + If GLOB_ONLYDIR is set filter out files and symlinks to files. */ + size_t i, e; - for (i = oldcount; i < pglob->gl_pathc + pglob->gl_offs; ++i) + for (i = e = oldcount; i < pglob->gl_pathc + pglob->gl_offs; ++i) if (is_dir (pglob->gl_pathv[i], flags, pglob)) { size_t len = strlen (pglob->gl_pathv[i]) + 2; @@ -1129,8 +1141,44 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), goto out; } strcpy (&new[len - 2], "/"); + if (pglob->gl_pathv[e] == NULL) + { + pglob->gl_pathv[e++] = new; + pglob->gl_pathv[i] = NULL; + } + else pglob->gl_pathv[i] = new; + } + else if (flags & GLOB_ONLYDIR) + { + free (pglob->gl_pathv[i]); + pglob->gl_pathv[i] = NULL; + if (pglob->gl_pathv[e] != NULL) + e = i; + } + if (pglob->gl_pathv[e] == NULL) + pglob->gl_pathc = e - pglob->gl_offs; + if (pglob->gl_pathc + pglob->gl_offs <= oldcount && + ((flags & GLOB_NOCHECK) || + (meta == GLOBPAT_NONE && (flags & GLOB_NOMAGIC)))) + { + /* Top level glob call. */ + pglob->gl_pathv[oldcount] = strdup(pattern); + if (pglob->gl_pathv[oldcount] == NULL) + { + globfree (pglob); + pglob->gl_pathc = 0; + retval = GLOB_NOSPACE; + goto out; + } + if (flags & GLOB_APPEND) + pglob->gl_pathc = oldcount - pglob->gl_offs + 1; + else + pglob->gl_pathc = 1; + } + else if (pglob->gl_pathc + pglob->gl_offs <= oldcount) + retval = GLOB_NOMATCH; } if (!(flags & GLOB_NOSORT)) diff --git a/tests/test-glob.c b/tests/test-glob.c index 74cde45c2..51e573c52 100644 --- a/tests/test-glob.c +++ b/tests/test-glob.c @@ -28,17 +28,230 @@ SIGNATURE_CHECK (globfree, void, (glob_t *)); #include #include #include +#include +#include +#include +#include +#include #include "macros.h" +static void +touch (const char *p) +{ + int fd = open (p, O_CREAT, S_IRUSR | S_IWUSR); + ASSERT (fd > 0); + close (fd); +} + +static void +md (const char *p) +{ + int rc = mkdir (p, 0770); + ASSERT (rc == 0); +} + +static void +cd (const char *p) +{ + int rc = chdir (p); + ASSERT (rc == 0); +} + +static void +ln (const char *old, const char *new) +{ + int rc = link (old, new); + ASSERT (rc == 0); +} + +static void +ln_s (const char *old, const char *new) +{ + int rc = symlink (old, new); + ASSERT (rc == 0); +} + +static const char * +flags2str (int flags) +{ + if (flags == 0) + return "0"; + + static const char mark[] = "GLOB_MARK"; + static const char dooffs[] = "GLOB_DOOFFS"; + static const char nocheck[] = "GLOB_NOCHECK"; + static const char nomagic[] = "GLOB_NOMAGIC"; + static const char append[] = "GLOB_APPEND"; + static const char brace[] = "GLOB_BRACE"; + static const char onlydir[] = "GLOB_ONLYDIR"; + static const char p[] = "|"; + const size_t nmark = sizeof (mark) - 1; + const size_t ndooffs = sizeof (dooffs) - 1; + const size_t nnocheck = sizeof (nocheck) - 1; + const size_t nnomagic= sizeof (nomagic) - 1; + const size_t nappend = sizeof (append) - 1; + const size_t nbrace = sizeof (brace) - 1; + const size_t nonlydir = sizeof (onlydir) - 1; + + static char buf[1024]; /* No need to memset buf. */ + char *n = buf; + const char *d = ""; + if (flags & GLOB_MARK) + n = mempcpy (n, mark, nmark), d = p; + if (flags & GLOB_DOOFFS) + n = mempcpy (mempcpy (n, d, strlen (d)), dooffs, ndooffs), d = p; + if (flags & GLOB_NOCHECK) + n = mempcpy (mempcpy(n, d, strlen (d)), nocheck, nnocheck), d = p; + if (flags & GLOB_NOMAGIC) + n = mempcpy (mempcpy(n, d, strlen (d)), nomagic, nnomagic), d = p; + if (flags & GLOB_APPEND) + n = mempcpy (mempcpy(n, d, strlen (d)), append, nappend), d = p; + if (flags & GLOB_BRACE) + n = mempcpy (mempcpy(n, d, strlen (d)), brace, nbrace), d = p; + if (flags & GLOB_ONLYDIR) + n = mempcpy (mempcpy(n, d, strlen (d)), onlydir, nonlydir), d = p; + *n = '\0'; + return buf; +} + +static bool verbose = false; + +static void +cmp (size_t offs, size_t oldpathc, const glob_t *g, va_list ap) +{ + size_t k = oldpathc; + for (;;) + { + const char *s = va_arg (ap, const char *); + if (s == NULL) + break; + ASSERT (k < g->gl_pathc); + if (verbose) + printf ("%s\n", g->gl_pathv[k + offs]); + int rc = strcmp (g->gl_pathv[k + offs], s); + ASSERT (rc == 0); + ++k; + } + ASSERT (g->gl_pathc == k); +} + +static void +testimp (int rc, int flags, size_t offs, const char *pattern, va_list ap) +{ + int res; + glob_t g; + va_list ap2; + va_copy (ap2, ap); + + if (verbose) + printf ("pattern=%s, flags=0x%x (%s), offs=%zu\n", pattern, + flags, flags2str (flags), offs); + memset (&g, 0, sizeof g); + g.gl_offs = offs; + res = glob (pattern, flags, NULL, &g); + ASSERT (res == rc); + cmp (offs, 0, &g, ap); + if (rc != 0) + return; + + flags |= GLOB_APPEND; + if (verbose) + printf ("pattern=%s, flags=0x%x (%s), offs=%zu\n", pattern, + flags, flags2str (flags), offs); + size_t pathc = g.gl_pathc; + res = glob (pattern, flags, NULL, &g); + ASSERT (res == rc); + cmp (offs, pathc, &g, ap2); + + globfree (&g); +} + +static void +test (int rc, int flags, const char *pattern, ...) +{ + va_list ap; + + va_start (ap, pattern); + testimp (rc, flags, 0, pattern, ap); + va_end (ap); + + va_start (ap, pattern); + testimp (rc, flags | GLOB_DOOFFS, 2, pattern, ap); + va_end (ap); + + if (rc != 0) + return; + + va_start (ap, pattern); + testimp (rc, flags | GLOB_NOCHECK, 0, pattern, ap); + va_end (ap); + + va_start (ap, pattern); + testimp (rc, flags | GLOB_NOCHECK | GLOB_DOOFFS, 2, pattern, ap); + va_end (ap); +} + +static void +test_nocheck (int flags, const char *pattern) +{ + test (GLOB_NOMATCH, flags, pattern, NULL); + test (GLOB_NOMATCH, flags | GLOB_MARK, pattern, NULL); + test (GLOB_NOMATCH, flags | GLOB_ONLYDIR, pattern, NULL); + test (GLOB_NOMATCH, flags | GLOB_MARK | GLOB_ONLYDIR, pattern, NULL); + + test (0, flags | GLOB_NOCHECK, pattern, pattern, NULL); + test (0, flags | GLOB_NOCHECK | GLOB_MARK, pattern, pattern, NULL); + test (0, flags | GLOB_NOCHECK | GLOB_ONLYDIR, pattern, pattern, NULL); + test (0, flags | GLOB_NOCHECK | GLOB_MARK | GLOB_ONLYDIR, pattern, pattern, + NULL); +} + +static void +test_nomagic (int flags, const char *pattern) +{ + test (GLOB_NOMATCH, flags, pattern, NULL); + test (GLOB_NOMATCH, flags | GLOB_MARK, pattern, NULL); + test (GLOB_NOMATCH, flags | GLOB_ONLYDIR, pattern, NULL); + test (GLOB_NOMATCH, flags | GLOB_MARK | GLOB_ONLYDIR, pattern, NULL); + + test (0, flags | GLOB_NOMAGIC, pattern, pattern, NULL); + test (0, flags | GLOB_NOMAGIC | GLOB_MARK, pattern, pattern, NULL); + test (0, flags | GLOB_NOMAGIC | GLOB_ONLYDIR, pattern, pattern, NULL); + test (0, flags | GLOB_NOMAGIC | GLOB_MARK | GLOB_ONLYDIR, pattern, pattern, + NULL); +} + + +static void +cleanup () +{ + unlink ("hellod/worldd/kenf1"); + unlink ("hellod/worldd/kenf2"); + unlink ("hellod/worldf"); + unlink ("hellofbs"); + unlink ("hellofs"); + unlink ("hellofl"); + unlink ("hellof"); + unlink ("hello"); + rmdir ("hello"); + rmdir ("hellod/worldd/kend1"); + rmdir ("hellod/worldd/kend2"); + rmdir ("hellod/worldd"); + unlink ("hellods"); + rmdir ("hellod"); +} + #define BASE "test-glob.t" #define GL_NO_SUCH_FILE "/gnulib-magic-does-not-exist" int -main () +main (int argc, char *argv[]) { int res; glob_t g; + verbose = argc > 1; + (void) argv; res = glob (".", 0, NULL, &g); ASSERT (res == 0 && g.gl_pathc == 1); @@ -86,6 +299,308 @@ main () ASSERT (strcmp (g.gl_pathv[1], BASE "globlink2/") == 0); globfree (&g); } + unlink (BASE "globlink1"); + unlink (BASE "globlink2"); + + /* Test that / matches explicitly. */ + cleanup (); + + test_nocheck (0, ""); + test_nomagic (0, ""); + test_nocheck (0, "hello"); + test_nomagic (0, "hello"); + test_nocheck (0, "hello/"); + test_nomagic (0, "hello/"); + test_nocheck (0, "hello*"); + test_nocheck (0, "hello*/"); + test_nocheck (0, "hello*/world*"); + test_nocheck (0, "hello*/world*/"); + test_nocheck (0, "hellod/*/ken?[12]"); + + test (0, 0, "/", "/", NULL); + test (0, GLOB_MARK, "/", "//", NULL); + test (0, GLOB_ONLYDIR, "/", "/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "/", "//", NULL); + + test (0, 0, "//", "/", NULL); + test (0, GLOB_MARK, "//", "//", NULL); + test (0, GLOB_ONLYDIR, "//", "/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "//", "//", NULL); + + test (0, 0, ".", ".", NULL); + test (0, GLOB_MARK, ".", "./", NULL); + test (0, GLOB_ONLYDIR, ".", ".", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, ".", "./", NULL); + + test (0, 0, "./", "./", NULL); + test (0, GLOB_MARK, "./", ".//", NULL); + test (0, GLOB_ONLYDIR, "./", "./", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "./", ".//", NULL); + + test (0, 0, ".//", ".//", NULL); + test (0, GLOB_MARK, ".//", ".//", NULL); + test (0, GLOB_ONLYDIR, ".//", ".//", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, ".//", ".//", NULL); + + test (0, 0, "..", "..", NULL); + test (0, GLOB_MARK, "..", "../", NULL); + test (0, GLOB_ONLYDIR, "..", "..", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "..", "../", NULL); + + test (0, 0, "../", "../", NULL); + test (0, GLOB_MARK, "../", "../", NULL); + test (0, GLOB_ONLYDIR, "../", "../", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "../", "../", NULL); + + test (0, 0, "..//", "../", NULL); + test (0, GLOB_MARK, "..//", "../", NULL); + test (0, GLOB_ONLYDIR, "..//", "../", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "..//", "../", NULL); + + test (0, 0, "/tmp", "/tmp", NULL); + test (0, GLOB_MARK, "/tmp", "/tmp/", NULL); + test (0, GLOB_ONLYDIR, "/tmp", "/tmp", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "/tmp", "/tmp/", NULL); + + test (0, 0, "/tmp/", "/tmp/", NULL); + test (0, GLOB_MARK, "/tmp/", "/tmp/", NULL); + test (0, GLOB_ONLYDIR, "/tmp/", "/tmp/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "/tmp/", "/tmp/", NULL); + + test (0, 0, "/tmp//", "/tmp/", NULL); + test (0, GLOB_MARK, "/tmp//", "/tmp/", NULL); + test (0, GLOB_ONLYDIR, "/tmp//", "/tmp/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "/tmp//", "/tmp/", NULL); + + touch ("hello"); + test (0, 0, "hello", "hello", NULL); + test (0, GLOB_MARK, "hello", "hello", NULL); + test_nocheck (GLOB_MARK|GLOB_ONLYDIR, "hello"); + test_nomagic (GLOB_MARK|GLOB_ONLYDIR, "hello"); + test_nocheck (0, "hello/"); + test_nomagic (0, "hello/"); + test_nocheck (0, "hello*/"); + unlink ("hello"); + + md ("hello"); + test (0, 0, "hello", "hello", NULL); + test (0, GLOB_MARK, "hello", "hello/", NULL); + test (0, GLOB_ONLYDIR, "hello", "hello", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hello", "hello/", NULL); + + test (0, 0, "hello/", "hello/", NULL); + test (0, GLOB_MARK, "hello/", "hello/", NULL); + test (0, GLOB_ONLYDIR, "hello/", "hello/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hello/", "hello/", NULL); + + test (0, 0, "hello*", "hello", NULL); + test (0, GLOB_MARK, "hello*", "hello/", NULL); + test (0, GLOB_ONLYDIR, "hello*", "hello", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hello*", "hello/", NULL); + + test (0, 0, "hello*/", "hello/", NULL); + test (0, GLOB_MARK, "hello*/", "hello/", NULL); + test (0, GLOB_ONLYDIR, "hello*/", "hello/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hello*/", "hello/", NULL); + rmdir ("hello"); + + touch ("hellof"); + test (0, 0, "hello*", "hellof", NULL); + test (GLOB_NOMATCH, 0, "hello*/", NULL); + test (GLOB_NOMATCH, GLOB_MARK, "hello*/", NULL); + test (GLOB_NOMATCH, GLOB_MARK | GLOB_ONLYDIR, "hello*/", NULL); + + md ("hellod"); + cd ("hellod"); + + test_nocheck (0, "../hellod/world*"); + test_nocheck (0, "../hello*/world*"); + test_nocheck (0, "../hello*/world*/"); + test_nocheck (0, "../hello*/world[d]/"); + test (GLOB_NOMATCH, GLOB_NOMAGIC, "../hellod/world*", NULL); + test (GLOB_NOMATCH, GLOB_NOMAGIC, "../hellod/world[d]", NULL); + test (GLOB_NOMATCH, GLOB_NOMAGIC, "../hellod/world*/", NULL); + test (GLOB_NOMATCH, GLOB_NOMAGIC, "../hellod/world[d]/", NULL); + + cd (".."); + + ln_s ("hellod", "hellods"); + ln_s ("broken_symlink", "hellofbs"); + ln ("hellof", "hellofl"); + ln_s ("hellof", "hellofs"); + touch ("hellod/worldf"); + md ("hellod/worldd"); + + test (0, 0, "hello*", "hellod", "hellods", "hellof", "hellofbs", + "hellofl", "hellofs", NULL); + test (0, GLOB_MARK, "hello*", "hellod/", "hellods/", "hellof", + "hellofbs", "hellofl", "hellofs", NULL); + test (0, GLOB_ONLYDIR | GLOB_MARK, "hello*", "hellod/", "hellods/", NULL); + + test (0, 0, "hello*/", "hellod/", "hellods/", NULL); + + test (0, GLOB_MARK, "hello*/", "hellod/", "hellods/", NULL); + + test (0, GLOB_ONLYDIR, "hello*/", "hellod/", "hellods/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hello*/", "hellod/", "hellods/", NULL); + + test (0, 0, "hellod/world*", "hellod/worldd", "hellod/worldf", NULL); + test (0, GLOB_MARK, "hellod/world*", "hellod/worldd/", "hellod/worldf", + NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hellod/world*", "hellod/worldd/", NULL); + + test (0, 0, "hello*/world*", "hellod/worldd", "hellod/worldf", + "hellods/worldd", "hellods/worldf", NULL); + test (0, GLOB_MARK, "hello*/world*", "hellod/worldd/", "hellod/worldf", + "hellods/worldd/", "hellods/worldf", NULL); + + test (0, GLOB_MARK | GLOB_ONLYDIR, "hello*/world*", "hellod/worldd/", + "hellods/worldd/", NULL); + + test (0, 0, "hellod/world*/", "hellod/worldd/", NULL); + test (0, GLOB_MARK, "hellod/world*/", "hellod/worldd/", NULL); + test (0, GLOB_ONLYDIR, "hellod/world*/", "hellod/worldd/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hellod/world*/", "hellod/worldd/", NULL); + + test (0, 0, "hellod/*", "hellod/worldd", "hellod/worldf", NULL); + test (0, GLOB_MARK, "hellod/*", "hellod/worldd/", "hellod/worldf", NULL); + + test (0, GLOB_MARK | GLOB_ONLYDIR, "hellod/*", "hellod/worldd/", NULL); + + test (0, 0, "hellod/*/", "hellod/worldd/", NULL); + test (0, GLOB_MARK, "hellod/*/", "hellod/worldd/", NULL); + test (0, GLOB_ONLYDIR, "hellod/*/", "hellod/worldd/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hellod/*/", "hellod/worldd/", NULL); + + test (0, 0, "*/world*", "hellod/worldd", "hellod/worldf", "hellods/worldd", + "hellods/worldf", NULL); + test (0, GLOB_MARK, "*/world*", "hellod/worldd/", "hellod/worldf", + "hellods/worldd/", "hellods/worldf", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "*/world*", "hellod/worldd/", + "hellods/worldd/", NULL); + + test (0, 0, "*/world*/", "hellod/worldd/", "hellods/worldd/", NULL); + test (0, GLOB_MARK, "*/world*/", "hellod/worldd/", "hellods/worldd/", NULL); + test (0, GLOB_ONLYDIR, "*/world*/", "hellod/worldd/", "hellods/worldd/", + NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "*/world*/", "hellod/worldd/", + "hellods/worldd/", NULL); + + + test (0, GLOB_BRACE, "hellod/{,worldd,worldf}", "hellod/", "hellod/worldd", + "hellod/worldf", NULL); + test (0, GLOB_BRACE, "{hellod/{,worldd,worldf},hellof}", "hellod/", + "hellod/worldd", "hellod/worldf", "hellof", NULL); + test (0, GLOB_BRACE, "{hello*/{,world*},hellof}", "hellod/", + "hellods/", "hellod/worldd", "hellod/worldf", "hellods/worldd", + "hellods/worldf", "hellof", NULL); + test (0, GLOB_MARK | GLOB_BRACE, "{hello*/{,world*},hellof}", + "hellod/", "hellods/", "hellod/worldd/", "hellod/worldf", + "hellods/worldd/", "hellods/worldf", "hellof", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR | GLOB_BRACE, + "{hello*/{,world*},hellof}", "hellod/", "hellods/", "hellod/worldd/", + "hellods/worldd/", NULL); + test (0, GLOB_BRACE, "{hello*/{,world*/},hellof}", "hellod/", + "hellods/", "hellod/worldd/", "hellods/worldd/", "hellof", NULL); + test (0, GLOB_MARK | GLOB_BRACE, "{hello*/{,world*/},hellof}", + "hellod/", "hellods/", "hellod/worldd/", "hellods/worldd/", "hellof", + NULL); + test (0, GLOB_ONLYDIR | GLOB_BRACE, "{hello*/{,world*/},hellof}", + "hellod/", "hellods/", "hellod/worldd/", "hellods/worldd/", "hellof", + NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR | GLOB_BRACE, + "{hello*/{,world*/},hellof}", "hellod/", "hellods/", + "hellod/worldd/", "hellods/worldd/", NULL); + + md ("hellod/worldd/kend1"); + md ("hellod/worldd/kend2"); + touch ("hellod/worldd/kenf1"); + touch ("hellod/worldd/kenf2"); + + test (0, 0, "hellod/*/ken*", "hellod/worldd/kend1", + "hellod/worldd/kend2", "hellod/worldd/kenf1", "hellod/worldd/kenf2", + NULL); + test (0, GLOB_MARK, "hellod/*/ken*", "hellod/worldd/kend1/", + "hellod/worldd/kend2/", "hellod/worldd/kenf1", "hellod/worldd/kenf2", + NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hellod/*/ken*", + "hellod/worldd/kend1/", "hellod/worldd/kend2/", NULL); + + + test (0, 0, "hellod/*/ken*/", "hellod/worldd/kend1/", "hellod/worldd/kend2/", + NULL); + test (0, GLOB_MARK, "hellod/*/ken*/", "hellod/worldd/kend1/", + "hellod/worldd/kend2/", NULL); + test (0, GLOB_ONLYDIR, "hellod/*/ken*/", "hellod/worldd/kend1/", + "hellod/worldd/kend2/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hellod/*/ken*/", + "hellod/worldd/kend1/", "hellod/worldd/kend2/", NULL); + + test (0, 0, "hellod/*/ken?[12]", "hellod/worldd/kend1", + "hellod/worldd/kend2", "hellod/worldd/kenf1", "hellod/worldd/kenf2", + NULL); + test (0, GLOB_MARK, "hellod/*/ken?[12]", "hellod/worldd/kend1/", + "hellod/worldd/kend2/", "hellod/worldd/kenf1", "hellod/worldd/kenf2", + NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hellod/*/ken?[12]", + "hellod/worldd/kend1/", "hellod/worldd/kend2/", NULL); + + + test (0, 0, "hellod/*/ken?[12]/", "hellod/worldd/kend1/", + "hellod/worldd/kend2/", NULL); + test (0, GLOB_MARK, "hellod/*/ken?[12]/", "hellod/worldd/kend1/", + "hellod/worldd/kend2/", NULL); + test (0, GLOB_ONLYDIR, "hellod/*/ken?[12]/", + "hellod/worldd/kend1/", "hellod/worldd/kend2/", NULL); + test (0, GLOB_MARK | GLOB_ONLYDIR, "hellod/*/ken?[12]/", + "hellod/worldd/kend1/", "hellod/worldd/kend2/", NULL); + + test (0, GLOB_BRACE, "{hello*{,world*/ken*[12]},hellof}", + "hellod", "hellods", "hellof", "hellofbs", "hellofl", "hellofs", + "hellof", NULL); + test_nocheck (0, "{hello*{,world*/ken*[12]},hellof}"); + + test (0, GLOB_BRACE, + "{hello*/{,world*/ken*[12]},hellof}", "hellod/", "hellods/", + "hellod/worldd/kend1", "hellod/worldd/kend2", + "hellod/worldd/kenf1", "hellod/worldd/kenf2", + "hellods/worldd/kend1", "hellods/worldd/kend2", + "hellods/worldd/kenf1", "hellods/worldd/kenf2", "hellof", NULL); + test_nocheck (0, "{hello*/{,world*/ken*[12]},hellof}"); + + test (0, GLOB_MARK | GLOB_BRACE, + "{hello*/{,world*/ken*[12]},hellof}", "hellod/", "hellods/", + "hellod/worldd/kend1/", "hellod/worldd/kend2/", + "hellod/worldd/kenf1", "hellod/worldd/kenf2", + "hellods/worldd/kend1/", "hellods/worldd/kend2/", + "hellods/worldd/kenf1", "hellods/worldd/kenf2", "hellof", NULL); + + test (0, GLOB_MARK | GLOB_ONLYDIR | GLOB_BRACE, + "{hello*/{,world*/ken*[12]},hellof}", "hellod/", "hellods/", + "hellod/worldd/kend1/", "hellod/worldd/kend2/", + "hellods/worldd/kend1/", "hellods/worldd/kend2/", NULL); + + cleanup (); + + test_nocheck (GLOB_BRACE, "{hello*{,world*/ken*[12]},hellof}"); + test_nocheck (GLOB_BRACE, "{hello*/{,world*/ken*[12]},hellof}"); + + test_nocheck (0, "hellod/*/ken*"); + test_nocheck (0, "hellod/*/ken*/"); + test_nocheck (0, "hellod/*/ken?[12]"); + test_nocheck (0, "hellod/*/ken?[12]/"); + + test_nocheck (0, "hellod/{,worldd,worldf}"); + test_nocheck (GLOB_BRACE, "hellod/{,worldd,worldf}"); + + test_nocheck (0, "{hellod/{,worldd,worldf},hellof}"); + test_nocheck (GLOB_BRACE, "{hellod/{,worldd,worldf},hellof}"); + + test_nocheck (0, "{hello*/{,world*},hellof}"); + test_nocheck (GLOB_BRACE, "{hello*/{,world*},hellof}"); + + test_nocheck (0, "{hello*/{,world*/},hellof}"); + test_nocheck (GLOB_BRACE, "{hello*/{,world*/},hellof}"); return 0; }