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