Fix lookup of collation sequence value during regexp matching
[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, 2010 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   static const char *const 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 #undef nstrs
316 }
317
318
319 static const char *
320 errstr (int val)
321 {
322   static const char *const strs[] =
323     {
324       [GLOB_NOSPACE] = "GLOB_NOSPACE",
325       [GLOB_ABORTED] = "GLOB_ABORTED",
326       [GLOB_NOMATCH] = "GLOB_NOMATCH",
327       [GLOB_NOSYS] = "GLOB_NOSYS"
328     };
329 #define nstrs (sizeof (strs) / sizeof (strs[0]))
330   static char buf[100];
331   if (val < 0 || val >= nstrs || strs[val] == NULL)
332     {
333       snprintf (buf, sizeof (buf), "GLOB_??? (%d)", val);
334       return buf;
335     }
336   return strs[val];
337 #undef nstrs
338 }
339
340
341 static int
342 test_result (const char *fmt, int flags, glob_t *gl, const char *str[])
343 {
344   size_t cnt;
345   int result = 0;
346
347   printf ("results for glob (\"%s\", %s)\n", fmt, flagstr (flags));
348   for (cnt = 0; cnt < gl->gl_pathc && str[cnt] != NULL; ++cnt)
349     {
350       int ok = strcmp (gl->gl_pathv[cnt], str[cnt]) == 0;
351       const char *errstr = "";
352
353       if (! ok)
354         {
355           size_t inner;
356
357           for (inner = 0; str[inner] != NULL; ++inner)
358             if (strcmp (gl->gl_pathv[cnt], str[inner]) == 0)
359               break;
360
361           if (str[inner] == NULL)
362             errstr =  ok ? "" : " *** WRONG";
363           else
364             errstr = ok ? "" : " * wrong position";
365
366           result = 1;
367         }
368
369       printf ("  %s%s\n", gl->gl_pathv[cnt], errstr);
370     }
371   puts ("");
372
373   if (str[cnt] != NULL || cnt < gl->gl_pathc)
374     {
375       puts ("  *** incorrect number of entries");
376       result = 1;
377     }
378
379   return result;
380 }
381
382
383 int
384 main (void)
385 {
386   glob_t gl;
387   int errval;
388   int result = 0;
389   const char *fmt;
390   int flags;
391
392   mtrace ();
393
394   memset (&gl, '\0', sizeof (gl));
395
396   gl.gl_closedir = my_closedir;
397   gl.gl_readdir = my_readdir;
398   gl.gl_opendir = my_opendir;
399   gl.gl_lstat = my_stat;
400   gl.gl_stat = my_stat;
401
402 #define test(a, b, r, c...) \
403   fmt = a;                                                                    \
404   flags = GLOB_ALTDIRFUNC | b;                                                \
405   errval = glob (fmt, flags, NULL, &gl);                                      \
406   if (errval != r)                                                            \
407     {                                                                         \
408       if (r == 0)                                                             \
409         printf ("glob (\"%s\", %s) failed: %s\n", fmt, flagstr (flags),       \
410                 errval >= 0 && errval < nglob_errstring                       \
411                 ? glob_errstring[errval] : "???");                            \
412       else                                                                    \
413         printf ("glob (\"%s\", %s) did not fail\n", fmt, flagstr (flags));    \
414       result = 1;                                                             \
415     }                                                                         \
416   else if (r == 0)                                                            \
417     result |= test_result (fmt, flags, &gl, (const char *[]) { c, NULL });    \
418   else                                                                        \
419     printf ("result for glob (\"%s\", %s) = %s\n\n", fmt, flagstr (flags),    \
420             errstr (errval))
421
422   test ("*/*/*", 0, 0,
423         "dir1lev1/dir2lev2/dir1lev3",
424         "dir1lev1/dir2lev2/file1lev3",
425         "dir1lev1/dir2lev2/file2lev3",
426         "dir1lev1/dir3lev2/file3lev3",
427         "dir1lev1/dir3lev2/file4lev3");
428
429   test ("*/*/*", GLOB_PERIOD, 0,
430         "dir1lev1/dir1lev2/.",
431         "dir1lev1/dir1lev2/..",
432         "dir1lev1/dir2lev2/.",
433         "dir1lev1/dir2lev2/..",
434         "dir1lev1/dir2lev2/.foo",
435         "dir1lev1/dir2lev2/dir1lev3",
436         "dir1lev1/dir2lev2/file1lev3",
437         "dir1lev1/dir2lev2/file2lev3",
438         "dir1lev1/dir3lev2/.",
439         "dir1lev1/dir3lev2/..",
440         "dir1lev1/dir3lev2/file3lev3",
441         "dir1lev1/dir3lev2/file4lev3",
442         "dir2lev1/dir1lev2/.",
443         "dir2lev1/dir1lev2/..",
444         "dir2lev1/dir1lev2/.dir",
445         "dir2lev1/dir1lev2/.foo");
446
447   test ("*/*/.*", 0, 0,
448         "dir1lev1/dir1lev2/.",
449         "dir1lev1/dir1lev2/..",
450         "dir1lev1/dir2lev2/.",
451         "dir1lev1/dir2lev2/..",
452         "dir1lev1/dir2lev2/.foo",
453         "dir1lev1/dir3lev2/.",
454         "dir1lev1/dir3lev2/..",
455         "dir2lev1/dir1lev2/.",
456         "dir2lev1/dir1lev2/..",
457         "dir2lev1/dir1lev2/.dir",
458         "dir2lev1/dir1lev2/.foo");
459
460   test ("*1*/*2*/.*", 0, 0,
461         "dir1lev1/dir1lev2/.",
462         "dir1lev1/dir1lev2/..",
463         "dir1lev1/dir2lev2/.",
464         "dir1lev1/dir2lev2/..",
465         "dir1lev1/dir2lev2/.foo",
466         "dir1lev1/dir3lev2/.",
467         "dir1lev1/dir3lev2/..",
468         "dir2lev1/dir1lev2/.",
469         "dir2lev1/dir1lev2/..",
470         "dir2lev1/dir1lev2/.dir",
471         "dir2lev1/dir1lev2/.foo");
472
473   test ("*1*/*1*/.*", 0, 0,
474         "dir1lev1/dir1lev2/.",
475         "dir1lev1/dir1lev2/..",
476         "dir2lev1/dir1lev2/.",
477         "dir2lev1/dir1lev2/..",
478         "dir2lev1/dir1lev2/.dir",
479         "dir2lev1/dir1lev2/.foo");
480
481   test ("\\/*", 0, 0,
482         "/dir1lev1",
483         "/dir2lev1",
484         "/file1lev1",
485         "/file2lev1");
486
487   test ("", 0, GLOB_NOMATCH, NULL);
488
489   test ("", GLOB_NOCHECK, 0, "");
490
491   globfree (&gl);
492
493   return result;
494 }