xref: /illumos-gate/usr/src/lib/libc/port/regex/glob.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This code is MKS code ported to Solaris originally with minimum
31  * modifications so that upgrades from MKS would readily integrate.
32  * The MKS basis for this modification was:
33  *
34  *	$Id: glob.c 1.31 1994/04/07 22:50:43 mark
35  *
36  * Additional modifications have been made to this code to make it
37  * 64-bit clean.
38  */
39 
40 /*
41  * glob, globfree -- POSIX.2 compatible file name expansion routines.
42  *
43  * Copyright 1985, 1991 by Mortice Kern Systems Inc.  All rights reserved.
44  *
45  * Written by Eric Gisin.
46  */
47 
48 #include "lint.h"
49 #include <stdio.h>
50 #include <unistd.h>
51 #include <limits.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <dirent.h>
55 #include <sys/stat.h>
56 #include <glob.h>
57 #include <errno.h>
58 #include <fnmatch.h>
59 
60 #define	GLOB__CHECK	0x80	/* stat generated paths */
61 
62 #define	INITIAL	8		/* initial pathv allocation */
63 #define	NULLCPP	((char **)0)	/* Null char ** */
64 
65 static int	globit(size_t, const char *, glob_t *, int,
66 	int (*)(const char *, int), char **);
67 static int	pstrcmp(const void *, const void *);
68 static int	append(glob_t *, const char *);
69 
70 /*
71  * Free all space consumed by glob.
72  */
73 void
74 globfree(glob_t *gp)
75 {
76 	size_t i;
77 
78 	if (gp->gl_pathv == 0)
79 		return;
80 
81 	for (i = gp->gl_offs; i < gp->gl_offs + gp->gl_pathc; ++i)
82 		free(gp->gl_pathv[i]);
83 	free((void *)gp->gl_pathv);
84 
85 	gp->gl_pathc = 0;
86 	gp->gl_pathv = NULLCPP;
87 }
88 
89 /*
90  * Do filename expansion.
91  */
92 int
93 glob(const char *pattern, int flags,
94 	int (*errfn)(const char *, int), glob_t *gp)
95 {
96 	int rv;
97 	size_t i;
98 	size_t ipathc;
99 	char	*path;
100 
101 	if ((flags & GLOB_DOOFFS) == 0)
102 		gp->gl_offs = 0;
103 
104 	if (!(flags & GLOB_APPEND)) {
105 		gp->gl_pathc = 0;
106 		gp->gl_pathn = gp->gl_offs + INITIAL;
107 		gp->gl_pathv = (char **)malloc(sizeof (char *) * gp->gl_pathn);
108 
109 		if (gp->gl_pathv == NULLCPP)
110 			return (GLOB_NOSPACE);
111 		gp->gl_pathp = gp->gl_pathv + gp->gl_offs;
112 
113 		for (i = 0; i < gp->gl_offs; ++i)
114 			gp->gl_pathv[i] = NULL;
115 	}
116 
117 	if ((path = malloc(strlen(pattern)+1)) == NULL)
118 		return (GLOB_NOSPACE);
119 
120 	ipathc = gp->gl_pathc;
121 	rv = globit(0, pattern, gp, flags, errfn, &path);
122 
123 	if (rv == GLOB_ABORTED) {
124 		/*
125 		 * User's error function returned non-zero, or GLOB_ERR was
126 		 * set, and we encountered a directory we couldn't search.
127 		 */
128 		free(path);
129 		return (GLOB_ABORTED);
130 	}
131 
132 	i = gp->gl_pathc - ipathc;
133 	if (i >= 1 && !(flags & GLOB_NOSORT)) {
134 		qsort((char *)(gp->gl_pathp+ipathc), i, sizeof (char *),
135 			pstrcmp);
136 	}
137 	if (i == 0) {
138 		if (flags & GLOB_NOCHECK)
139 			(void) append(gp, pattern);
140 		else
141 			rv = GLOB_NOMATCH;
142 	}
143 	gp->gl_pathp[gp->gl_pathc] = NULL;
144 	free(path);
145 
146 	return (rv);
147 }
148 
149 
150 /*
151  * Recursive routine to match glob pattern, and walk directories.
152  */
153 int
154 globit(size_t dend, const char *sp, glob_t *gp, int flags,
155 	int (*errfn)(const char *, int), char **path)
156 {
157 	size_t n;
158 	size_t m;
159 	ssize_t end = 0;	/* end of expanded directory */
160 	char *pat = (char *)sp;	/* pattern component */
161 	char *dp = (*path) + dend;
162 	int expand = 0;		/* path has pattern */
163 	char *cp;
164 	struct stat64 sb;
165 	DIR *dirp;
166 	struct dirent64 *d;
167 	struct dirent64 *entry = NULL;
168 	int namemax;
169 	int err;
170 
171 #define	FREE_ENTRY	if (entry) free(entry)
172 
173 	for (;;)
174 		switch (*dp++ = *(unsigned char *)sp++) {
175 		case '\0':	/* end of source path */
176 			if (expand)
177 				goto Expand;
178 			else {
179 				if (!(flags & GLOB_NOCHECK) ||
180 				    flags & (GLOB__CHECK|GLOB_MARK))
181 					if (stat64(*path, &sb) < 0) {
182 						FREE_ENTRY;
183 						return (0);
184 					}
185 				if (flags & GLOB_MARK && S_ISDIR(sb.st_mode)) {
186 					*dp = '\0';
187 					*--dp = '/';
188 				}
189 				if (append(gp, *path) < 0) {
190 					FREE_ENTRY;
191 					return (GLOB_NOSPACE);
192 				}
193 				return (0);
194 			}
195 			/*NOTREACHED*/
196 
197 		case '*':
198 		case '?':
199 		case '[':
200 		case '\\':
201 			++expand;
202 			break;
203 
204 		case '/':
205 			if (expand)
206 				goto Expand;
207 			end = dp - *path;
208 			pat = (char *)sp;
209 			break;
210 
211 		Expand:
212 			/* determine directory and open it */
213 			(*path)[end] = '\0';
214 #ifdef NAME_MAX
215 			namemax = NAME_MAX;
216 #else
217 			namemax = 1024;  /* something large */
218 #endif
219 			if ((entry = (struct dirent64 *)malloc(
220 				sizeof (struct dirent64) + namemax + 1))
221 				== NULL) {
222 				return (GLOB_NOSPACE);
223 			}
224 
225 			dirp = opendir(**path == '\0' ? "." : *path);
226 			if (dirp == (DIR *)0 || namemax == -1) {
227 				if (errfn != 0 && errfn(*path, errno) != 0 ||
228 				    flags&GLOB_ERR) {
229 					FREE_ENTRY;
230 					return (GLOB_ABORTED);
231 				}
232 				FREE_ENTRY;
233 				return (0);
234 			}
235 
236 			/* extract pattern component */
237 			n = sp - pat;
238 			if ((cp = malloc(n)) == NULL) {
239 				(void) closedir(dirp);
240 				FREE_ENTRY;
241 				return (GLOB_NOSPACE);
242 			}
243 			pat = memcpy(cp, pat, n);
244 			pat[n-1] = '\0';
245 			if (*--sp != '\0')
246 				flags |= GLOB__CHECK;
247 
248 			/* expand path to max. expansion */
249 			n = dp - *path;
250 			*path = realloc(*path,
251 				strlen(*path)+namemax+strlen(sp)+1);
252 			if (*path == NULL) {
253 				(void) closedir(dirp);
254 				free(pat);
255 				FREE_ENTRY;
256 				return (GLOB_NOSPACE);
257 			}
258 			dp = (*path) + n;
259 
260 			/* read directory and match entries */
261 			err = 0;
262 			while (readdir64_r(dirp, entry, &d) == 0) {
263 				if (d == NULL)
264 					break;
265 				cp = d->d_name;
266 				if ((flags&GLOB_NOESCAPE)
267 				    ? fnmatch(pat, cp, FNM_PERIOD|FNM_NOESCAPE)
268 				    : fnmatch(pat, cp, FNM_PERIOD))
269 					continue;
270 
271 				n = strlen(cp);
272 				(void) memcpy((*path) + end, cp, n);
273 				m = dp - *path;
274 				err = globit(end+n, sp, gp, flags, errfn, path);
275 				dp = (*path) + m;   /* globit can move path */
276 				if (err != 0)
277 					break;
278 			}
279 
280 			(void) closedir(dirp);
281 			free(pat);
282 			FREE_ENTRY;
283 			return (err);
284 		}
285 		/* NOTREACHED */
286 }
287 
288 /*
289  * Comparison routine for two name arguments, called by qsort.
290  */
291 int
292 pstrcmp(const void *npp1, const void *npp2)
293 {
294 	return (strcoll(*(char **)npp1, *(char **)npp2));
295 }
296 
297 /*
298  * Add a new matched filename to the glob_t structure, increasing the
299  * size of that array, as required.
300  */
301 int
302 append(glob_t *gp, const char *str)
303 {
304 	char *cp;
305 
306 	if ((cp = malloc(strlen(str)+1)) == NULL)
307 		return (GLOB_NOSPACE);
308 	gp->gl_pathp[gp->gl_pathc++] = strcpy(cp, str);
309 
310 	if ((gp->gl_pathc + gp->gl_offs) >= gp->gl_pathn) {
311 		gp->gl_pathn *= 2;
312 		gp->gl_pathv = (char **)realloc((void *)gp->gl_pathv,
313 						gp->gl_pathn * sizeof (char *));
314 		if (gp->gl_pathv == NULLCPP)
315 			return (GLOB_NOSPACE);
316 		gp->gl_pathp = gp->gl_pathv + gp->gl_offs;
317 	}
318 	return (0);
319 }
320