Good morning.
On Mon, Nov 18, 2019 at 1:05 AM Paul Eggert <[email protected]> wrote:
> Did you intend to attach a new version of the patch
> to the above-quoted email, or was that email merely commenting on the patch
> you
> sent on November 12
> <https://lists.gnu.org/r/bug-gnulib/2019-11/msg00023.html>?
i intended to attach a new version of the patch.
The difference from the previous version is in handling of
GLOB_NOCHECK and GLOB_NOMAGIC.
This patch has a bit of new code to handle GLOB_NOCHECK and
GLOB_NOMAGIC right after we filter out files.
It is possible though to replace this new piece of code with goto
no_matches. What do you think?
In addition to the trailing slash fix this patch also fixes glob ("",
GLOB_NOMAGIC, ...).
regards, Dmitry
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 <errno.h>
#include <string.h>
#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
#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;
}