Test for GNU extensions in glob.
authordrepper <drepper>
Wed, 14 Feb 2001 19:00:04 +0000 (19:00 +0000)
committerdrepper <drepper>
Wed, 14 Feb 2001 19:00:04 +0000 (19:00 +0000)
posix/tst-gnuglob.c [new file with mode: 0644]

diff --git a/posix/tst-gnuglob.c b/posix/tst-gnuglob.c
new file mode 100644 (file)
index 0000000..d3e6b00
--- /dev/null
@@ -0,0 +1,422 @@
+/* Test the GNU extensions in glob which allow the user to provide callbacks
+   for the filesystem access functions.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <glob.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+
+/* #define DEBUG */
+#ifdef DEBUG
+# define PRINTF(fmt, args...) printf (fmt, ##args)
+#else
+# define PRINTF(fmt, args...)
+#endif
+
+
+static struct
+{
+  const char *name;
+  int level;
+  int type;
+} filesystem[] =
+{
+  { ".", 1, DT_DIR },
+  { "..", 1, DT_DIR },
+  { "file1lev1", 1, DT_REG },
+  { "file2lev1", 1, DT_REG },
+  { "dir1lev1", 1, DT_DIR },
+    { ".", 2, DT_DIR },
+    { "..", 2, DT_DIR },
+    { "file1lev2", 2, DT_REG },
+    { "dir1lev2", 2, DT_DIR },
+      { ".", 3, DT_DIR },
+      { "..", 3, DT_DIR },
+    { "dir2lev2", 2, DT_DIR },
+      { ".", 3, DT_DIR },
+      { "..", 3, DT_DIR },
+      { ".foo", 3, DT_REG },
+      { "dir1lev3", 3, DT_DIR },
+        { ".", 4, DT_DIR },
+        { "..", 4, DT_DIR },
+        { "file1lev4", 4, DT_REG },
+      { "file1lev3", 3, DT_REG },
+      { "file2lev3", 3, DT_REG },
+    { "file2lev2", 2, DT_REG },
+    { "file3lev2", 2, DT_REG },
+    { "dir3lev2", 2, DT_DIR },
+      { ".", 3, DT_DIR },
+      { "..", 3, DT_DIR },
+      { "file3lev3", 3, DT_REG },
+      { "file4lev3", 3, DT_REG },
+  { "dir2lev1", 1, DT_DIR },
+    { ".", 2, DT_DIR },
+    { "..", 2, DT_DIR },
+    { "dir1lev2", 2, DT_DIR },
+      { ".", 3, DT_DIR },
+      { "..", 3, DT_DIR },
+      { ".foo", 3, DT_REG },
+      { ".dir", 3, DT_DIR },
+        { ".", 4, DT_DIR },
+        { "..", 4, DT_DIR },
+        { "hidden", 4, DT_REG }
+};
+#define nfiles (sizeof (filesystem) / sizeof (filesystem[0]))
+
+
+typedef struct
+{
+  int level;
+  int idx;
+  struct dirent d;
+} my_DIR;
+
+
+static long int
+find_file (const char *s)
+{
+  int level = 1;
+  long int idx = 0;
+
+  if (strcmp (s, ".") == 0)
+    return 0;
+
+  while (*s != '\0')
+    {
+      char *endp = strchrnul (s, '/');
+
+      PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
+
+      while (idx < nfiles && filesystem[idx].level >= level)
+       {
+         if (filesystem[idx].level == level
+             && memcmp (s, filesystem[idx].name, endp - s) == 0
+             && filesystem[idx].name[endp - s] == '\0')
+           break;
+         ++idx;
+       }
+
+      if (idx == nfiles || filesystem[idx].level < level)
+       {
+         errno = ENOENT;
+         return -1;
+       }
+      if (filesystem[idx].type != DT_DIR)
+       {
+         errno = ENOTDIR;
+         return -1;
+       }
+
+      ++idx;
+
+      if (*endp == '\0')
+       return idx;
+
+      s = endp + 1;
+      ++level;
+    }
+
+  errno = ENOENT;
+  return -1;
+}
+
+
+static void *
+my_opendir (const char *s)
+{
+  long int idx = find_file (s);
+  my_DIR *dir;
+
+
+  if (idx == -1)
+    {
+      PRINTF ("my_opendir(\"%s\") == NULL\n", s);
+      return NULL;
+    }
+
+  dir = (my_DIR *) malloc (sizeof (my_DIR));
+  if (dir == NULL)
+    error (EXIT_FAILURE, errno, "cannot allocate directory handle");
+
+  dir->level = filesystem[idx].level;
+  dir->idx = idx;
+
+  PRINTF ("my_opendir(\"%s\") == { level: %d, idx: %ld }\n",
+         s, filesystem[idx].level, idx);
+
+  return dir;
+}
+
+
+static struct dirent *
+my_readdir (void *gdir)
+{
+  my_DIR *dir = gdir;
+
+  if (dir->idx == -1)
+    {
+      PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
+             dir->level, (long int) dir->idx);
+      return NULL;
+    }
+
+  while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)
+    ++dir->idx;
+
+  if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)
+    {
+      dir->idx = -1;
+      PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
+             dir->level, (long int) dir->idx);
+      return NULL;
+    }
+
+  dir->d.d_ino = dir->idx;
+  dir->d.d_type = filesystem[dir->idx].type;
+  strcpy (dir->d.d_name, filesystem[dir->idx].name);
+
+  PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_type: %d, d_name: \"%s\" }\n",
+         dir->level, (long int) dir->idx, dir->d.d_ino, dir->d.d_type,
+         dir->d.d_name);
+
+  ++dir->idx;
+
+  return &dir->d;
+}
+
+
+static void
+my_closedir (void *dir)
+{
+  PRINTF ("my_closedir ()\n");
+  free (dir);
+}
+
+
+/* We use this function for lstat as well since we don't have any.  */
+static int
+my_stat (const char *name, struct stat *st)
+{
+  long int idx = find_file (name);
+
+  if (idx == -1)
+    {
+      PRINTF ("my_stat (\"%s\", ...) = -1 (%s)\n", name, strerror (errno));
+      return -1;
+    }
+
+  memset (st, '\0', sizeof (*st));
+
+  st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
+
+  PRINTF ("my_stat (\"%s\", { st_mode: %o }) = 0\n", name, st->st_mode);
+
+  return 0;
+}
+
+
+static const char *glob_errstring[] =
+{
+  [GLOB_NOSPACE] = "out of memory",
+  [GLOB_ABORTED] = "read error",
+  [GLOB_NOMATCH] = "no matches found"
+};
+#define nglob_errstring (sizeof (glob_errstring) / sizeof (glob_errstring[0]))
+
+
+static const char *
+flagstr (int flags)
+{
+  const char *strs[] =
+  {
+    "GLOB_ERR", "GLOB_MARK", "GLOB_NOSORT", "GLOB_DOOFSS", "GLOB_NOCHECK",
+    "GLOB_APPEND", "GLOB_NOESCAPE", "GLOB_PERIOD", "GLOB_MAGCHAR",
+    "GLOB_ALTDIRFUNC", "GLOB_BRACE", "GLOB_NOMAGIC", "GLOB_TILDE",
+    "GLOB_ONLYDIR", "GLOB_TILDECHECK"
+  };
+#define nstrs (sizeof (strs) / sizeof (strs[0]))
+  static char buf[100];
+  char *cp = buf;
+  int cnt;
+
+  for (cnt = 0; cnt < nstrs; ++cnt)
+    if (flags & (1 << cnt))
+      {
+       flags &= ~(1 << cnt);
+       if (cp != buf)
+         *cp++ = '|';
+       cp = stpcpy (cp, strs[cnt]);
+      }
+
+  if (flags != 0)
+    {
+      if (cp != buf)
+       *cp++ = '|';
+      sprintf (cp, "%#x", flags);
+    }
+
+  return buf;
+}
+
+
+static int
+test_result (const char *fmt, int flags, glob_t *gl, const char *str[])
+{
+  size_t cnt;
+  int result = 0;
+
+  printf ("results for glob (\"%s\", %s)\n", fmt, flagstr (flags));
+  for (cnt = 0; cnt < gl->gl_pathc && str[cnt] != NULL; ++cnt)
+    {
+      int ok = strcmp (gl->gl_pathv[cnt], str[cnt]) == 0;
+      const char *errstr = "";
+
+      if (! ok)
+       {
+         size_t inner;
+
+         for (inner = 0; str[inner] != NULL; ++inner)
+           if (strcmp (gl->gl_pathv[cnt], str[inner]) == 0)
+             break;
+
+         if (str[inner] == NULL)
+           errstr =  ok ? "" : " *** WRONG";
+         else
+           errstr = ok ? "" : " * wrong position";
+
+         result = 1;
+       }
+
+      printf ("  %s%s\n", gl->gl_pathv[cnt], errstr);
+    }
+  puts ("");
+
+  if (str[cnt] != NULL || cnt < gl->gl_pathc)
+    {
+      puts ("  *** incorrect number of entries");
+      result = 1;
+    }
+
+  return result;
+}
+
+
+int
+main (void)
+{
+  glob_t gl;
+  int errval;
+  int result = 0;
+  const char *fmt;
+  int flags;
+
+  mtrace ();
+
+  memset (&gl, '\0', sizeof (gl));
+
+  gl.gl_closedir = my_closedir;
+  gl.gl_readdir = my_readdir;
+  gl.gl_opendir = my_opendir;
+  gl.gl_lstat = my_stat;
+  gl.gl_stat = my_stat;
+
+#define test(a, b, c...) \
+  fmt = a;                                                                   \
+  flags = b;                                                                 \
+  errval = glob (fmt, flags, NULL, &gl);                                     \
+  if (errval != 0)                                                           \
+    {                                                                        \
+      printf ("glob (\"%s\", %s) failed: %s\n", fmt, flagstr (flags),        \
+             errval >= 0 && errval < nglob_errstring                         \
+             ? glob_errstring[errval] : "???");                              \
+      result = 1;                                                            \
+    }                                                                        \
+  else                                                                       \
+    result |= test_result (fmt, flags, &gl, (const char *[]) { c, NULL })
+
+  test ("*/*/*", GLOB_ALTDIRFUNC,
+       "dir1lev1/dir2lev2/dir1lev3",
+       "dir1lev1/dir2lev2/file1lev3",
+       "dir1lev1/dir2lev2/file2lev3",
+       "dir1lev1/dir3lev2/file3lev3",
+       "dir1lev1/dir3lev2/file4lev3");
+
+  test ("*/*/*", GLOB_ALTDIRFUNC | GLOB_PERIOD,
+       "dir1lev1/dir1lev2/.",
+       "dir1lev1/dir1lev2/..",
+       "dir1lev1/dir2lev2/.",
+       "dir1lev1/dir2lev2/..",
+       "dir1lev1/dir2lev2/.foo",
+       "dir1lev1/dir2lev2/dir1lev3",
+       "dir1lev1/dir2lev2/file1lev3",
+       "dir1lev1/dir2lev2/file2lev3",
+       "dir1lev1/dir3lev2/.",
+       "dir1lev1/dir3lev2/..",
+       "dir1lev1/dir3lev2/file3lev3",
+       "dir1lev1/dir3lev2/file4lev3",
+       "dir2lev1/dir1lev2/.",
+       "dir2lev1/dir1lev2/..",
+       "dir2lev1/dir1lev2/.dir",
+       "dir2lev1/dir1lev2/.foo");
+
+  test ("*/*/.*", GLOB_ALTDIRFUNC,
+       "dir1lev1/dir1lev2/.",
+       "dir1lev1/dir1lev2/..",
+       "dir1lev1/dir2lev2/.",
+       "dir1lev1/dir2lev2/..",
+       "dir1lev1/dir2lev2/.foo",
+       "dir1lev1/dir3lev2/.",
+       "dir1lev1/dir3lev2/..",
+       "dir2lev1/dir1lev2/.",
+       "dir2lev1/dir1lev2/..",
+       "dir2lev1/dir1lev2/.dir",
+       "dir2lev1/dir1lev2/.foo");
+
+  test ("*1*/*2*/.*", GLOB_ALTDIRFUNC,
+       "dir1lev1/dir1lev2/.",
+       "dir1lev1/dir1lev2/..",
+       "dir1lev1/dir2lev2/.",
+       "dir1lev1/dir2lev2/..",
+       "dir1lev1/dir2lev2/.foo",
+       "dir1lev1/dir3lev2/.",
+       "dir1lev1/dir3lev2/..",
+       "dir2lev1/dir1lev2/.",
+       "dir2lev1/dir1lev2/..",
+       "dir2lev1/dir1lev2/.dir",
+       "dir2lev1/dir1lev2/.foo");
+
+  test ("*1*/*1*/.*", GLOB_ALTDIRFUNC,
+       "dir1lev1/dir1lev2/.",
+       "dir1lev1/dir1lev2/..",
+       "dir2lev1/dir1lev2/.",
+       "dir2lev1/dir1lev2/..",
+       "dir2lev1/dir1lev2/.dir",
+       "dir2lev1/dir1lev2/.foo");
+
+  globfree (&gl);
+
+  return result;
+}