xref: /netbsd-src/tests/lib/libc/gen/t_glob.c (revision cf957f51cbf4427c207359713a6508c6f00f90f7)
1 /*	$NetBSD: t_glob.c,v 1.10 2020/03/13 23:27:54 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.10 2020/03/13 23:27:54 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 vfs_file {
60 	char type;		/* 'd' or '-', like in ls(1) */
61 	const char *name;
62 };
63 
64 static struct vfs_file a[] = {
65 	{ '-', "1" },
66 	{ 'd', "b" },
67 	{ '-', "3" },
68 	{ '-', "4" },
69 };
70 
71 static struct vfs_file b[] = {
72 	{ '-', "x" },
73 	{ '-', "y" },
74 	{ '-', "z" },
75 	{ '-', "w" },
76 };
77 
78 static struct vfs_file hidden_dir[] = {
79 	{ '-', "visible-file" },
80 	{ '-', ".hidden-file" },
81 };
82 
83 static struct vfs_file dot[] = {
84 	{ 'd', "a" },
85 	{ 'd', ".hidden-dir" },
86 };
87 
88 struct vfs_dir {
89 	const char *name;	/* full directory name */
90 	const struct vfs_file *entries;
91 	size_t entries_len;
92 	size_t pos;		/* only between opendir/closedir */
93 };
94 
95 #define VFS_DIR_INIT(name, entries) \
96     { name, entries, __arraycount(entries), 0 }
97 
98 static struct vfs_dir d[] = {
99 	VFS_DIR_INIT("a", a),
100 	VFS_DIR_INIT("a/b", b),
101 	VFS_DIR_INIT(".", dot),
102 	VFS_DIR_INIT(".hidden-dir", hidden_dir),
103 };
104 
105 static void
trim(char * buf,size_t len,const char * name)106 trim(char *buf, size_t len, const char *name)
107 {
108 	char *path = buf, *epath = buf + len;
109 	while (path < epath && (*path++ = *name++) != '\0')
110 		continue;
111 	path--;
112 	while (path > buf && *--path == '/')
113 		*path = '\0';
114 }
115 
116 static void *
vfs_opendir(const char * dir)117 vfs_opendir(const char *dir)
118 {
119 	size_t i;
120 	char buf[MAXPATHLEN];
121 	trim(buf, sizeof(buf), dir);
122 
123 	for (i = 0; i < __arraycount(d); i++)
124 		if (strcmp(buf, d[i].name) == 0) {
125 			DPRINTF(("opendir %s %p\n", buf, &d[i]));
126 			return &d[i];
127 		}
128 	DPRINTF(("opendir %s ENOENT\n", buf));
129 	errno = ENOENT;
130 	return NULL;
131 }
132 
133 static struct dirent *
vfs_readdir(void * v)134 vfs_readdir(void *v)
135 {
136 	static struct dirent dir;
137 	struct vfs_dir *dd = v;
138 	if (dd->pos < dd->entries_len) {
139 		const struct vfs_file *f = &dd->entries[dd->pos++];
140 		strcpy(dir.d_name, f->name);
141 		dir.d_namlen = strlen(f->name);
142 		dir.d_ino = dd->pos;
143 		dir.d_type = f->type == 'd' ? DT_DIR : DT_REG;
144 		DPRINTF(("readdir %s %d\n", dir.d_name, dir.d_type));
145 		dir.d_reclen = _DIRENT_RECLEN(&dir, dir.d_namlen);
146 		return &dir;
147 	}
148 	return NULL;
149 }
150 
151 static int
vfs_stat(const char * name,__gl_stat_t * st)152 vfs_stat(const char *name , __gl_stat_t *st)
153 {
154 	char buf[MAXPATHLEN];
155 	trim(buf, sizeof(buf), name);
156 	memset(st, 0, sizeof(*st));
157 
158 	for (size_t i = 0; i < __arraycount(d); i++)
159 		if (strcmp(buf, d[i].name) == 0) {
160 			st->st_mode = S_IFDIR | 0755;
161 			goto out;
162 		}
163 
164 	for (size_t i = 0; i < __arraycount(d); i++) {
165 		size_t dir_len = strlen(d[i].name);
166 		if (strncmp(buf, d[i].name, dir_len) != 0)
167 			continue;
168 		if (buf[dir_len] != '/')
169 			continue;
170 		const char *base = buf + dir_len + 1;
171 
172 		for (size_t j = 0; j < d[i].entries_len; j++) {
173 			const struct vfs_file *f = &d[i].entries[j];
174 			if (strcmp(f->name, base) != 0)
175 				continue;
176 			ATF_CHECK(f->type != 'd'); // handled above
177 			st->st_mode = S_IFREG | 0644;
178 			goto out;
179 		}
180 	}
181 	DPRINTF(("stat %s ENOENT\n", buf));
182 	errno = ENOENT;
183 	return -1;
184 out:
185 	DPRINTF(("stat %s %06o\n", buf, st->st_mode));
186 	return 0;
187 }
188 
189 static int
vfs_lstat(const char * name,__gl_stat_t * st)190 vfs_lstat(const char *name , __gl_stat_t *st)
191 {
192 	return vfs_stat(name, st);
193 }
194 
195 static void
vfs_closedir(void * v)196 vfs_closedir(void *v)
197 {
198 	struct vfs_dir *dd = v;
199 	dd->pos = 0;
200 	DPRINTF(("closedir %p\n", dd));
201 }
202 
203 static void
run(const char * p,int flags,...)204 run(const char *p, int flags, /* const char *res */ ...)
205 {
206 	glob_t gl;
207 	size_t i;
208 	int e;
209 
210 	DPRINTF(("pattern %s\n", p));
211 	memset(&gl, 0, sizeof(gl));
212 	gl.gl_opendir = vfs_opendir;
213 	gl.gl_readdir = vfs_readdir;
214 	gl.gl_closedir = vfs_closedir;
215 	gl.gl_stat = vfs_stat;
216 	gl.gl_lstat = vfs_lstat;
217 
218 	switch ((e = glob(p, GLOB_ALTDIRFUNC | flags, NULL, &gl))) {
219 	case 0:
220 		break;
221 	case GLOB_NOSPACE:
222 		fprintf(stderr, "Malloc call failed.\n");
223 		goto bad;
224 	case GLOB_ABORTED:
225 		fprintf(stderr, "Unignored error.\n");
226 		goto bad;
227 	case GLOB_NOMATCH:
228 		fprintf(stderr, "No match, and GLOB_NOCHECK was not set.\n");
229 		goto bad;
230 	case GLOB_NOSYS:
231 		fprintf(stderr, "Implementation does not support function.\n");
232 		goto bad;
233 	default:
234 		fprintf(stderr, "Unknown error %d.\n", e);
235 		goto bad;
236 	}
237 
238 	for (i = 0; i < gl.gl_pathc; i++)
239 		DPRINTF(("glob result %zu: %s\n", i, gl.gl_pathv[i]));
240 
241 	va_list res;
242 	va_start(res, flags);
243 	i = 0;
244 	const char *name;
245 	while ((name = va_arg(res, const char *)) != NULL && i < gl.gl_pathc) {
246 		ATF_CHECK_STREQ(gl.gl_pathv[i], name);
247 		i++;
248 	}
249 	va_end(res);
250 	ATF_CHECK_EQ_MSG(i, gl.gl_pathc,
251 	    "expected %zu results, got %zu", i, gl.gl_pathc);
252 	ATF_CHECK_EQ_MSG(name, NULL,
253 	    "\"%s\" should have been found, but wasn't", name);
254 
255 	globfree(&gl);
256 	return;
257 bad:
258 	ATF_REQUIRE_MSG(e == 0, "No match for `%s'", p);
259 }
260 
261 #define run(p, flags, ...) (run)(p, flags, __VA_ARGS__, (const char *) 0)
262 
263 ATF_TC(glob_range);
ATF_TC_HEAD(glob_range,tc)264 ATF_TC_HEAD(glob_range, tc)
265 {
266 	atf_tc_set_md_var(tc, "descr",
267 	    "Test glob(3) range");
268 }
269 
ATF_TC_BODY(glob_range,tc)270 ATF_TC_BODY(glob_range, tc)
271 {
272 	run("a/b/[x-z]", 0,
273 	    "a/b/x", "a/b/y", "a/b/z");
274 }
275 
276 ATF_TC(glob_range_not);
ATF_TC_HEAD(glob_range_not,tc)277 ATF_TC_HEAD(glob_range_not, tc)
278 {
279 	atf_tc_set_md_var(tc, "descr",
280 	    "Test glob(3) ! range");
281 }
282 
ATF_TC_BODY(glob_range_not,tc)283 ATF_TC_BODY(glob_range_not, tc)
284 {
285 	run("a/b/[!x-z]", 0,
286 	    "a/b/w");
287 }
288 
289 ATF_TC(glob_star);
ATF_TC_HEAD(glob_star,tc)290 ATF_TC_HEAD(glob_star, tc)
291 {
292 	atf_tc_set_md_var(tc, "descr",
293 	    "Test glob(3) ** with GLOB_STAR");
294 }
295 
ATF_TC_BODY(glob_star,tc)296 ATF_TC_BODY(glob_star, tc)
297 {
298 	run("a/**", GLOB_STAR,
299 	    "a/1", "a/3", "a/4", "a/b", "a/b/w", "a/b/x", "a/b/y", "a/b/z");
300 }
301 
302 ATF_TC(glob_star_not);
ATF_TC_HEAD(glob_star_not,tc)303 ATF_TC_HEAD(glob_star_not, tc)
304 {
305 	atf_tc_set_md_var(tc, "descr",
306 	    "Test glob(3) ** without GLOB_STAR");
307 }
308 
ATF_TC_BODY(glob_star_not,tc)309 ATF_TC_BODY(glob_star_not, tc)
310 {
311 	run("a/**", 0,
312 	    "a/1", "a/3", "a/4", "a/b");
313 }
314 
315 ATF_TC(glob_star_star);
ATF_TC_HEAD(glob_star_star,tc)316 ATF_TC_HEAD(glob_star_star, tc)
317 {
318 	atf_tc_set_md_var(tc, "descr",
319 	    "Test glob(3) with star-star");
320 }
321 
ATF_TC_BODY(glob_star_star,tc)322 ATF_TC_BODY(glob_star_star, tc)
323 {
324 	run("**", GLOB_STAR,
325 	    "a",
326 	    "a/1", "a/3", "a/4", "a/b",
327 	    "a/b/w", "a/b/x", "a/b/y", "a/b/z");
328 }
329 
330 ATF_TC(glob_hidden);
ATF_TC_HEAD(glob_hidden,tc)331 ATF_TC_HEAD(glob_hidden, tc)
332 {
333 	atf_tc_set_md_var(tc, "descr",
334 	    "Test glob(3) with hidden directory");
335 }
336 
ATF_TC_BODY(glob_hidden,tc)337 ATF_TC_BODY(glob_hidden, tc)
338 {
339 	run(".**", GLOB_STAR,
340 	    ".hidden-dir",
341 	    ".hidden-dir/visible-file");
342 }
343 
344 #if 0
345 ATF_TC(glob_nocheck);
346 ATF_TC_HEAD(glob_nocheck, tc)
347 {
348 	atf_tc_set_md_var(tc, "descr",
349 	    "Test glob(3) pattern with backslash and GLOB_NOCHECK");
350 }
351 
352 
353 ATF_TC_BODY(glob_nocheck, tc)
354 {
355 	static const char pattern[] = { 'f', 'o', 'o', '\\', ';', 'b', 'a',
356 	    'r', '\0' };
357 	static const char *glob_nocheck[] = {
358 	    pattern
359 	};
360 	run(pattern, GLOB_NOCHECK, glob_nocheck, __arraycount(glob_nocheck));
361 }
362 #endif
363 
ATF_TP_ADD_TCS(tp)364 ATF_TP_ADD_TCS(tp)
365 {
366 	ATF_TP_ADD_TC(tp, glob_star);
367 	ATF_TP_ADD_TC(tp, glob_star_not);
368 	ATF_TP_ADD_TC(tp, glob_range);
369 	ATF_TP_ADD_TC(tp, glob_range_not);
370 	ATF_TP_ADD_TC(tp, glob_star_star);
371 	ATF_TP_ADD_TC(tp, glob_hidden);
372 /*
373  * Remove this test for now - the GLOB_NOCHECK return value has been
374  * re-defined to return a modified pattern in revision 1.33 of glob.c
375  *
376  *	ATF_TP_ADD_TC(tp, glob_nocheck);
377  */
378 
379 	return atf_no_error();
380 }
381