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 }