t_glob.c revision 1.7 1 /* $NetBSD: t_glob.c,v 1.7 2020/03/13 20:48:33 rillig Exp $ */
2 /*-
3 * Copyright (c) 2010 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Christos Zoulas
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: t_glob.c,v 1.7 2020/03/13 20:48:33 rillig Exp $");
36
37 #include <atf-c.h>
38
39 #include <sys/param.h>
40 #include <sys/stat.h>
41
42 #include <dirent.h>
43 #include <glob.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <errno.h>
49
50 #include "h_macros.h"
51
52
53 #ifdef DEBUG
54 #define DPRINTF(a) printf a
55 #else
56 #define DPRINTF(a)
57 #endif
58
59 struct gl_file {
60 const char *name;
61 int dir;
62 };
63
64 static struct gl_file a[] = {
65 { "1", 0 },
66 { "b", 1 },
67 { "3", 0 },
68 { "4", 0 },
69 };
70
71 static struct gl_file b[] = {
72 { "x", 0 },
73 { "y", 0 },
74 { "z", 0 },
75 { "w", 0 },
76 };
77
78 struct gl_dir {
79 const char *name; /* directory name */
80 const struct gl_file *dir;
81 size_t len, pos;
82 };
83
84 static struct gl_dir d[] = {
85 { "a", a, __arraycount(a), 0 },
86 { "a/b", b, __arraycount(b), 0 },
87 };
88
89 static void
90 trim(char *buf, size_t len, const char *name)
91 {
92 char *path = buf, *epath = buf + len;
93 while (path < epath && (*path++ = *name++) != '\0')
94 continue;
95 path--;
96 while (path > buf && *--path == '/')
97 *path = '\0';
98 }
99
100 static void *
101 gl_opendir(const char *dir)
102 {
103 size_t i;
104 char buf[MAXPATHLEN];
105 trim(buf, sizeof(buf), dir);
106
107 for (i = 0; i < __arraycount(d); i++)
108 if (strcmp(buf, d[i].name) == 0) {
109 DPRINTF(("opendir %s %zu\n", buf, i));
110 return &d[i];
111 }
112 errno = ENOENT;
113 return NULL;
114 }
115
116 static struct dirent *
117 gl_readdir(void *v)
118 {
119 static struct dirent dir;
120 struct gl_dir *dd = v;
121 if (dd->pos < dd->len) {
122 const struct gl_file *f = &dd->dir[dd->pos++];
123 strcpy(dir.d_name, f->name);
124 dir.d_namlen = strlen(f->name);
125 dir.d_ino = dd->pos;
126 dir.d_type = f->dir ? DT_DIR : DT_REG;
127 DPRINTF(("readdir %s %d\n", dir.d_name, dir.d_type));
128 dir.d_reclen = _DIRENT_RECLEN(&dir, dir.d_namlen);
129 return &dir;
130 }
131 return NULL;
132 }
133
134 static int
135 gl_stat(const char *name , __gl_stat_t *st)
136 {
137 char buf[MAXPATHLEN];
138 trim(buf, sizeof(buf), name);
139 memset(st, 0, sizeof(*st));
140
141 if (strcmp(buf, "a") == 0 || strcmp(buf, "a/b") == 0) {
142 st->st_mode |= S_IFDIR;
143 return 0;
144 }
145
146 if (buf[0] == 'a' && buf[1] == '/') {
147 struct gl_file *f;
148 size_t offs, count;
149
150 if (buf[2] == 'b' && buf[3] == '/') {
151 offs = 4;
152 count = __arraycount(b);
153 f = b;
154 } else {
155 offs = 2;
156 count = __arraycount(a);
157 f = a;
158 }
159
160 for (size_t i = 0; i < count; i++)
161 if (strcmp(f[i].name, buf + offs) == 0)
162 return 0;
163 }
164 DPRINTF(("stat %s %d\n", buf, st->st_mode));
165 errno = ENOENT;
166 return -1;
167 }
168
169 static int
170 gl_lstat(const char *name , __gl_stat_t *st)
171 {
172 return gl_stat(name, st);
173 }
174
175 static void
176 gl_closedir(void *v)
177 {
178 struct gl_dir *dd = v;
179 dd->pos = 0;
180 DPRINTF(("closedir %p\n", dd));
181 }
182
183 static void
184 run(const char *p, int flags, /* const char *res */ ...)
185 {
186 glob_t gl;
187 size_t i;
188 int e;
189
190 DPRINTF(("pattern %s\n", p));
191 memset(&gl, 0, sizeof(gl));
192 gl.gl_opendir = gl_opendir;
193 gl.gl_readdir = gl_readdir;
194 gl.gl_closedir = gl_closedir;
195 gl.gl_stat = gl_stat;
196 gl.gl_lstat = gl_lstat;
197
198 switch ((e = glob(p, GLOB_ALTDIRFUNC | flags, NULL, &gl))) {
199 case 0:
200 break;
201 case GLOB_NOSPACE:
202 fprintf(stderr, "Malloc call failed.\n");
203 goto bad;
204 case GLOB_ABORTED:
205 fprintf(stderr, "Unignored error.\n");
206 goto bad;
207 case GLOB_NOMATCH:
208 fprintf(stderr, "No match, and GLOB_NOCHECK was not set.\n");
209 goto bad;
210 case GLOB_NOSYS:
211 fprintf(stderr, "Implementation does not support function.\n");
212 goto bad;
213 default:
214 fprintf(stderr, "Unknown error %d.\n", e);
215 goto bad;
216 }
217
218 for (i = 0; i < gl.gl_pathc; i++)
219 DPRINTF(("%s\n", gl.gl_pathv[i]));
220
221 va_list res;
222 va_start(res, flags);
223 const char *expected = NULL;
224 for (i = 0; (expected = va_arg(res, const char *)) != NULL && i < gl.gl_pathc; i++)
225 ATF_CHECK_STREQ(gl.gl_pathv[i], expected);
226 va_end(res);
227 ATF_CHECK_EQ(i, gl.gl_pathc);
228 ATF_CHECK_MSG(expected == NULL, "expected \"%s\" to be matched", expected);
229
230 globfree(&gl);
231 return;
232 bad:
233 ATF_REQUIRE_MSG(e == 0, "No match for `%s'", p);
234 }
235
236 #define run(p, flags, ...) (run)(p, flags, __VA_ARGS__, (const char *) 0)
237
238 ATF_TC(glob_range);
239 ATF_TC_HEAD(glob_range, tc)
240 {
241 atf_tc_set_md_var(tc, "descr",
242 "Test glob(3) range");
243 }
244
245 ATF_TC_BODY(glob_range, tc)
246 {
247 run("a/b/[x-z]", 0,
248 "a/b/x", "a/b/y", "a/b/z");
249 }
250
251 ATF_TC(glob_range_not);
252 ATF_TC_HEAD(glob_range_not, tc)
253 {
254 atf_tc_set_md_var(tc, "descr",
255 "Test glob(3) ! range");
256 }
257
258 ATF_TC_BODY(glob_range_not, tc)
259 {
260 run("a/b/[!x-z]", 0,
261 "a/b/w");
262 }
263
264 ATF_TC(glob_star);
265 ATF_TC_HEAD(glob_star, tc)
266 {
267 atf_tc_set_md_var(tc, "descr",
268 "Test glob(3) ** with GLOB_STAR");
269 }
270
271 ATF_TC_BODY(glob_star, tc)
272 {
273 run("a/**", GLOB_STAR,
274 "a/1", "a/3", "a/4", "a/b", "a/b/w", "a/b/x", "a/b/y", "a/b/z");
275 }
276
277 ATF_TC(glob_star_not);
278 ATF_TC_HEAD(glob_star_not, tc)
279 {
280 atf_tc_set_md_var(tc, "descr",
281 "Test glob(3) ** without GLOB_STAR");
282 }
283
284 ATF_TC_BODY(glob_star_not, tc)
285 {
286 run("a/**", 0,
287 "a/1", "a/3", "a/4", "a/b");
288 }
289
290 #if 0
291 ATF_TC(glob_nocheck);
292 ATF_TC_HEAD(glob_nocheck, tc)
293 {
294 atf_tc_set_md_var(tc, "descr",
295 "Test glob(3) pattern with backslash and GLOB_NOCHECK");
296 }
297
298
299 ATF_TC_BODY(glob_nocheck, tc)
300 {
301 static const char pattern[] = { 'f', 'o', 'o', '\\', ';', 'b', 'a',
302 'r', '\0' };
303 static const char *glob_nocheck[] = {
304 pattern
305 };
306 run(pattern, GLOB_NOCHECK, glob_nocheck, __arraycount(glob_nocheck));
307 }
308 #endif
309
310 ATF_TP_ADD_TCS(tp)
311 {
312 ATF_TP_ADD_TC(tp, glob_star);
313 ATF_TP_ADD_TC(tp, glob_star_not);
314 ATF_TP_ADD_TC(tp, glob_range);
315 ATF_TP_ADD_TC(tp, glob_range_not);
316 /*
317 * Remove this test for now - the GLOB_NOCHECK return value has been
318 * re-defined to return a modified pattern in revision 1.33 of glob.c
319 *
320 * ATF_TP_ADD_TC(tp, glob_nocheck);
321 */
322
323 return atf_no_error();
324 }
325