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