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