[BZ #5103]
[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-2002, 2007 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   char room_for_dirent[NAME_MAX];
97 } my_DIR;
98
99
100 static long int
101 find_file (const char *s)
102 {
103   int level = 1;
104   long int idx = 0;
105
106   while (s[0] == '/')
107     {
108       if (s[1] == '\0')
109         {
110           s = ".";
111           break;
112         }
113       ++s;
114     }
115
116   if (strcmp (s, ".") == 0)
117     return 0;
118
119   if (s[0] == '.' && s[1] == '/')
120     s += 2;
121
122   while (*s != '\0')
123     {
124       char *endp = strchrnul (s, '/');
125
126       PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
127
128       while (idx < nfiles && filesystem[idx].level >= level)
129         {
130           if (filesystem[idx].level == level
131               && memcmp (s, filesystem[idx].name, endp - s) == 0
132               && filesystem[idx].name[endp - s] == '\0')
133             break;
134           ++idx;
135         }
136
137       if (idx == nfiles || filesystem[idx].level < level)
138         {
139           errno = ENOENT;
140           return -1;
141         }
142
143       if (*endp == '\0')
144         return idx + 1;
145
146       if (filesystem[idx].type != DT_DIR
147           && (idx + 1 >= nfiles
148               || filesystem[idx].level >= filesystem[idx + 1].level))
149         {
150           errno = ENOTDIR;
151           return -1;
152         }
153
154       ++idx;
155
156       s = endp + 1;
157       ++level;
158     }
159
160   errno = ENOENT;
161   return -1;
162 }
163
164
165 static void *
166 my_opendir (const char *s)
167 {
168   long int idx = find_file (s);
169   my_DIR *dir;
170
171
172   if (idx == -1)
173     {
174       PRINTF ("my_opendir(\"%s\") == NULL\n", s);
175       return NULL;
176     }
177
178   dir = (my_DIR *) malloc (sizeof (my_DIR));
179   if (dir == NULL)
180     error (EXIT_FAILURE, errno, "cannot allocate directory handle");
181
182   dir->level = filesystem[idx].level;
183   dir->idx = idx;
184
185   PRINTF ("my_opendir(\"%s\") == { level: %d, idx: %ld }\n",
186           s, filesystem[idx].level, idx);
187
188   return dir;
189 }
190
191
192 static struct dirent *
193 my_readdir (void *gdir)
194 {
195   my_DIR *dir = gdir;
196
197   if (dir->idx == -1)
198     {
199       PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
200               dir->level, (long int) dir->idx);
201       return NULL;
202     }
203
204   while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)
205     ++dir->idx;
206
207   if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)
208     {
209       dir->idx = -1;
210       PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
211               dir->level, (long int) dir->idx);
212       return NULL;
213     }
214
215   dir->d.d_ino = dir->idx;
216
217 #ifdef _DIRENT_HAVE_D_TYPE
218   dir->d.d_type = filesystem[dir->idx].type;
219 #endif
220
221   strcpy (dir->d.d_name, filesystem[dir->idx].name);
222
223 #ifdef _DIRENT_HAVE_D_TYPE
224   PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_type: %d, d_name: \"%s\" }\n",
225           dir->level, (long int) dir->idx, dir->d.d_ino, dir->d.d_type,
226           dir->d.d_name);
227 #else
228   PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_name: \"%s\" }\n",
229           dir->level, (long int) dir->idx, dir->d.d_ino,
230           dir->d.d_name);
231 #endif
232
233   ++dir->idx;
234
235   return &dir->d;
236 }
237
238
239 static void
240 my_closedir (void *dir)
241 {
242   PRINTF ("my_closedir ()\n");
243   free (dir);
244 }
245
246
247 /* We use this function for lstat as well since we don't have any.  */
248 static int
249 my_stat (const char *name, struct stat *st)
250 {
251   long int idx = find_file (name);
252
253   if (idx == -1)
254     {
255       PRINTF ("my_stat (\"%s\", ...) = -1 (%s)\n", name, strerror (errno));
256       return -1;
257     }
258
259   memset (st, '\0', sizeof (*st));
260
261   if (filesystem[idx].type == DT_UNKNOWN)
262     st->st_mode = DTTOIF (idx + 1 < nfiles
263                           && filesystem[idx].level < filesystem[idx + 1].level
264                           ? DT_DIR : DT_REG) | 0777;
265   else
266     st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
267
268   PRINTF ("my_stat (\"%s\", { st_mode: %o }) = 0\n", name, st->st_mode);
269
270   return 0;
271 }
272
273
274 static const char *glob_errstring[] =
275 {
276   [GLOB_NOSPACE] = "out of memory",
277   [GLOB_ABORTED] = "read error",
278   [GLOB_NOMATCH] = "no matches found"
279 };
280 #define nglob_errstring (sizeof (glob_errstring) / sizeof (glob_errstring[0]))
281
282
283 static const char *
284 flagstr (int flags)
285 {
286   const char *strs[] =
287   {
288     "GLOB_ERR", "GLOB_MARK", "GLOB_NOSORT", "GLOB_DOOFSS", "GLOB_NOCHECK",
289     "GLOB_APPEND", "GLOB_NOESCAPE", "GLOB_PERIOD", "GLOB_MAGCHAR",
290     "GLOB_ALTDIRFUNC", "GLOB_BRACE", "GLOB_NOMAGIC", "GLOB_TILDE",
291     "GLOB_ONLYDIR", "GLOB_TILDECHECK"
292   };
293 #define nstrs (sizeof (strs) / sizeof (strs[0]))
294   static char buf[100];
295   char *cp = buf;
296   int cnt;
297
298   for (cnt = 0; cnt < nstrs; ++cnt)
299     if (flags & (1 << cnt))
300       {
301         flags &= ~(1 << cnt);
302         if (cp != buf)
303           *cp++ = '|';
304         cp = stpcpy (cp, strs[cnt]);
305       }
306
307   if (flags != 0)
308     {
309       if (cp != buf)
310         *cp++ = '|';
311       sprintf (cp, "%#x", flags);
312     }
313
314   return buf;
315 }
316
317
318 static int
319 test_result (const char *fmt, int flags, glob_t *gl, const char *str[])
320 {
321   size_t cnt;
322   int result = 0;
323
324   printf ("results for glob (\"%s\", %s)\n", fmt, flagstr (flags));
325   for (cnt = 0; cnt < gl->gl_pathc && str[cnt] != NULL; ++cnt)
326     {
327       int ok = strcmp (gl->gl_pathv[cnt], str[cnt]) == 0;
328       const char *errstr = "";
329
330       if (! ok)
331         {
332           size_t inner;
333
334           for (inner = 0; str[inner] != NULL; ++inner)
335             if (strcmp (gl->gl_pathv[cnt], str[inner]) == 0)
336               break;
337
338           if (str[inner] == NULL)
339             errstr =  ok ? "" : " *** WRONG";
340           else
341             errstr = ok ? "" : " * wrong position";
342
343           result = 1;
344         }
345
346       printf ("  %s%s\n", gl->gl_pathv[cnt], errstr);
347     }
348   puts ("");
349
350   if (str[cnt] != NULL || cnt < gl->gl_pathc)
351     {
352       puts ("  *** incorrect number of entries");
353       result = 1;
354     }
355
356   return result;
357 }
358
359
360 int
361 main (void)
362 {
363   glob_t gl;
364   int errval;
365   int result = 0;
366   const char *fmt;
367   int flags;
368
369   mtrace ();
370
371   memset (&gl, '\0', sizeof (gl));
372
373   gl.gl_closedir = my_closedir;
374   gl.gl_readdir = my_readdir;
375   gl.gl_opendir = my_opendir;
376   gl.gl_lstat = my_stat;
377   gl.gl_stat = my_stat;
378
379 #define test(a, b, c...) \
380   fmt = a;                                                                    \
381   flags = b;                                                                  \
382   errval = glob (fmt, flags, NULL, &gl);                                      \
383   if (errval != 0)                                                            \
384     {                                                                         \
385       printf ("glob (\"%s\", %s) failed: %s\n", fmt, flagstr (flags),         \
386               errval >= 0 && errval < nglob_errstring                         \
387               ? glob_errstring[errval] : "???");                              \
388       result = 1;                                                             \
389     }                                                                         \
390   else                                                                        \
391     result |= test_result (fmt, flags, &gl, (const char *[]) { c, NULL })
392
393   test ("*/*/*", GLOB_ALTDIRFUNC,
394         "dir1lev1/dir2lev2/dir1lev3",
395         "dir1lev1/dir2lev2/file1lev3",
396         "dir1lev1/dir2lev2/file2lev3",
397         "dir1lev1/dir3lev2/file3lev3",
398         "dir1lev1/dir3lev2/file4lev3");
399
400   test ("*/*/*", GLOB_ALTDIRFUNC | GLOB_PERIOD,
401         "dir1lev1/dir1lev2/.",
402         "dir1lev1/dir1lev2/..",
403         "dir1lev1/dir2lev2/.",
404         "dir1lev1/dir2lev2/..",
405         "dir1lev1/dir2lev2/.foo",
406         "dir1lev1/dir2lev2/dir1lev3",
407         "dir1lev1/dir2lev2/file1lev3",
408         "dir1lev1/dir2lev2/file2lev3",
409         "dir1lev1/dir3lev2/.",
410         "dir1lev1/dir3lev2/..",
411         "dir1lev1/dir3lev2/file3lev3",
412         "dir1lev1/dir3lev2/file4lev3",
413         "dir2lev1/dir1lev2/.",
414         "dir2lev1/dir1lev2/..",
415         "dir2lev1/dir1lev2/.dir",
416         "dir2lev1/dir1lev2/.foo");
417
418   test ("*/*/.*", GLOB_ALTDIRFUNC,
419         "dir1lev1/dir1lev2/.",
420         "dir1lev1/dir1lev2/..",
421         "dir1lev1/dir2lev2/.",
422         "dir1lev1/dir2lev2/..",
423         "dir1lev1/dir2lev2/.foo",
424         "dir1lev1/dir3lev2/.",
425         "dir1lev1/dir3lev2/..",
426         "dir2lev1/dir1lev2/.",
427         "dir2lev1/dir1lev2/..",
428         "dir2lev1/dir1lev2/.dir",
429         "dir2lev1/dir1lev2/.foo");
430
431   test ("*1*/*2*/.*", GLOB_ALTDIRFUNC,
432         "dir1lev1/dir1lev2/.",
433         "dir1lev1/dir1lev2/..",
434         "dir1lev1/dir2lev2/.",
435         "dir1lev1/dir2lev2/..",
436         "dir1lev1/dir2lev2/.foo",
437         "dir1lev1/dir3lev2/.",
438         "dir1lev1/dir3lev2/..",
439         "dir2lev1/dir1lev2/.",
440         "dir2lev1/dir1lev2/..",
441         "dir2lev1/dir1lev2/.dir",
442         "dir2lev1/dir1lev2/.foo");
443
444   test ("*1*/*1*/.*", GLOB_ALTDIRFUNC,
445         "dir1lev1/dir1lev2/.",
446         "dir1lev1/dir1lev2/..",
447         "dir2lev1/dir1lev2/.",
448         "dir2lev1/dir1lev2/..",
449         "dir2lev1/dir1lev2/.dir",
450         "dir2lev1/dir1lev2/.foo");
451
452   test ("\\/*", GLOB_ALTDIRFUNC,
453         "/dir1lev1",
454         "/dir2lev1",
455         "/file1lev1",
456         "/file2lev1");
457
458   globfree (&gl);
459
460   return result;
461 }