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 }