| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 | /* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Library General Public License aspublished by the Free Software Foundation; either version 2 of theLicense, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNULibrary General Public License for more details.You should have received a copy of the GNU Library General PublicLicense along with this library; see the file COPYING.LIB.  Ifnot, write to the Free Software Foundation, Inc., 675 Mass Ave,Cambridge, MA 02139, USA.  */#include <features.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <dirent.h>#include <malloc.h>#include <fnmatch.h>#define _GNU_SOURCE#include <glob.h>extern __ptr_t (*__glob_opendir_hook) __P ((const char *directory));extern void (*__glob_closedir_hook) __P ((__ptr_t stream));extern const char *(*__glob_readdir_hook) __P ((__ptr_t stream));static int glob_in_dir __P ((const char *pattern, const char *directory,			     int flags,			     int (*errfunc) __P ((const char *, int)),			     glob_t *pglob));static int prefix_array __P ((const char *prefix, char **array, size_t n,			      int add_slash));static int collated_compare __P ((const __ptr_t, const __ptr_t));#ifdef __GLOB64extern int glob_pattern_p(const char *pattern, int quote);#else/* Return nonzero if PATTERN contains any metacharacters.   Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */int glob_pattern_p(const char *pattern, int quote){    const char *p;    int open = 0;    for (p = pattern; *p != '\0'; ++p)	switch (*p)	{	    case '?':	    case '*':		return 1;	    case '\\':		if (quote && p[1] != '\0')		    ++p;		break;	    case '[':		open = 1;		break;	    case ']':		if (open)		    return 1;		break;	}    return 0;}#endif/* Do glob searching for PATTERN, placing results in PGLOB.   The bits defined above may be set in FLAGS.   If a directory cannot be opened or read and ERRFUNC is not nil,   it is called with the pathname that caused the error, and the   `errno' value from the failing call; if it returns non-zero   `glob' returns GLOB_ABEND; if it returns zero, the error is ignored.   If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.   Otherwise, `glob' returns zero.  */intglob (pattern, flags, errfunc, pglob)     const char *pattern;     int flags;     int (*errfunc) __P ((const char *, int));     glob_t *pglob;{  const char *filename;  char *dirname;  size_t dirlen;  int status;  int oldcount;  if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0)    {      errno = EINVAL;      return -1;    }  /* Find the filename.  */  filename = strrchr (pattern, '/');  if (filename == NULL)    {      filename = pattern;      dirname = (char *) ".";      dirlen = 0;    }  else if (filename == pattern)    {      /* "/pattern".  */      dirname = (char *) "/";      dirlen = 1;      ++filename;    }  else    {      dirlen = filename - pattern;      dirname = (char *) alloca (dirlen + 1);      memcpy (dirname, pattern, dirlen);      dirname[dirlen] = '\0';      ++filename;    }  if (filename[0] == '\0' && dirlen > 1)    /* "pattern/".  Expand "pattern", appending slashes.  */    {      int val = glob (dirname, flags | GLOB_MARK, errfunc, pglob);      if (val == 0)	pglob->gl_flags = (pglob->gl_flags & ~GLOB_MARK) | (flags & GLOB_MARK);      return val;    }  if (!(flags & GLOB_APPEND))    {      pglob->gl_pathc = 0;      pglob->gl_pathv = NULL;    }  oldcount = pglob->gl_pathc;  if (glob_pattern_p (dirname, !(flags & GLOB_NOESCAPE)))    {      /* The directory name contains metacharacters, so we	 have to glob for the directory, and then glob for	 the pattern in each directory found.  */      glob_t dirs;      register int i;      status = glob (dirname,		     ((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE)) |		      GLOB_NOSORT),		     errfunc, &dirs);      if (status != 0)	return status;      /* We have successfully globbed the preceding directory name.	 For each name we found, call glob_in_dir on it and FILENAME,	 appending the results to PGLOB.  */      for (i = 0; i < dirs.gl_pathc; ++i)	{	  int oldcount;#ifdef	SHELL	  {	    /* Make globbing interruptible in the bash shell. */	    extern int interrupt_state;	    if (interrupt_state)	      {		globfree (&dirs);		globfree (&files);		return GLOB_ABEND;	      }	  }#endif /* SHELL.  */	  oldcount = pglob->gl_pathc;	  status = glob_in_dir (filename, dirs.gl_pathv[i],				(flags | GLOB_APPEND) & ~GLOB_NOCHECK,				errfunc, pglob);	  if (status == GLOB_NOMATCH)	    /* No matches in this directory.  Try the next.  */	    continue;	  if (status != 0)	    {	      globfree (&dirs);	      globfree (pglob);	      return status;	    }	  /* Stick the directory on the front of each name.  */	  if (prefix_array (dirs.gl_pathv[i],			    &pglob->gl_pathv[oldcount],			    pglob->gl_pathc - oldcount,			    flags & GLOB_MARK))	    {	      globfree (&dirs);	      globfree (pglob);	      return GLOB_NOSPACE;	    }	}      flags |= GLOB_MAGCHAR;      if (pglob->gl_pathc == oldcount)      {	/* No matches.  */	if (flags & GLOB_NOCHECK)	{	    size_t len = strlen (pattern) + 1;	    char *patcopy = (char *) malloc (len);	    if (patcopy == NULL)	      return GLOB_NOSPACE;	    memcpy (patcopy, pattern, len);	    pglob->gl_pathv	      = (char **) realloc (pglob->gl_pathv,				   (pglob->gl_pathc +				    ((flags & GLOB_DOOFFS) ?				     pglob->gl_offs : 0) +				    1 + 1) *				   sizeof (char *));	    if (pglob->gl_pathv == NULL)	      {		free (patcopy);		return GLOB_NOSPACE;	      }	    if (flags & GLOB_DOOFFS)	      while (pglob->gl_pathc < pglob->gl_offs)		pglob->gl_pathv[pglob->gl_pathc++] = NULL;	    pglob->gl_pathv[pglob->gl_pathc++] = patcopy;	    pglob->gl_pathv[pglob->gl_pathc] = NULL;	    pglob->gl_flags = flags;	}	else 	{	  return GLOB_NOMATCH;	}      }    }  else    {      status = glob_in_dir (filename, dirname, flags, errfunc, pglob);      if (status != 0)	return status;      if (dirlen > 0)	{	  /* Stick the directory on the front of each name.  */	  if (prefix_array (dirname,			    &pglob->gl_pathv[oldcount],			    pglob->gl_pathc - oldcount,			    flags & GLOB_MARK))	    {	      globfree (pglob);	      return GLOB_NOSPACE;	    }	}    }  if (flags & GLOB_MARK)    {      /* Append slashes to directory names.  glob_in_dir has already	 allocated the extra character for us.  */      int i;      struct stat st;      for (i = oldcount; i < pglob->gl_pathc; ++i)	if (lstat (pglob->gl_pathv[i], &st) == 0 &&	    S_ISDIR (st.st_mode))	  strcat (pglob->gl_pathv[i], "/");    }  if (!(flags & GLOB_NOSORT))    /* Sort the vector.  */    qsort ((__ptr_t) &pglob->gl_pathv[oldcount],	   pglob->gl_pathc - oldcount,	   sizeof (char *), (__compar_fn_t)collated_compare);  return 0;}/* Free storage allocated in PGLOB by a previous `glob' call.  */voidglobfree (pglob)     register glob_t *pglob;{  if (pglob->gl_pathv != NULL)    {      register int i = pglob->gl_flags & GLOB_DOOFFS? pglob->gl_offs : 0;      for (; i < pglob->gl_pathc; ++i)	if (pglob->gl_pathv[i] != NULL)	  free ((__ptr_t) pglob->gl_pathv[i]);      free ((__ptr_t) pglob->gl_pathv);    }}/* Do a collated comparison of A and B.  */static intcollated_compare (a, b)     const __ptr_t a;     const __ptr_t b;{  const char *const s1 = *(const char *const *) a;  const char *const s2 = *(const char *const *) b;  if (s1 == s2)    return 0;  if (s1 == NULL)    return 1;  if (s2 == NULL)    return -1;  return strcoll (s1, s2);}/* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's   elements in place.  Return nonzero if out of memory, zero if successful.   A slash is inserted between DIRNAME and each elt of ARRAY,   unless DIRNAME is just "/".  Each old element of ARRAY is freed.   If ADD_SLASH is non-zero, allocate one character more than   necessary, so that a slash can be appended later.  */static intprefix_array (dirname, array, n, add_slash)     const char *dirname;     char **array;     size_t n;     int add_slash;{  register size_t i;  size_t dirlen = strlen (dirname);  if (dirlen == 1 && dirname[0] == '/')    /* DIRNAME is just "/", so normal prepending would get us "//foo".       We want "/foo" instead, so don't prepend any chars from DIRNAME.  */    dirlen = 0;  for (i = 0; i < n; ++i)    {      size_t eltlen = strlen (array[i]) + 1;      char *new = (char *) malloc (dirlen + 1 + eltlen + (add_slash ? 1 : 0));      if (new == NULL)	{	  while (i > 0)	    free ((__ptr_t) array[--i]);	  return 1;	}      memcpy (new, dirname, dirlen);      new[dirlen] = '/';      memcpy (&new[dirlen + 1], array[i], eltlen);      free ((__ptr_t) array[i]);      array[i] = new;    }  return 0;}/* Like `glob', but PATTERN is a final pathname component,   and matches are searched for in DIRECTORY.   The GLOB_NOSORT bit in FLAGS is ignored.  No sorting is ever done.   The GLOB_APPEND flag is assumed to be set (always appends).  */static intglob_in_dir (pattern, directory, flags, errfunc, pglob)     const char *pattern;     const char *directory;     int flags;     int (*errfunc) __P ((const char *, int));     glob_t *pglob;{  __ptr_t stream;  struct globlink    {      struct globlink *next;      char *name;    };  struct globlink *names = NULL;  size_t nfound = 0;  int meta;  stream = (__glob_opendir_hook ? (*__glob_opendir_hook) (directory)	   : (__ptr_t) opendir (directory));  if (stream == NULL)    {      if ((errfunc != NULL && (*errfunc) (directory, errno)) ||	  (flags & GLOB_ERR))	return GLOB_ABORTED;    }  meta = glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));  if (meta)    flags |= GLOB_MAGCHAR;  while (1)    {      const char *name;      size_t len;      if (__glob_readdir_hook)	{	  name = (*__glob_readdir_hook) (stream);	  if (name == NULL)	    break;	  len = 0;	}      else	{	  struct dirent *d = readdir ((DIR *) stream);	  if (d == NULL)	    break;	  if (! (d->d_ino != 0))	    continue;	  name = d->d_name;#ifdef	HAVE_D_NAMLEN	  len = d->d_namlen;#else	  len = 0;#endif	}		      if ((!meta && strcmp (pattern, name) == 0)	  || fnmatch (pattern, name,		      (!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) |		      ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)) == 0)	{	  struct globlink *new	    = (struct globlink *) alloca (sizeof (struct globlink));	  if (len == 0)	    len = strlen (name);	  new->name	    = (char *) malloc (len + ((flags & GLOB_MARK) ? 1 : 0) + 1);	  if (new->name == NULL)	    goto memory_error;	  memcpy ((__ptr_t) new->name, name, len);	  new->name[len] = '\0';	  new->next = names;	  names = new;	  ++nfound;	  if (!meta)	    break;	}    }  if (nfound == 0 && (flags & GLOB_NOCHECK))    {      size_t len = strlen (pattern);      nfound = 1;      names = (struct globlink *) alloca (sizeof (struct globlink));      names->next = NULL;      names->name = (char *) malloc (len + (flags & GLOB_MARK ? 1 : 0) + 1);      if (names->name == NULL)	goto memory_error;      memcpy (names->name, pattern, len);      names->name[len] = '\0';    }  pglob->gl_pathv    = (char **) realloc (pglob->gl_pathv,			 (pglob->gl_pathc +			  ((flags & GLOB_DOOFFS) ? pglob->gl_offs : 0) +			  nfound + 1) *			 sizeof (char *));  if (pglob->gl_pathv == NULL)    goto memory_error;  if (flags & GLOB_DOOFFS)    while (pglob->gl_pathc < pglob->gl_offs)      pglob->gl_pathv[pglob->gl_pathc++] = NULL;  for (; names != NULL; names = names->next)    pglob->gl_pathv[pglob->gl_pathc++] = names->name;  pglob->gl_pathv[pglob->gl_pathc] = NULL;  pglob->gl_flags = flags;  {    int save = errno;    if (__glob_closedir_hook)      (*__glob_closedir_hook) (stream);    else      (void) closedir ((DIR *) stream);    errno = save;  }  return nfound == 0 ? GLOB_NOMATCH : 0; memory_error:  {    int save = errno;    if (__glob_closedir_hook)      (*__glob_closedir_hook) (stream);    else      (void) closedir ((DIR *) stream);    errno = save;  }  while (names != NULL)    {      if (names->name != NULL)	free ((__ptr_t) names->name);      names = names->next;    }  return GLOB_NOSPACE;}
 |