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 | }
|
---|