Back to index

glibc  2.9
tst-gnuglob.c
Go to the documentation of this file.
00001 /* Test the GNU extensions in glob which allow the user to provide callbacks
00002    for the filesystem access functions.
00003    Copyright (C) 2001-2002, 2007 Free Software Foundation, Inc.
00004    This file is part of the GNU C Library.
00005    Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
00006 
00007    The GNU C Library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser General Public
00009    License as published by the Free Software Foundation; either
00010    version 2.1 of the License, or (at your option) any later version.
00011 
00012    The GNU C Library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Lesser General Public License for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public
00018    License along with the GNU C Library; if not, write to the Free
00019    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00020    02111-1307 USA.  */
00021 
00022 #include <dirent.h>
00023 #include <errno.h>
00024 #include <error.h>
00025 #include <glob.h>
00026 #include <mcheck.h>
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 #include <sys/stat.h>
00031 
00032 
00033 // #define DEBUG
00034 #ifdef DEBUG
00035 # define PRINTF(fmt, args...) printf (fmt, ##args)
00036 #else
00037 # define PRINTF(fmt, args...)
00038 #endif
00039 
00040 
00041 static struct
00042 {
00043   const char *name;
00044   int level;
00045   int type;
00046 } filesystem[] =
00047 {
00048   { ".", 1, DT_DIR },
00049   { "..", 1, DT_DIR },
00050   { "file1lev1", 1, DT_REG },
00051   { "file2lev1", 1, DT_UNKNOWN },
00052   { "dir1lev1", 1, DT_UNKNOWN },
00053     { ".", 2, DT_DIR },
00054     { "..", 2, DT_DIR },
00055     { "file1lev2", 2, DT_REG },
00056     { "dir1lev2", 2, DT_DIR },
00057       { ".", 3, DT_DIR },
00058       { "..", 3, DT_DIR },
00059     { "dir2lev2", 2, DT_DIR },
00060       { ".", 3, DT_DIR },
00061       { "..", 3, DT_DIR },
00062       { ".foo", 3, DT_REG },
00063       { "dir1lev3", 3, DT_DIR },
00064         { ".", 4, DT_DIR },
00065         { "..", 4, DT_DIR },
00066         { "file1lev4", 4, DT_REG },
00067       { "file1lev3", 3, DT_REG },
00068       { "file2lev3", 3, DT_REG },
00069     { "file2lev2", 2, DT_REG },
00070     { "file3lev2", 2, DT_REG },
00071     { "dir3lev2", 2, DT_DIR },
00072       { ".", 3, DT_DIR },
00073       { "..", 3, DT_DIR },
00074       { "file3lev3", 3, DT_REG },
00075       { "file4lev3", 3, DT_REG },
00076   { "dir2lev1", 1, DT_DIR },
00077     { ".", 2, DT_DIR },
00078     { "..", 2, DT_DIR },
00079     { "dir1lev2", 2, DT_UNKNOWN },
00080       { ".", 3, DT_DIR },
00081       { "..", 3, DT_DIR },
00082       { ".foo", 3, DT_REG },
00083       { ".dir", 3, DT_DIR },
00084         { ".", 4, DT_DIR },
00085         { "..", 4, DT_DIR },
00086         { "hidden", 4, DT_REG }
00087 };
00088 #define nfiles (sizeof (filesystem) / sizeof (filesystem[0]))
00089 
00090 
00091 typedef struct
00092 {
00093   int level;
00094   int idx;
00095   struct dirent d;
00096   char room_for_dirent[NAME_MAX];
00097 } my_DIR;
00098 
00099 
00100 static long int
00101 find_file (const char *s)
00102 {
00103   int level = 1;
00104   long int idx = 0;
00105 
00106   while (s[0] == '/')
00107     {
00108       if (s[1] == '\0')
00109        {
00110          s = ".";
00111          break;
00112        }
00113       ++s;
00114     }
00115 
00116   if (strcmp (s, ".") == 0)
00117     return 0;
00118 
00119   if (s[0] == '.' && s[1] == '/')
00120     s += 2;
00121 
00122   while (*s != '\0')
00123     {
00124       char *endp = strchrnul (s, '/');
00125 
00126       PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
00127 
00128       while (idx < nfiles && filesystem[idx].level >= level)
00129        {
00130          if (filesystem[idx].level == level
00131              && memcmp (s, filesystem[idx].name, endp - s) == 0
00132              && filesystem[idx].name[endp - s] == '\0')
00133            break;
00134          ++idx;
00135        }
00136 
00137       if (idx == nfiles || filesystem[idx].level < level)
00138        {
00139          errno = ENOENT;
00140          return -1;
00141        }
00142 
00143       if (*endp == '\0')
00144        return idx + 1;
00145 
00146       if (filesystem[idx].type != DT_DIR
00147          && (idx + 1 >= nfiles
00148              || filesystem[idx].level >= filesystem[idx + 1].level))
00149        {
00150          errno = ENOTDIR;
00151          return -1;
00152        }
00153 
00154       ++idx;
00155 
00156       s = endp + 1;
00157       ++level;
00158     }
00159 
00160   errno = ENOENT;
00161   return -1;
00162 }
00163 
00164 
00165 static void *
00166 my_opendir (const char *s)
00167 {
00168   long int idx = find_file (s);
00169   my_DIR *dir;
00170 
00171 
00172   if (idx == -1)
00173     {
00174       PRINTF ("my_opendir(\"%s\") == NULL\n", s);
00175       return NULL;
00176     }
00177 
00178   dir = (my_DIR *) malloc (sizeof (my_DIR));
00179   if (dir == NULL)
00180     error (EXIT_FAILURE, errno, "cannot allocate directory handle");
00181 
00182   dir->level = filesystem[idx].level;
00183   dir->idx = idx;
00184 
00185   PRINTF ("my_opendir(\"%s\") == { level: %d, idx: %ld }\n",
00186          s, filesystem[idx].level, idx);
00187 
00188   return dir;
00189 }
00190 
00191 
00192 static struct dirent *
00193 my_readdir (void *gdir)
00194 {
00195   my_DIR *dir = gdir;
00196 
00197   if (dir->idx == -1)
00198     {
00199       PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
00200              dir->level, (long int) dir->idx);
00201       return NULL;
00202     }
00203 
00204   while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)
00205     ++dir->idx;
00206 
00207   if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)
00208     {
00209       dir->idx = -1;
00210       PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
00211              dir->level, (long int) dir->idx);
00212       return NULL;
00213     }
00214 
00215   dir->d.d_ino = dir->idx;
00216 
00217 #ifdef _DIRENT_HAVE_D_TYPE
00218   dir->d.d_type = filesystem[dir->idx].type;
00219 #endif
00220 
00221   strcpy (dir->d.d_name, filesystem[dir->idx].name);
00222 
00223 #ifdef _DIRENT_HAVE_D_TYPE
00224   PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_type: %d, d_name: \"%s\" }\n",
00225          dir->level, (long int) dir->idx, dir->d.d_ino, dir->d.d_type,
00226          dir->d.d_name);
00227 #else
00228   PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_name: \"%s\" }\n",
00229          dir->level, (long int) dir->idx, dir->d.d_ino,
00230          dir->d.d_name);
00231 #endif
00232 
00233   ++dir->idx;
00234 
00235   return &dir->d;
00236 }
00237 
00238 
00239 static void
00240 my_closedir (void *dir)
00241 {
00242   PRINTF ("my_closedir ()\n");
00243   free (dir);
00244 }
00245 
00246 
00247 /* We use this function for lstat as well since we don't have any.  */
00248 static int
00249 my_stat (const char *name, struct stat *st)
00250 {
00251   long int idx = find_file (name);
00252 
00253   if (idx == -1)
00254     {
00255       PRINTF ("my_stat (\"%s\", ...) = -1 (%s)\n", name, strerror (errno));
00256       return -1;
00257     }
00258 
00259   memset (st, '\0', sizeof (*st));
00260 
00261   if (filesystem[idx].type == DT_UNKNOWN)
00262     st->st_mode = DTTOIF (idx + 1 < nfiles
00263                        && filesystem[idx].level < filesystem[idx + 1].level
00264                        ? DT_DIR : DT_REG) | 0777;
00265   else
00266     st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
00267 
00268   PRINTF ("my_stat (\"%s\", { st_mode: %o }) = 0\n", name, st->st_mode);
00269 
00270   return 0;
00271 }
00272 
00273 
00274 static const char *glob_errstring[] =
00275 {
00276   [GLOB_NOSPACE] = "out of memory",
00277   [GLOB_ABORTED] = "read error",
00278   [GLOB_NOMATCH] = "no matches found"
00279 };
00280 #define nglob_errstring (sizeof (glob_errstring) / sizeof (glob_errstring[0]))
00281 
00282 
00283 static const char *
00284 flagstr (int flags)
00285 {
00286   const char *strs[] =
00287   {
00288     "GLOB_ERR", "GLOB_MARK", "GLOB_NOSORT", "GLOB_DOOFSS", "GLOB_NOCHECK",
00289     "GLOB_APPEND", "GLOB_NOESCAPE", "GLOB_PERIOD", "GLOB_MAGCHAR",
00290     "GLOB_ALTDIRFUNC", "GLOB_BRACE", "GLOB_NOMAGIC", "GLOB_TILDE",
00291     "GLOB_ONLYDIR", "GLOB_TILDECHECK"
00292   };
00293 #define nstrs (sizeof (strs) / sizeof (strs[0]))
00294   static char buf[100];
00295   char *cp = buf;
00296   int cnt;
00297 
00298   for (cnt = 0; cnt < nstrs; ++cnt)
00299     if (flags & (1 << cnt))
00300       {
00301        flags &= ~(1 << cnt);
00302        if (cp != buf)
00303          *cp++ = '|';
00304        cp = stpcpy (cp, strs[cnt]);
00305       }
00306 
00307   if (flags != 0)
00308     {
00309       if (cp != buf)
00310        *cp++ = '|';
00311       sprintf (cp, "%#x", flags);
00312     }
00313 
00314   return buf;
00315 }
00316 
00317 
00318 static int
00319 test_result (const char *fmt, int flags, glob_t *gl, const char *str[])
00320 {
00321   size_t cnt;
00322   int result = 0;
00323 
00324   printf ("results for glob (\"%s\", %s)\n", fmt, flagstr (flags));
00325   for (cnt = 0; cnt < gl->gl_pathc && str[cnt] != NULL; ++cnt)
00326     {
00327       int ok = strcmp (gl->gl_pathv[cnt], str[cnt]) == 0;
00328       const char *errstr = "";
00329 
00330       if (! ok)
00331        {
00332          size_t inner;
00333 
00334          for (inner = 0; str[inner] != NULL; ++inner)
00335            if (strcmp (gl->gl_pathv[cnt], str[inner]) == 0)
00336              break;
00337 
00338          if (str[inner] == NULL)
00339            errstr =  ok ? "" : " *** WRONG";
00340          else
00341            errstr = ok ? "" : " * wrong position";
00342 
00343          result = 1;
00344        }
00345 
00346       printf ("  %s%s\n", gl->gl_pathv[cnt], errstr);
00347     }
00348   puts ("");
00349 
00350   if (str[cnt] != NULL || cnt < gl->gl_pathc)
00351     {
00352       puts ("  *** incorrect number of entries");
00353       result = 1;
00354     }
00355 
00356   return result;
00357 }
00358 
00359 
00360 int
00361 main (void)
00362 {
00363   glob_t gl;
00364   int errval;
00365   int result = 0;
00366   const char *fmt;
00367   int flags;
00368 
00369   mtrace ();
00370 
00371   memset (&gl, '\0', sizeof (gl));
00372 
00373   gl.gl_closedir = my_closedir;
00374   gl.gl_readdir = my_readdir;
00375   gl.gl_opendir = my_opendir;
00376   gl.gl_lstat = my_stat;
00377   gl.gl_stat = my_stat;
00378 
00379 #define test(a, b, c...) \
00380   fmt = a;                                                           \
00381   flags = b;                                                         \
00382   errval = glob (fmt, flags, NULL, &gl);                             \
00383   if (errval != 0)                                                   \
00384     {                                                                \
00385       printf ("glob (\"%s\", %s) failed: %s\n", fmt, flagstr (flags),       \
00386              errval >= 0 && errval < nglob_errstring                        \
00387              ? glob_errstring[errval] : "???");                      \
00388       result = 1;                                                    \
00389     }                                                                \
00390   else                                                               \
00391     result |= test_result (fmt, flags, &gl, (const char *[]) { c, NULL })
00392 
00393   test ("*/*/*", GLOB_ALTDIRFUNC,
00394        "dir1lev1/dir2lev2/dir1lev3",
00395        "dir1lev1/dir2lev2/file1lev3",
00396        "dir1lev1/dir2lev2/file2lev3",
00397        "dir1lev1/dir3lev2/file3lev3",
00398        "dir1lev1/dir3lev2/file4lev3");
00399 
00400   test ("*/*/*", GLOB_ALTDIRFUNC | GLOB_PERIOD,
00401        "dir1lev1/dir1lev2/.",
00402        "dir1lev1/dir1lev2/..",
00403        "dir1lev1/dir2lev2/.",
00404        "dir1lev1/dir2lev2/..",
00405        "dir1lev1/dir2lev2/.foo",
00406        "dir1lev1/dir2lev2/dir1lev3",
00407        "dir1lev1/dir2lev2/file1lev3",
00408        "dir1lev1/dir2lev2/file2lev3",
00409        "dir1lev1/dir3lev2/.",
00410        "dir1lev1/dir3lev2/..",
00411        "dir1lev1/dir3lev2/file3lev3",
00412        "dir1lev1/dir3lev2/file4lev3",
00413        "dir2lev1/dir1lev2/.",
00414        "dir2lev1/dir1lev2/..",
00415        "dir2lev1/dir1lev2/.dir",
00416        "dir2lev1/dir1lev2/.foo");
00417 
00418   test ("*/*/.*", GLOB_ALTDIRFUNC,
00419        "dir1lev1/dir1lev2/.",
00420        "dir1lev1/dir1lev2/..",
00421        "dir1lev1/dir2lev2/.",
00422        "dir1lev1/dir2lev2/..",
00423        "dir1lev1/dir2lev2/.foo",
00424        "dir1lev1/dir3lev2/.",
00425        "dir1lev1/dir3lev2/..",
00426        "dir2lev1/dir1lev2/.",
00427        "dir2lev1/dir1lev2/..",
00428        "dir2lev1/dir1lev2/.dir",
00429        "dir2lev1/dir1lev2/.foo");
00430 
00431   test ("*1*/*2*/.*", GLOB_ALTDIRFUNC,
00432        "dir1lev1/dir1lev2/.",
00433        "dir1lev1/dir1lev2/..",
00434        "dir1lev1/dir2lev2/.",
00435        "dir1lev1/dir2lev2/..",
00436        "dir1lev1/dir2lev2/.foo",
00437        "dir1lev1/dir3lev2/.",
00438        "dir1lev1/dir3lev2/..",
00439        "dir2lev1/dir1lev2/.",
00440        "dir2lev1/dir1lev2/..",
00441        "dir2lev1/dir1lev2/.dir",
00442        "dir2lev1/dir1lev2/.foo");
00443 
00444   test ("*1*/*1*/.*", GLOB_ALTDIRFUNC,
00445        "dir1lev1/dir1lev2/.",
00446        "dir1lev1/dir1lev2/..",
00447        "dir2lev1/dir1lev2/.",
00448        "dir2lev1/dir1lev2/..",
00449        "dir2lev1/dir1lev2/.dir",
00450        "dir2lev1/dir1lev2/.foo");
00451 
00452   test ("\\/*", GLOB_ALTDIRFUNC,
00453        "/dir1lev1",
00454        "/dir2lev1",
00455        "/file1lev1",
00456        "/file2lev1");
00457 
00458   globfree (&gl);
00459 
00460   return result;
00461 }