Good morning.
If pattern has a trailing slash match directories only.
This patch also makes glob return directories and symlinks to directories only
when GLOB_ONLYDIR is specified.
The original version of this patch was discussed here
https://sourceware.org/ml/libc-alpha/2017-11/msg00983.html.
Tested on linux, sunos and aix, 32 and 64 bits.
This patch can be applied with patch -l. This keeps indentation messed
up though. Alternatively, the patch can be manually merged.
regards, Dmitry
diff --git a/lib/glob.c b/lib/glob.c
index 80233fdec..0e296b114 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -1111,16 +1111,22 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
}
}
- if (flags & GLOB_MARK)
+ /* The following loop filters out files and symlinks to files.
+ This is needed when readdir_result_type returns DT_UNKNOWN and DT_LNK.
+ If GLOB_MARK is set the loop also marks directories. */
+ if ((flags & GLOB_MARK) || (flags & GLOB_ONLYDIR))
{
/* Append slashes to directory names. */
- size_t i;
+ 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))
{
+ char *new;
+ if (flags & GLOB_MARK)
+ {
size_t len = strlen (pglob->gl_pathv[i]) + 2;
- char *new = realloc (pglob->gl_pathv[i], len);
+ new = realloc (pglob->gl_pathv[i], len);
if (new == NULL)
{
globfree (pglob);
@@ -1129,8 +1135,46 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
goto out;
}
strcpy (&new[len - 2], "/");
+ }
+ else
+ new = pglob->gl_pathv[i];
+ 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 ((flags & GLOB_NOCHECK) && pglob->gl_pathc == 0)
+ {
+ size_t len = strlen (pattern) + 2;
+ char *new = malloc (len);
+ if (new == NULL)
+ {
+ globfree (pglob);
+ pglob->gl_pathc = 0;
+ retval = GLOB_NOSPACE;
+ goto out;
+ }
+ memcpy (new, pattern, len - 2);
+ new[len - 2] = '/';
+ new[len - 1] = '\0';
+ pglob->gl_pathv[pglob->gl_offs] = new;
+ pglob->gl_pathc = 1;
}
+ else if (pglob->gl_pathc == 0)
+ retval = GLOB_NOMATCH;
}
if (!(flags & GLOB_NOSORT))
diff --git a/tests/test-glob.c b/tests/test-glob.c
index 74cde45c2..bc267d7a6 100644
--- a/tests/test-glob.c
+++ b/tests/test-glob.c
@@ -28,17 +28,183 @@ 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 "macros.h"
+static int
+touch (const char *p)
+{
+ int fd = open (p, O_CREAT, S_IRUSR|S_IWUSR);
+ ASSERT (fd > 0);
+ close (fd);
+ return 0;
+}
+
+static int
+md (const char *p)
+{
+ int rc = mkdir (p, 0770);
+ ASSERT (rc == 0);
+ return rc;
+}
+
+static int
+ln (const char *old, const char *new)
+{
+ int rc = link(old, new);
+ ASSERT (rc == 0);
+ return rc;
+}
+
+static int
+ln_s (const char *old, const char *new)
+{
+ int rc = symlink(old, new);
+ ASSERT (rc == 0);
+ return rc;
+}
+
+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 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 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_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 int verbose = 0;
+
+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 (k >= g->gl_pathc)
+ break;
+ if (verbose != 0)
+ printf ("%s\n", s);
+ 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 != 0)
+ printf ("pattern=%s, flags=%s(0x%x), offs=%zu\n", pattern,
+ flags2str(flags), 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 != 0)
+ printf ("pattern=%s, flags=%s(0x%x), offs=%zu\n", pattern,
+ flags2str(flags), 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 == GLOB_NOMATCH)
+ test (0, flags|GLOB_NOCHECK, 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");
+ 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 +252,162 @@ main ()
ASSERT (strcmp (g.gl_pathv[1], BASE "globlink2/") == 0);
globfree (&g);
}
+ unlink (BASE "globlink1");
+ unlink (BASE "globlink2");
+
+ /* Test that / matches explicitly. */
+ cleanup ();
+
+ test (GLOB_NOMATCH, 0, "", NULL);
+ test (GLOB_NOMATCH, 0, "hello", NULL);
+ test (GLOB_NOMATCH, 0, "hello*", NULL);
+ test (GLOB_NOMATCH, 0, "hello*/", NULL);
+
+ touch ("hellof");
+ test (0, 0, "hello*", "hellof", NULL);
+ test (GLOB_NOMATCH, 0, "hello*/", NULL);
+
+ md ("hellod");
+ 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, "hello*", "hellod", "hellods", 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_ONLYDIR, "hellod/world*", "hellod/worldd", 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_ONLYDIR, "hello*/world*", "hellod/worldd", "hellods/worldd",
+ 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_ONLYDIR, "hellod/*", "hellod/worldd", 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_ONLYDIR, "*/world*", "hellod/worldd", "hellods/worldd", 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_ONLYDIR|GLOB_BRACE, "{hello*/{,world*},hellof}",
+ "hellod/", "hellods/", "hellod/worldd", "hellods/worldd", 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/", 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_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*/", "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_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, 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);
+
+
+ cleanup ();
return 0;
}