Update to LGPL v2.1.
[jlayton/glibc.git] / posix / tst-gnuglob.c
1 /* Test the GNU extensions in glob which allow the user to provide callbacks
2    for the filesystem access functions.
3    Copyright (C) 2001 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5    Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
6
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    The GNU C Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library; if not, write to the Free
19    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20    02111-1307 USA.  */
21
22 #include <dirent.h>
23 #include <errno.h>
24 #include <error.h>
25 #include <glob.h>
26 #include <mcheck.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/stat.h>
31
32
33 /* #define DEBUG */
34 #ifdef DEBUG
35 # define PRINTF(fmt, args...) printf (fmt, ##args)
36 #else
37 # define PRINTF(fmt, args...)
38 #endif
39
40
41 static struct
42 {
43   const char *name;
44   int level;
45   int type;
46 } filesystem[] =
47 {
48   { ".", 1, DT_DIR },
49   { "..", 1, DT_DIR },
50   { "file1lev1", 1, DT_REG },
51   { "file2lev1", 1, DT_UNKNOWN },
52   { "dir1lev1", 1, DT_UNKNOWN },
53     { ".", 2, DT_DIR },
54     { "..", 2, DT_DIR },
55     { "file1lev2", 2, DT_REG },
56     { "dir1lev2", 2, DT_DIR },
57       { ".", 3, DT_DIR },
58       { "..", 3, DT_DIR },
59     { "dir2lev2", 2, DT_DIR },
60       { ".", 3, DT_DIR },
61       { "..", 3, DT_DIR },
62       { ".foo", 3, DT_REG },
63       { "dir1lev3", 3, DT_DIR },
64         { ".", 4, DT_DIR },
65         { "..", 4, DT_DIR },
66         { "file1lev4", 4, DT_REG },
67       { "file1lev3", 3, DT_REG },
68       { "file2lev3", 3, DT_REG },
69     { "file2lev2", 2, DT_REG },
70     { "file3lev2", 2, DT_REG },
71     { "dir3lev2", 2, DT_DIR },
72       { ".", 3, DT_DIR },
73       { "..", 3, DT_DIR },
74       { "file3lev3", 3, DT_REG },
75       { "file4lev3", 3, DT_REG },
76   { "dir2lev1", 1, DT_DIR },
77     { ".", 2, DT_DIR },
78     { "..", 2, DT_DIR },
79     { "dir1lev2", 2, DT_UNKNOWN },
80       { ".", 3, DT_DIR },
81       { "..", 3, DT_DIR },
82       { ".foo", 3, DT_REG },
83       { ".dir", 3, DT_DIR },
84         { ".", 4, DT_DIR },
85         { "..", 4, DT_DIR },
86         { "hidden", 4, DT_REG }
87 };
88 #define nfiles (sizeof (filesystem) / sizeof (filesystem[0]))
89
90
91 typedef struct
92 {
93   int level;
94   int idx;
95   struct dirent d;
96 } my_DIR;
97
98
99 static long int
100 find_file (const char *s)
101 {
102   int level = 1;
103   long int idx = 0;
104
105   if (strcmp (s, ".") == 0)
106     return 0;
107
108   while (*s != '\0')
109     {
110       char *endp = strchrnul (s, '/');
111
112       PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
113
114       while (idx < nfiles && filesystem[idx].level >= level)
115         {
116           if (filesystem[idx].level == level
117               && memcmp (s, filesystem[idx].name, endp - s) == 0
118               && filesystem[idx].name[endp - s] == '\0')
119             break;
120           ++idx;
121         }
122
123       if (idx == nfiles || filesystem[idx].level < level)
124         {
125           errno = ENOENT;
126           return -1;
127         }
128       if (filesystem[idx].type != DT_DIR
129           && (idx + 1 >= nfiles
130               || filesystem[idx].level >= filesystem[idx + 1].level))
131         {
132           errno = ENOTDIR;
133           return -1;
134         }
135
136       ++idx;
137
138       if (*endp == '\0')
139         return idx;
140
141       s = endp + 1;
142       ++level;
143     }
144
145   errno = ENOENT;
146   return -1;
147 }
148
149
150 static void *
151 my_opendir (const char *s)
152 {
153   long int idx = find_file (s);
154   my_DIR *dir;
155
156
157   if (idx == -1)
158     {
159       PRINTF ("my_opendir(\"%s\") == NULL\n", s);
160       return NULL;
161     }
162
163   dir = (my_DIR *) malloc (sizeof (my_DIR));
164   if (dir == NULL)
165     error (EXIT_FAILURE, errno, "cannot allocate directory handle");
166
167   dir->level = filesystem[idx].level;
168   dir->idx = idx;
169
170   PRINTF ("my_opendir(\"%s\") == { level: %d, idx: %ld }\n",
171           s, filesystem[idx].level, idx);
172
173   return dir;
174 }
175
176
177 static struct dirent *
178 my_readdir (void *gdir)
179 {
180   my_DIR *dir = gdir;
181
182   if (dir->idx == -1)
183     {
184       PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
185               dir->level, (long int) dir->idx);
186       return NULL;
187     }
188
189   while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)
190     ++dir->idx;
191
192   if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)
193     {
194       dir->idx = -1;
195       PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
196               dir->level, (long int) dir->idx);
197       return NULL;
198     }
199
200   dir->d.d_ino = dir->idx;
201
202 #ifdef _DIRENT_HAVE_D_TYPE
203   dir->d.d_type = filesystem[dir->idx].type;
204 #endif
205
206   strcpy (dir->d.d_name, filesystem[dir->idx].name);
207
208 #ifdef _DIRENT_HAVE_D_TYPE
209   PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_type: %d, d_name: \"%s\" }\n",
210           dir->level, (long int) dir->idx, dir->d.d_ino, dir->d.d_type,
211           dir->d.d_name);
212 #else
213   PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_name: \"%s\" }\n",
214           dir->level, (long int) dir->idx, dir->d.d_ino,
215           dir->d.d_name);
216 #endif
217
218   ++dir->idx;
219
220   return &dir->d;
221 }
222
223
224 static void
225 my_closedir (void *dir)
226 {
227   PRINTF ("my_closedir ()\n");
228   free (dir);
229 }
230
231
232 /* We use this function for lstat as well since we don't have any.  */
233 static int
234 my_stat (const char *name, struct stat *st)
235 {
236   long int idx = find_file (name);
237
238   if (idx == -1)
239     {
240       PRINTF ("my_stat (\"%s\", ...) = -1 (%s)\n", name, strerror (errno));
241       return -1;
242     }
243
244   memset (st, '\0', sizeof (*st));
245
246   if (filesystem[idx].type == DT_UNKNOWN)
247     st->st_mode = DTTOIF (idx + 1 < nfiles
248                           && filesystem[idx].level < filesystem[idx + 1].level
249                           ? DT_DIR : DT_REG) | 0777;
250   else
251     st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
252
253   PRINTF ("my_stat (\"%s\", { st_mode: %o }) = 0\n", name, st->st_mode);
254
255   return 0;
256 }
257
258
259 static const char *glob_errstring[] =
260 {
261   [GLOB_NOSPACE] = "out of memory",
262   [GLOB_ABORTED] = "read error",
263   [GLOB_NOMATCH] = "no matches found"
264 };
265 #define nglob_errstring (sizeof (glob_errstring) / sizeof (glob_errstring[0]))
266
267
268 static const char *
269 flagstr (int flags)
270 {
271   const char *strs[] =
272   {
273     "GLOB_ERR", "GLOB_MARK", "GLOB_NOSORT", "GLOB_DOOFSS", "GLOB_NOCHECK",
274     "GLOB_APPEND", "GLOB_NOESCAPE", "GLOB_PERIOD", "GLOB_MAGCHAR",
275     "GLOB_ALTDIRFUNC", "GLOB_BRACE", "GLOB_NOMAGIC", "GLOB_TILDE",
276     "GLOB_ONLYDIR", "GLOB_TILDECHECK"
277   };
278 #define nstrs (sizeof (strs) / sizeof (strs[0]))
279   static char buf[100];
280   char *cp = buf;
281   int cnt;
282
283   for (cnt = 0; cnt < nstrs; ++cnt)
284     if (flags & (1 << cnt))
285       {
286         flags &= ~(1 << cnt);
287         if (cp != buf)
288           *cp++ = '|';
289         cp = stpcpy (cp, strs[cnt]);
290       }
291
292   if (flags != 0)
293     {
294       if (cp != buf)
295         *cp++ = '|';
296       sprintf (cp, "%#x", flags);
297     }
298
299   return buf;
300 }
301
302
303 static int
304 test_result (const char *fmt, int flags, glob_t *gl, const char *str[])
305 {
306   size_t cnt;
307   int result = 0;
308
309   printf ("results for glob (\"%s\", %s)\n", fmt, flagstr (flags));
310   for (cnt = 0; cnt < gl->gl_pathc && str[cnt] != NULL; ++cnt)
311     {
312       int ok = strcmp (gl->gl_pathv[cnt], str[cnt]) == 0;
313       const char *errstr = "";
314
315       if (! ok)
316         {
317           size_t inner;
318
319           for (inner = 0; str[inner] != NULL; ++inner)
320             if (strcmp (gl->gl_pathv[cnt], str[inner]) == 0)
321               break;
322
323           if (str[inner] == NULL)
324             errstr =  ok ? "" : " *** WRONG";
325           else
326             errstr = ok ? "" : " * wrong position";
327
328           result = 1;
329         }
330
331       printf ("  %s%s\n", gl->gl_pathv[cnt], errstr);
332     }
333   puts ("");
334
335   if (str[cnt] != NULL || cnt < gl->gl_pathc)
336     {
337       puts ("  *** incorrect number of entries");
338       result = 1;
339     }
340
341   return result;
342 }
343
344
345 int
346 main (void)
347 {
348   glob_t gl;
349   int errval;
350   int result = 0;
351   const char *fmt;
352   int flags;
353
354   mtrace ();
355
356   memset (&gl, '\0', sizeof (gl));
357
358   gl.gl_closedir = my_closedir;
359   gl.gl_readdir = my_readdir;
360   gl.gl_opendir = my_opendir;
361   gl.gl_lstat = my_stat;
362   gl.gl_stat = my_stat;
363
364 #define test(a, b, c...) \
365   fmt = a;                                                                    \
366   flags = b;                                                                  \
367   errval = glob (fmt, flags, NULL, &gl);                                      \
368   if (errval != 0)                                                            \
369     {                                                                         \
370       printf ("glob (\"%s\", %s) failed: %s\n", fmt, flagstr (flags),         \
371               errval >= 0 && errval < nglob_errstring                         \
372               ? glob_errstring[errval] : "???");                              \
373       result = 1;                                                             \
374     }                                                                         \
375   else                                                                        \
376     result |= test_result (fmt, flags, &gl, (const char *[]) { c, NULL })
377
378   test ("*/*/*", GLOB_ALTDIRFUNC,
379         "dir1lev1/dir2lev2/dir1lev3",
380         "dir1lev1/dir2lev2/file1lev3",
381         "dir1lev1/dir2lev2/file2lev3",
382         "dir1lev1/dir3lev2/file3lev3",
383         "dir1lev1/dir3lev2/file4lev3");
384
385   test ("*/*/*", GLOB_ALTDIRFUNC | GLOB_PERIOD,
386         "dir1lev1/dir1lev2/.",
387         "dir1lev1/dir1lev2/..",
388         "dir1lev1/dir2lev2/.",
389         "dir1lev1/dir2lev2/..",
390         "dir1lev1/dir2lev2/.foo",
391         "dir1lev1/dir2lev2/dir1lev3",
392         "dir1lev1/dir2lev2/file1lev3",
393         "dir1lev1/dir2lev2/file2lev3",
394         "dir1lev1/dir3lev2/.",
395         "dir1lev1/dir3lev2/..",
396         "dir1lev1/dir3lev2/file3lev3",
397         "dir1lev1/dir3lev2/file4lev3",
398         "dir2lev1/dir1lev2/.",
399         "dir2lev1/dir1lev2/..",
400         "dir2lev1/dir1lev2/.dir",
401         "dir2lev1/dir1lev2/.foo");
402
403   test ("*/*/.*", GLOB_ALTDIRFUNC,
404         "dir1lev1/dir1lev2/.",
405         "dir1lev1/dir1lev2/..",
406         "dir1lev1/dir2lev2/.",
407         "dir1lev1/dir2lev2/..",
408         "dir1lev1/dir2lev2/.foo",
409         "dir1lev1/dir3lev2/.",
410         "dir1lev1/dir3lev2/..",
411         "dir2lev1/dir1lev2/.",
412         "dir2lev1/dir1lev2/..",
413         "dir2lev1/dir1lev2/.dir",
414         "dir2lev1/dir1lev2/.foo");
415
416   test ("*1*/*2*/.*", GLOB_ALTDIRFUNC,
417         "dir1lev1/dir1lev2/.",
418         "dir1lev1/dir1lev2/..",
419         "dir1lev1/dir2lev2/.",
420         "dir1lev1/dir2lev2/..",
421         "dir1lev1/dir2lev2/.foo",
422         "dir1lev1/dir3lev2/.",
423         "dir1lev1/dir3lev2/..",
424         "dir2lev1/dir1lev2/.",
425         "dir2lev1/dir1lev2/..",
426         "dir2lev1/dir1lev2/.dir",
427         "dir2lev1/dir1lev2/.foo");
428
429   test ("*1*/*1*/.*", GLOB_ALTDIRFUNC,
430         "dir1lev1/dir1lev2/.",
431         "dir1lev1/dir1lev2/..",
432         "dir2lev1/dir1lev2/.",
433         "dir2lev1/dir1lev2/..",
434         "dir2lev1/dir1lev2/.dir",
435         "dir2lev1/dir1lev2/.foo");
436
437   globfree (&gl);
438
439   return result;
440 }