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