IA64: Move NPTL public headers to sysdeps/ia64/nptl/.
[platform/upstream/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-2014 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, see
19    <http://www.gnu.org/licenses/>.  */
20
21 #include <dirent.h>
22 #include <errno.h>
23 #include <error.h>
24 #include <glob.h>
25 #include <mcheck.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30
31
32 // #define DEBUG
33 #ifdef DEBUG
34 # define PRINTF(fmt, args...) printf (fmt, ##args)
35 #else
36 # define PRINTF(fmt, args...)
37 #endif
38
39
40 static struct
41 {
42   const char *name;
43   int level;
44   int type;
45 } filesystem[] =
46 {
47   { ".", 1, DT_DIR },
48   { "..", 1, DT_DIR },
49   { "file1lev1", 1, DT_REG },
50   { "file2lev1", 1, DT_UNKNOWN },
51   { "dir1lev1", 1, DT_UNKNOWN },
52     { ".", 2, DT_DIR },
53     { "..", 2, DT_DIR },
54     { "file1lev2", 2, DT_REG },
55     { "dir1lev2", 2, DT_DIR },
56       { ".", 3, DT_DIR },
57       { "..", 3, DT_DIR },
58     { "dir2lev2", 2, DT_DIR },
59       { ".", 3, DT_DIR },
60       { "..", 3, DT_DIR },
61       { ".foo", 3, DT_REG },
62       { "dir1lev3", 3, DT_DIR },
63         { ".", 4, DT_DIR },
64         { "..", 4, DT_DIR },
65         { "file1lev4", 4, DT_REG },
66       { "file1lev3", 3, DT_REG },
67       { "file2lev3", 3, DT_REG },
68     { "file2lev2", 2, DT_REG },
69     { "file3lev2", 2, DT_REG },
70     { "dir3lev2", 2, DT_DIR },
71       { ".", 3, DT_DIR },
72       { "..", 3, DT_DIR },
73       { "file3lev3", 3, DT_REG },
74       { "file4lev3", 3, DT_REG },
75   { "dir2lev1", 1, DT_DIR },
76     { ".", 2, DT_DIR },
77     { "..", 2, DT_DIR },
78     { "dir1lev2", 2, DT_UNKNOWN },
79       { ".", 3, DT_DIR },
80       { "..", 3, DT_DIR },
81       { ".foo", 3, DT_REG },
82       { ".dir", 3, DT_DIR },
83         { ".", 4, DT_DIR },
84         { "..", 4, DT_DIR },
85         { "hidden", 4, DT_REG }
86 };
87 #define nfiles (sizeof (filesystem) / sizeof (filesystem[0]))
88
89
90 typedef struct
91 {
92   int level;
93   int idx;
94   struct dirent d;
95   char room_for_dirent[NAME_MAX];
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   while (s[0] == '/')
106     {
107       if (s[1] == '\0')
108         {
109           s = ".";
110           break;
111         }
112       ++s;
113     }
114
115   if (strcmp (s, ".") == 0)
116     return 0;
117
118   if (s[0] == '.' && s[1] == '/')
119     s += 2;
120
121   while (*s != '\0')
122     {
123       char *endp = strchrnul (s, '/');
124
125       PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
126
127       while (idx < nfiles && filesystem[idx].level >= level)
128         {
129           if (filesystem[idx].level == level
130               && memcmp (s, filesystem[idx].name, endp - s) == 0
131               && filesystem[idx].name[endp - s] == '\0')
132             break;
133           ++idx;
134         }
135
136       if (idx == nfiles || filesystem[idx].level < level)
137         {
138           errno = ENOENT;
139           return -1;
140         }
141
142       if (*endp == '\0')
143         return idx + 1;
144
145       if (filesystem[idx].type != DT_DIR
146           && (idx + 1 >= nfiles
147               || filesystem[idx].level >= filesystem[idx + 1].level))
148         {
149           errno = ENOTDIR;
150           return -1;
151         }
152
153       ++idx;
154
155       s = endp + 1;
156       ++level;
157     }
158
159   errno = ENOENT;
160   return -1;
161 }
162
163
164 static void *
165 my_opendir (const char *s)
166 {
167   long int idx = find_file (s);
168   my_DIR *dir;
169
170
171   if (idx == -1 || filesystem[idx].type != DT_DIR)
172     {
173       PRINTF ("my_opendir(\"%s\") == NULL\n", s);
174       return NULL;
175     }
176
177   dir = (my_DIR *) malloc (sizeof (my_DIR));
178   if (dir == NULL)
179     error (EXIT_FAILURE, errno, "cannot allocate directory handle");
180
181   dir->level = filesystem[idx].level;
182   dir->idx = idx;
183
184   PRINTF ("my_opendir(\"%s\") == { level: %d, idx: %ld }\n",
185           s, filesystem[idx].level, idx);
186
187   return dir;
188 }
189
190
191 static struct dirent *
192 my_readdir (void *gdir)
193 {
194   my_DIR *dir = gdir;
195
196   if (dir->idx == -1)
197     {
198       PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
199               dir->level, (long int) dir->idx);
200       return NULL;
201     }
202
203   while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)
204     ++dir->idx;
205
206   if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)
207     {
208       dir->idx = -1;
209       PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
210               dir->level, (long int) dir->idx);
211       return NULL;
212     }
213
214   dir->d.d_ino = dir->idx;
215
216 #ifdef _DIRENT_HAVE_D_TYPE
217   dir->d.d_type = filesystem[dir->idx].type;
218 #endif
219
220   strcpy (dir->d.d_name, filesystem[dir->idx].name);
221
222 #ifdef _DIRENT_HAVE_D_TYPE
223   PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_type: %d, d_name: \"%s\" }\n",
224           dir->level, (long int) dir->idx, dir->d.d_ino, dir->d.d_type,
225           dir->d.d_name);
226 #else
227   PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_name: \"%s\" }\n",
228           dir->level, (long int) dir->idx, dir->d.d_ino,
229           dir->d.d_name);
230 #endif
231
232   ++dir->idx;
233
234   return &dir->d;
235 }
236
237
238 static void
239 my_closedir (void *dir)
240 {
241   PRINTF ("my_closedir ()\n");
242   free (dir);
243 }
244
245
246 /* We use this function for lstat as well since we don't have any.  */
247 static int
248 my_stat (const char *name, struct stat *st)
249 {
250   long int idx = find_file (name);
251
252   if (idx == -1)
253     {
254       PRINTF ("my_stat (\"%s\", ...) = -1 (%s)\n", name, strerror (errno));
255       return -1;
256     }
257
258   memset (st, '\0', sizeof (*st));
259
260   if (filesystem[idx].type == DT_UNKNOWN)
261     st->st_mode = DTTOIF (idx + 1 < nfiles
262                           && filesystem[idx].level < filesystem[idx + 1].level
263                           ? DT_DIR : DT_REG) | 0777;
264   else
265     st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
266
267   PRINTF ("my_stat (\"%s\", { st_mode: %o }) = 0\n", name, st->st_mode);
268
269   return 0;
270 }
271
272
273 static const char *glob_errstring[] =
274 {
275   [GLOB_NOSPACE] = "out of memory",
276   [GLOB_ABORTED] = "read error",
277   [GLOB_NOMATCH] = "no matches found"
278 };
279 #define nglob_errstring (sizeof (glob_errstring) / sizeof (glob_errstring[0]))
280
281
282 static const char *
283 flagstr (int flags)
284 {
285   static const char *const strs[] =
286   {
287     "GLOB_ERR", "GLOB_MARK", "GLOB_NOSORT", "GLOB_DOOFSS", "GLOB_NOCHECK",
288     "GLOB_APPEND", "GLOB_NOESCAPE", "GLOB_PERIOD", "GLOB_MAGCHAR",
289     "GLOB_ALTDIRFUNC", "GLOB_BRACE", "GLOB_NOMAGIC", "GLOB_TILDE",
290     "GLOB_ONLYDIR", "GLOB_TILDECHECK"
291   };
292 #define nstrs (sizeof (strs) / sizeof (strs[0]))
293   static char buf[100];
294   char *cp = buf;
295   int cnt;
296
297   for (cnt = 0; cnt < nstrs; ++cnt)
298     if (flags & (1 << cnt))
299       {
300         flags &= ~(1 << cnt);
301         if (cp != buf)
302           *cp++ = '|';
303         cp = stpcpy (cp, strs[cnt]);
304       }
305
306   if (flags != 0)
307     {
308       if (cp != buf)
309         *cp++ = '|';
310       sprintf (cp, "%#x", flags);
311     }
312
313   return buf;
314 #undef nstrs
315 }
316
317
318 static const char *
319 errstr (int val)
320 {
321   static const char *const strs[] =
322     {
323       [GLOB_NOSPACE] = "GLOB_NOSPACE",
324       [GLOB_ABORTED] = "GLOB_ABORTED",
325       [GLOB_NOMATCH] = "GLOB_NOMATCH",
326       [GLOB_NOSYS] = "GLOB_NOSYS"
327     };
328 #define nstrs (sizeof (strs) / sizeof (strs[0]))
329   static char buf[100];
330   if (val < 0 || val >= nstrs || strs[val] == NULL)
331     {
332       snprintf (buf, sizeof (buf), "GLOB_??? (%d)", val);
333       return buf;
334     }
335   return strs[val];
336 #undef nstrs
337 }
338
339
340 static int
341 test_result (const char *fmt, int flags, glob_t *gl, const char *str[])
342 {
343   size_t cnt;
344   int result = 0;
345
346   printf ("results for glob (\"%s\", %s)\n", fmt, flagstr (flags));
347   for (cnt = 0; cnt < gl->gl_pathc && str[cnt] != NULL; ++cnt)
348     {
349       int ok = strcmp (gl->gl_pathv[cnt], str[cnt]) == 0;
350       const char *errstr = "";
351
352       if (! ok)
353         {
354           size_t inner;
355
356           for (inner = 0; str[inner] != NULL; ++inner)
357             if (strcmp (gl->gl_pathv[cnt], str[inner]) == 0)
358               break;
359
360           if (str[inner] == NULL)
361             errstr = ok ? "" : " *** WRONG";
362           else
363             errstr = ok ? "" : " * wrong position";
364
365           result = 1;
366         }
367
368       printf ("  %s%s\n", gl->gl_pathv[cnt], errstr);
369     }
370   puts ("");
371
372   if (str[cnt] != NULL || cnt < gl->gl_pathc)
373     {
374       puts ("  *** incorrect number of entries");
375       result = 1;
376     }
377
378   return result;
379 }
380
381
382 int
383 main (void)
384 {
385   glob_t gl;
386   int errval;
387   int result = 0;
388   const char *fmt;
389   int flags;
390
391   mtrace ();
392
393   memset (&gl, '\0', sizeof (gl));
394
395   gl.gl_closedir = my_closedir;
396   gl.gl_readdir = my_readdir;
397   gl.gl_opendir = my_opendir;
398   gl.gl_lstat = my_stat;
399   gl.gl_stat = my_stat;
400
401 #define test(a, b, r, c...) \
402   fmt = a;                                                                    \
403   flags = GLOB_ALTDIRFUNC | b;                                                \
404   errval = glob (fmt, flags, NULL, &gl);                                      \
405   if (errval != r)                                                            \
406     {                                                                         \
407       if (r == 0)                                                             \
408         printf ("glob (\"%s\", %s) failed: %s\n", fmt, flagstr (flags),       \
409                 errval >= 0 && errval < nglob_errstring                       \
410                 ? glob_errstring[errval] : "???");                            \
411       else                                                                    \
412         printf ("glob (\"%s\", %s) did not fail\n", fmt, flagstr (flags));    \
413       result = 1;                                                             \
414     }                                                                         \
415   else if (r == 0)                                                            \
416     result |= test_result (fmt, flags, &gl, (const char *[]) { c, NULL });    \
417   else                                                                        \
418     printf ("result for glob (\"%s\", %s) = %s\n\n", fmt, flagstr (flags),    \
419             errstr (errval))
420
421   test ("*/*/*", 0, 0,
422         "dir1lev1/dir2lev2/dir1lev3",
423         "dir1lev1/dir2lev2/file1lev3",
424         "dir1lev1/dir2lev2/file2lev3",
425         "dir1lev1/dir3lev2/file3lev3",
426         "dir1lev1/dir3lev2/file4lev3");
427
428   test ("*/*/*", GLOB_PERIOD, 0,
429         "dir1lev1/dir1lev2/.",
430         "dir1lev1/dir1lev2/..",
431         "dir1lev1/dir2lev2/.",
432         "dir1lev1/dir2lev2/..",
433         "dir1lev1/dir2lev2/.foo",
434         "dir1lev1/dir2lev2/dir1lev3",
435         "dir1lev1/dir2lev2/file1lev3",
436         "dir1lev1/dir2lev2/file2lev3",
437         "dir1lev1/dir3lev2/.",
438         "dir1lev1/dir3lev2/..",
439         "dir1lev1/dir3lev2/file3lev3",
440         "dir1lev1/dir3lev2/file4lev3",
441         "dir2lev1/dir1lev2/.",
442         "dir2lev1/dir1lev2/..",
443         "dir2lev1/dir1lev2/.dir",
444         "dir2lev1/dir1lev2/.foo");
445
446   test ("*/*/.*", 0, 0,
447         "dir1lev1/dir1lev2/.",
448         "dir1lev1/dir1lev2/..",
449         "dir1lev1/dir2lev2/.",
450         "dir1lev1/dir2lev2/..",
451         "dir1lev1/dir2lev2/.foo",
452         "dir1lev1/dir3lev2/.",
453         "dir1lev1/dir3lev2/..",
454         "dir2lev1/dir1lev2/.",
455         "dir2lev1/dir1lev2/..",
456         "dir2lev1/dir1lev2/.dir",
457         "dir2lev1/dir1lev2/.foo");
458
459   test ("*1*/*2*/.*", 0, 0,
460         "dir1lev1/dir1lev2/.",
461         "dir1lev1/dir1lev2/..",
462         "dir1lev1/dir2lev2/.",
463         "dir1lev1/dir2lev2/..",
464         "dir1lev1/dir2lev2/.foo",
465         "dir1lev1/dir3lev2/.",
466         "dir1lev1/dir3lev2/..",
467         "dir2lev1/dir1lev2/.",
468         "dir2lev1/dir1lev2/..",
469         "dir2lev1/dir1lev2/.dir",
470         "dir2lev1/dir1lev2/.foo");
471
472   test ("*1*/*1*/.*", 0, 0,
473         "dir1lev1/dir1lev2/.",
474         "dir1lev1/dir1lev2/..",
475         "dir2lev1/dir1lev2/.",
476         "dir2lev1/dir1lev2/..",
477         "dir2lev1/dir1lev2/.dir",
478         "dir2lev1/dir1lev2/.foo");
479
480   test ("\\/*", 0, 0,
481         "/dir1lev1",
482         "/dir2lev1",
483         "/file1lev1",
484         "/file2lev1");
485
486   test ("*/*/", 0 , 0,
487         "dir1lev1/dir1lev2/",
488         "dir1lev1/dir2lev2/",
489         "dir1lev1/dir3lev2/",
490         "dir2lev1/dir1lev2/");
491
492   test ("", 0, GLOB_NOMATCH, NULL);
493
494   test ("", GLOB_NOCHECK, 0, "");
495
496   globfree (&gl);
497
498   return result;
499 }