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