Good morning.

On Mon, Nov 18, 2019 at 1:05 AM Paul Eggert <egg...@cs.ucla.edu> 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;
 }

Reply via email to