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