Home | History | Annotate | Download | only in regex
      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