1 | /* Template for tests of the GNU extension GLOB_ALTDIRFUNC. |
2 | Copyright (C) 2001-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
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 |
17 | <https://www.gnu.org/licenses/>. */ |
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: s, c: '/'); |
125 | |
126 | if (test_verbose> 0) |
127 | printf (format: "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 (format: "info: my_opendir(\"%s\") == NULL\n" , s); |
178 | return NULL; |
179 | } |
180 | |
181 | dir = (my_DIR *) malloc (size: sizeof (my_DIR)); |
182 | if (dir == NULL) |
183 | error (EXIT_FAILURE, errno, format: "cannot allocate directory handle" ); |
184 | |
185 | dir->level = filesystem[idx].level; |
186 | dir->idx = idx; |
187 | |
188 | if (test_verbose > 0) |
189 | printf (format: "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 (format: "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 (format: "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 | |
223 | dir->d.d_type = filesystem[dir->idx].type; |
224 | |
225 | strcpy (dir->d.d_name, filesystem[dir->idx].name); |
226 | |
227 | if (test_verbose > 0) |
228 | printf (format: "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); |
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 (format: "info: my_closedir ()\n" ); |
245 | free (ptr: 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 (s: name); |
254 | |
255 | if (idx == -1) |
256 | { |
257 | if (test_verbose > 0) |
258 | printf (format: "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 (format: "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 (s: buf, maxlen: sizeof (buf), format: "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 (format: "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 (format: " %s%s\n" , gl->gl_pathv[cnt], errstr); |
375 | } |
376 | puts (s: "" ); |
377 | |
378 | if (str[cnt] != NULL || cnt < gl->gl_pathc) |
379 | { |
380 | puts (s: " *** 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> |
508 | |