1100 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1100 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* glob.c -- file-name wildcard pattern matching for Bash.
 | |
| 
 | |
|    Copyright (C) 1985-2009 Free Software Foundation, Inc.
 | |
| 
 | |
|    This file is part of GNU Bash, the Bourne-Again SHell.
 | |
|    
 | |
|    Bash is free software: you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation, either version 3 of the License, or
 | |
|    (at your option) any later version.
 | |
| 
 | |
|    Bash is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| 
 | |
| /* To whomever it may concern: I have never seen the code which most
 | |
|    Unix programs use to perform this function.  I wrote this from scratch
 | |
|    based on specifications for the pattern matching.  --RMS.  */
 | |
| 
 | |
| #include <config.h>
 | |
| 
 | |
| #if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
 | |
|   #pragma alloca
 | |
| #endif /* _AIX && RISC6000 && !__GNUC__ */
 | |
| 
 | |
| #include "bashtypes.h"
 | |
| 
 | |
| #if defined (HAVE_UNISTD_H)
 | |
| #  include <unistd.h>
 | |
| #endif
 | |
| 
 | |
| #include "bashansi.h"
 | |
| #include "posixdir.h"
 | |
| #include "posixstat.h"
 | |
| #include "shmbutil.h"
 | |
| #include "xmalloc.h"
 | |
| 
 | |
| #include "filecntl.h"
 | |
| #if !defined (F_OK)
 | |
| #  define F_OK 0
 | |
| #endif
 | |
| 
 | |
| #include "stdc.h"
 | |
| #include "memalloc.h"
 | |
| 
 | |
| #include "shell.h"
 | |
| 
 | |
| #include "glob.h"
 | |
| #include "strmatch.h"
 | |
| 
 | |
| #if !defined (HAVE_BCOPY) && !defined (bcopy)
 | |
| #  define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
 | |
| #endif /* !HAVE_BCOPY && !bcopy */
 | |
| 
 | |
| #if !defined (NULL)
 | |
| #  if defined (__STDC__)
 | |
| #    define NULL ((void *) 0)
 | |
| #  else
 | |
| #    define NULL 0x0
 | |
| #  endif /* __STDC__ */
 | |
| #endif /* !NULL */
 | |
| 
 | |
| #if !defined (FREE)
 | |
| #  define FREE(x)	if (x) free (x)
 | |
| #endif
 | |
| 
 | |
| /* Don't try to alloca() more than this much memory for `struct globval'
 | |
|    in glob_vector() */
 | |
| #ifndef ALLOCA_MAX
 | |
| #  define ALLOCA_MAX	100000
 | |
| #endif
 | |
| 
 | |
| struct globval
 | |
|   {
 | |
|     struct globval *next;
 | |
|     char *name;
 | |
|   };
 | |
| 
 | |
| extern void throw_to_top_level __P((void));
 | |
| extern int sh_eaccess __P((char *, int));
 | |
| extern char *sh_makepath __P((const char *, const char *, int));
 | |
| 
 | |
| extern int extended_glob;
 | |
| 
 | |
| /* Global variable which controls whether or not * matches .*.
 | |
|    Non-zero means don't match .*.  */
 | |
| int noglob_dot_filenames = 1;
 | |
| 
 | |
| /* Global variable which controls whether or not filename globbing
 | |
|    is done without regard to case. */
 | |
| int glob_ignore_case = 0;
 | |
| 
 | |
| /* Global variable to return to signify an error in globbing. */
 | |
| char *glob_error_return;
 | |
| 
 | |
| static struct globval finddirs_error_return;
 | |
| 
 | |
| /* Some forward declarations. */
 | |
| static int skipname __P((char *, char *, int));
 | |
| #if HANDLE_MULTIBYTE
 | |
| static int mbskipname __P((char *, char *, int));
 | |
| #endif
 | |
| #if HANDLE_MULTIBYTE
 | |
| static void udequote_pathname __P((char *));
 | |
| static void wdequote_pathname __P((char *));
 | |
| #else
 | |
| #  define dequote_pathname udequote_pathname
 | |
| #endif
 | |
| static void dequote_pathname __P((char *));
 | |
| static int glob_testdir __P((char *));
 | |
| static char **glob_dir_to_array __P((char *, char **, int));
 | |
| 
 | |
| /* Compile `glob_loop.c' for single-byte characters. */
 | |
| #define CHAR	unsigned char
 | |
| #define INT	int
 | |
| #define L(CS)	CS
 | |
| #define INTERNAL_GLOB_PATTERN_P internal_glob_pattern_p
 | |
| #include "glob_loop.c"
 | |
| 
 | |
| /* Compile `glob_loop.c' again for multibyte characters. */
 | |
| #if HANDLE_MULTIBYTE
 | |
| 
 | |
| #define CHAR	wchar_t
 | |
| #define INT	wint_t
 | |
| #define L(CS)	L##CS
 | |
| #define INTERNAL_GLOB_PATTERN_P internal_glob_wpattern_p
 | |
| #include "glob_loop.c"
 | |
| 
 | |
| #endif /* HANDLE_MULTIBYTE */
 | |
| 
 | |
| /* And now a function that calls either the single-byte or multibyte version
 | |
|    of internal_glob_pattern_p. */
 | |
| int
 | |
| glob_pattern_p (pattern)
 | |
|      const char *pattern;
 | |
| {
 | |
| #if HANDLE_MULTIBYTE
 | |
|   size_t n;
 | |
|   wchar_t *wpattern;
 | |
|   int r;
 | |
| 
 | |
|   if (MB_CUR_MAX == 1)
 | |
|     return (internal_glob_pattern_p ((unsigned char *)pattern));
 | |
| 
 | |
|   /* Convert strings to wide chars, and call the multibyte version. */
 | |
|   n = xdupmbstowcs (&wpattern, NULL, pattern);
 | |
|   if (n == (size_t)-1)
 | |
|     /* Oops.  Invalid multibyte sequence.  Try it as single-byte sequence. */
 | |
|     return (internal_glob_pattern_p ((unsigned char *)pattern));
 | |
| 
 | |
|   r = internal_glob_wpattern_p (wpattern);
 | |
|   free (wpattern);
 | |
| 
 | |
|   return r;
 | |
| #else
 | |
|   return (internal_glob_pattern_p (pattern));
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* Return 1 if DNAME should be skipped according to PAT.  Mostly concerned
 | |
|    with matching leading `.'. */
 | |
| 
 | |
| static int
 | |
| skipname (pat, dname, flags)
 | |
|      char *pat;
 | |
|      char *dname;
 | |
|      int flags;
 | |
| {
 | |
|   /* If a leading dot need not be explicitly matched, and the pattern
 | |
|      doesn't start with a `.', don't match `.' or `..' */
 | |
|   if (noglob_dot_filenames == 0 && pat[0] != '.' &&
 | |
| 	(pat[0] != '\\' || pat[1] != '.') &&
 | |
| 	(dname[0] == '.' &&
 | |
| 	  (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))))
 | |
|     return 1;
 | |
| 
 | |
|   /* If a dot must be explicity matched, check to see if they do. */
 | |
|   else if (noglob_dot_filenames && dname[0] == '.' && pat[0] != '.' &&
 | |
| 	(pat[0] != '\\' || pat[1] != '.'))
 | |
|     return 1;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| #if HANDLE_MULTIBYTE
 | |
| /* Return 1 if DNAME should be skipped according to PAT.  Handles multibyte
 | |
|    characters in PAT and DNAME.  Mostly concerned with matching leading `.'. */
 | |
| 
 | |
| static int
 | |
| mbskipname (pat, dname, flags)
 | |
|      char *pat, *dname;
 | |
|      int flags;
 | |
| {
 | |
|   int ret;
 | |
|   wchar_t *pat_wc, *dn_wc;
 | |
|   size_t pat_n, dn_n;
 | |
| 
 | |
|   pat_n = xdupmbstowcs (&pat_wc, NULL, pat);
 | |
|   dn_n = xdupmbstowcs (&dn_wc, NULL, dname);
 | |
| 
 | |
|   ret = 0;
 | |
|   if (pat_n != (size_t)-1 && dn_n !=(size_t)-1)
 | |
|     {
 | |
|       /* If a leading dot need not be explicitly matched, and the
 | |
| 	 pattern doesn't start with a `.', don't match `.' or `..' */
 | |
|       if (noglob_dot_filenames == 0 && pat_wc[0] != L'.' &&
 | |
| 	    (pat_wc[0] != L'\\' || pat_wc[1] != L'.') &&
 | |
| 	    (dn_wc[0] == L'.' &&
 | |
| 	      (dn_wc[1] == L'\0' || (dn_wc[1] == L'.' && dn_wc[2] == L'\0'))))
 | |
| 	ret = 1;
 | |
| 
 | |
|       /* If a leading dot must be explicity matched, check to see if the
 | |
| 	 pattern and dirname both have one. */
 | |
|      else if (noglob_dot_filenames && dn_wc[0] == L'.' &&
 | |
| 	   pat_wc[0] != L'.' &&
 | |
| 	   (pat_wc[0] != L'\\' || pat_wc[1] != L'.'))
 | |
| 	ret = 1;
 | |
|     }
 | |
| 
 | |
|   FREE (pat_wc);
 | |
|   FREE (dn_wc);
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| #endif /* HANDLE_MULTIBYTE */
 | |
| 
 | |
| /* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
 | |
| static void
 | |
| udequote_pathname (pathname)
 | |
|      char *pathname;
 | |
| {
 | |
|   register int i, j;
 | |
| 
 | |
|   for (i = j = 0; pathname && pathname[i]; )
 | |
|     {
 | |
|       if (pathname[i] == '\\')
 | |
| 	i++;
 | |
| 
 | |
|       pathname[j++] = pathname[i++];
 | |
| 
 | |
|       if (pathname[i - 1] == 0)
 | |
| 	break;
 | |
|     }
 | |
|   if (pathname)
 | |
|     pathname[j] = '\0';
 | |
| }
 | |
| 
 | |
| #if HANDLE_MULTIBYTE
 | |
| /* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
 | |
| static void
 | |
| wdequote_pathname (pathname)
 | |
|      char *pathname;
 | |
| {
 | |
|   mbstate_t ps;
 | |
|   size_t len, n;
 | |
|   wchar_t *wpathname;
 | |
|   int i, j;
 | |
|   wchar_t *orig_wpathname;
 | |
| 
 | |
|   len = strlen (pathname);
 | |
|   /* Convert the strings into wide characters.  */
 | |
|   n = xdupmbstowcs (&wpathname, NULL, pathname);
 | |
|   if (n == (size_t) -1)
 | |
|     /* Something wrong. */
 | |
|     return;
 | |
|   orig_wpathname = wpathname;
 | |
| 
 | |
|   for (i = j = 0; wpathname && wpathname[i]; )
 | |
|     {
 | |
|       if (wpathname[i] == L'\\')
 | |
| 	i++;
 | |
| 
 | |
|       wpathname[j++] = wpathname[i++];
 | |
| 
 | |
|       if (wpathname[i - 1] == L'\0')
 | |
| 	break;
 | |
|     }
 | |
|   if (wpathname)
 | |
|     wpathname[j] = L'\0';
 | |
| 
 | |
|   /* Convert the wide character string into unibyte character set. */
 | |
|   memset (&ps, '\0', sizeof(mbstate_t));
 | |
|   n = wcsrtombs(pathname, (const wchar_t **)&wpathname, len, &ps);
 | |
|   pathname[len] = '\0';
 | |
| 
 | |
|   /* Can't just free wpathname here; wcsrtombs changes it in many cases. */
 | |
|   free (orig_wpathname);
 | |
| }
 | |
| 
 | |
| static void
 | |
| dequote_pathname (pathname)
 | |
|      char *pathname;
 | |
| {
 | |
|   if (MB_CUR_MAX > 1)
 | |
|     wdequote_pathname (pathname);
 | |
|   else
 | |
|     udequote_pathname (pathname);
 | |
| }
 | |
| #endif /* HANDLE_MULTIBYTE */
 | |
| 
 | |
| /* Test whether NAME exists. */
 | |
| 
 | |
| #if defined (HAVE_LSTAT)
 | |
| #  define GLOB_TESTNAME(name)  (lstat (name, &finfo))
 | |
| #else /* !HAVE_LSTAT */
 | |
| #  if !defined (AFS)
 | |
| #    define GLOB_TESTNAME(name)  (sh_eaccess (name, F_OK))
 | |
| #  else /* AFS */
 | |
| #    define GLOB_TESTNAME(name)  (access (name, F_OK))
 | |
| #  endif /* AFS */
 | |
| #endif /* !HAVE_LSTAT */
 | |
| 
 | |
| /* Return 0 if DIR is a directory, -1 otherwise. */
 | |
| static int
 | |
| glob_testdir (dir)
 | |
|      char *dir;
 | |
| {
 | |
|   struct stat finfo;
 | |
| 
 | |
| /*itrace("glob_testdir: testing %s", dir);*/
 | |
|   if (stat (dir, &finfo) < 0)
 | |
|     return (-1);
 | |
| 
 | |
|   if (S_ISDIR (finfo.st_mode) == 0)
 | |
|     return (-1);
 | |
| 
 | |
|   return (0);
 | |
| }
 | |
| 
 | |
| /* Recursively scan SDIR for directories matching PAT (PAT is always `**').
 | |
|    FLAGS is simply passed down to the recursive call to glob_vector.  Returns
 | |
|    a list of matching directory names.  EP, if non-null, is set to the last
 | |
|    element of the returned list.  NP, if non-null, is set to the number of
 | |
|    directories in the returned list.  These two variables exist for the
 | |
|    convenience of the caller (always glob_vector). */
 | |
| static struct globval *
 | |
| finddirs (pat, sdir, flags, ep, np)
 | |
|      char *pat;
 | |
|      char *sdir;
 | |
|      int flags;
 | |
|      struct globval **ep;
 | |
|      int *np;
 | |
| {
 | |
|   char **r, *n;
 | |
|   int ndirs;
 | |
|   struct globval *ret, *e, *g;
 | |
| 
 | |
| /*itrace("finddirs: pat = `%s' sdir = `%s' flags = 0x%x", pat, sdir, flags);*/
 | |
|   e = ret = 0;
 | |
|   r = glob_vector (pat, sdir, flags);
 | |
|   if (r == 0 || r[0] == 0)
 | |
|     {
 | |
|       if (np)
 | |
| 	*np = 0;
 | |
|       if (ep)
 | |
|         *ep = 0;
 | |
|       if (r && r != &glob_error_return)
 | |
| 	free (r);
 | |
|       return (struct globval *)0;
 | |
|     }
 | |
|   for (ndirs = 0; r[ndirs] != 0; ndirs++)
 | |
|     {
 | |
|       g = (struct globval *) malloc (sizeof (struct globval));
 | |
|       if (g == 0)
 | |
| 	{
 | |
| 	  while (ret)		/* free list built so far */
 | |
| 	    {
 | |
| 	      g = ret->next;
 | |
| 	      free (ret);
 | |
| 	      ret = g;
 | |
| 	    }
 | |
| 
 | |
| 	  free (r);
 | |
| 	  if (np)
 | |
| 	    *np = 0;
 | |
| 	  if (ep)
 | |
| 	    *ep = 0;
 | |
| 	  return (&finddirs_error_return);
 | |
| 	}
 | |
|       if (e == 0)
 | |
| 	e = g;
 | |
| 
 | |
|       g->next = ret;
 | |
|       ret = g;
 | |
| 
 | |
|       g->name = r[ndirs];
 | |
|     }
 | |
| 
 | |
|   free (r);
 | |
|   if (ep)
 | |
|     *ep = e;
 | |
|   if (np)
 | |
|     *np = ndirs;
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
|      	
 | |
| /* Return a vector of names of files in directory DIR
 | |
|    whose names match glob pattern PAT.
 | |
|    The names are not in any particular order.
 | |
|    Wildcards at the beginning of PAT do not match an initial period.
 | |
| 
 | |
|    The vector is terminated by an element that is a null pointer.
 | |
| 
 | |
|    To free the space allocated, first free the vector's elements,
 | |
|    then free the vector.
 | |
| 
 | |
|    Return 0 if cannot get enough memory to hold the pointer
 | |
|    and the names.
 | |
| 
 | |
|    Return -1 if cannot access directory DIR.
 | |
|    Look in errno for more information.  */
 | |
| 
 | |
| char **
 | |
| glob_vector (pat, dir, flags)
 | |
|      char *pat;
 | |
|      char *dir;
 | |
|      int flags;
 | |
| {
 | |
|   DIR *d;
 | |
|   register struct dirent *dp;
 | |
|   struct globval *lastlink, *e, *dirlist;
 | |
|   register struct globval *nextlink;
 | |
|   register char *nextname, *npat, *subdir;
 | |
|   unsigned int count;
 | |
|   int lose, skip, ndirs, isdir, sdlen, add_current, patlen;
 | |
|   register char **name_vector;
 | |
|   register unsigned int i;
 | |
|   int mflags;		/* Flags passed to strmatch (). */
 | |
|   int pflags;		/* flags passed to sh_makepath () */
 | |
|   int nalloca;
 | |
|   struct globval *firstmalloc, *tmplink;
 | |
|   char *convfn;
 | |
| 
 | |
|   lastlink = 0;
 | |
|   count = lose = skip = add_current = 0;
 | |
| 
 | |
|   firstmalloc = 0;
 | |
|   nalloca = 0;
 | |
| 
 | |
| /*itrace("glob_vector: pat = `%s' dir = `%s' flags = 0x%x", pat, dir, flags);*/
 | |
|   /* If PAT is empty, skip the loop, but return one (empty) filename. */
 | |
|   if (pat == 0 || *pat == '\0')
 | |
|     {
 | |
|       if (glob_testdir (dir) < 0)
 | |
| 	return ((char **) &glob_error_return);
 | |
| 
 | |
|       nextlink = (struct globval *)alloca (sizeof (struct globval));
 | |
|       if (nextlink == NULL)
 | |
| 	return ((char **) NULL);
 | |
| 
 | |
|       nextlink->next = (struct globval *)0;
 | |
|       nextname = (char *) malloc (1);
 | |
|       if (nextname == 0)
 | |
| 	lose = 1;
 | |
|       else
 | |
| 	{
 | |
| 	  lastlink = nextlink;
 | |
| 	  nextlink->name = nextname;
 | |
| 	  nextname[0] = '\0';
 | |
| 	  count = 1;
 | |
| 	}
 | |
| 
 | |
|       skip = 1;
 | |
|     }
 | |
| 
 | |
|   patlen = strlen (pat);
 | |
| 
 | |
|   /* If the filename pattern (PAT) does not contain any globbing characters,
 | |
|      we can dispense with reading the directory, and just see if there is
 | |
|      a filename `DIR/PAT'.  If there is, and we can access it, just make the
 | |
|      vector to return and bail immediately. */
 | |
|   if (skip == 0 && glob_pattern_p (pat) == 0)
 | |
|     {
 | |
|       int dirlen;
 | |
|       struct stat finfo;
 | |
| 
 | |
|       if (glob_testdir (dir) < 0)
 | |
| 	return ((char **) &glob_error_return);
 | |
| 
 | |
|       dirlen = strlen (dir);
 | |
|       nextname = (char *)malloc (dirlen + patlen + 2);
 | |
|       npat = (char *)malloc (patlen + 1);
 | |
|       if (nextname == 0 || npat == 0)
 | |
| 	lose = 1;
 | |
|       else
 | |
| 	{
 | |
| 	  strcpy (npat, pat);
 | |
| 	  dequote_pathname (npat);
 | |
| 
 | |
| 	  strcpy (nextname, dir);
 | |
| 	  nextname[dirlen++] = '/';
 | |
| 	  strcpy (nextname + dirlen, npat);
 | |
| 
 | |
| 	  if (GLOB_TESTNAME (nextname) >= 0)
 | |
| 	    {
 | |
| 	      free (nextname);
 | |
| 	      nextlink = (struct globval *)alloca (sizeof (struct globval));
 | |
| 	      if (nextlink)
 | |
| 		{
 | |
| 		  nextlink->next = (struct globval *)0;
 | |
| 		  lastlink = nextlink;
 | |
| 		  nextlink->name = npat;
 | |
| 		  count = 1;
 | |
| 		}
 | |
| 	      else
 | |
| 		lose = 1;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      free (nextname);
 | |
| 	      free (npat);
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       skip = 1;
 | |
|     }
 | |
| 
 | |
|   if (skip == 0)
 | |
|     {
 | |
|       /* Open the directory, punting immediately if we cannot.  If opendir
 | |
| 	 is not robust (i.e., it opens non-directories successfully), test
 | |
| 	 that DIR is a directory and punt if it's not. */
 | |
| #if defined (OPENDIR_NOT_ROBUST)
 | |
|       if (glob_testdir (dir) < 0)
 | |
| 	return ((char **) &glob_error_return);
 | |
| #endif
 | |
| 
 | |
|       d = opendir (dir);
 | |
|       if (d == NULL)
 | |
| 	return ((char **) &glob_error_return);
 | |
| 
 | |
|       /* Compute the flags that will be passed to strmatch().  We don't
 | |
| 	 need to do this every time through the loop. */
 | |
|       mflags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;
 | |
| 
 | |
| #ifdef FNM_CASEFOLD
 | |
|       if (glob_ignore_case)
 | |
| 	mflags |= FNM_CASEFOLD;
 | |
| #endif
 | |
| 
 | |
|       if (extended_glob)
 | |
| 	mflags |= FNM_EXTMATCH;
 | |
| 
 | |
|       add_current = ((flags & (GX_ALLDIRS|GX_ADDCURDIR)) == (GX_ALLDIRS|GX_ADDCURDIR));
 | |
| 
 | |
|       /* Scan the directory, finding all names that match.
 | |
| 	 For each name that matches, allocate a struct globval
 | |
| 	 on the stack and store the name in it.
 | |
| 	 Chain those structs together; lastlink is the front of the chain.  */
 | |
|       while (1)
 | |
| 	{
 | |
| 	  /* Make globbing interruptible in the shell. */
 | |
| 	  if (interrupt_state || terminating_signal)
 | |
| 	    {
 | |
| 	      lose = 1;
 | |
| 	      break;
 | |
| 	    }
 | |
| 	  
 | |
| 	  dp = readdir (d);
 | |
| 	  if (dp == NULL)
 | |
| 	    break;
 | |
| 
 | |
| 	  /* If this directory entry is not to be used, try again. */
 | |
| 	  if (REAL_DIR_ENTRY (dp) == 0)
 | |
| 	    continue;
 | |
| 
 | |
| #if 0
 | |
| 	  if (dp->d_name == 0 || *dp->d_name == 0)
 | |
| 	    continue;
 | |
| #endif
 | |
| 
 | |
| #if HANDLE_MULTIBYTE
 | |
| 	  if (MB_CUR_MAX > 1 && mbskipname (pat, dp->d_name, flags))
 | |
| 	    continue;
 | |
| 	  else
 | |
| #endif
 | |
| 	  if (skipname (pat, dp->d_name, flags))
 | |
| 	    continue;
 | |
| 
 | |
| 	  /* If we're only interested in directories, don't bother with files */
 | |
| 	  if (flags & (GX_MATCHDIRS|GX_ALLDIRS))
 | |
| 	    {
 | |
| 	      pflags = (flags & GX_ALLDIRS) ? MP_RMDOT : 0;
 | |
| 	      if (flags & GX_NULLDIR)
 | |
| 		pflags |= MP_IGNDOT;
 | |
| 	      subdir = sh_makepath (dir, dp->d_name, pflags);
 | |
| 	      isdir = glob_testdir (subdir);
 | |
| 	      if (isdir < 0 && (flags & GX_MATCHDIRS))
 | |
| 		{
 | |
| 		  free (subdir);
 | |
| 		  continue;
 | |
| 		}
 | |
| 	    }
 | |
| 
 | |
| 	  if (flags & GX_ALLDIRS)
 | |
| 	    {
 | |
| 	      if (isdir == 0)
 | |
| 		{
 | |
| 		  dirlist = finddirs (pat, subdir, (flags & ~GX_ADDCURDIR), &e, &ndirs);
 | |
| 		  if (dirlist == &finddirs_error_return)
 | |
| 		    {
 | |
| 		      free (subdir);
 | |
| 		      lose = 1;
 | |
| 		      break;
 | |
| 		    }
 | |
| 		  if (ndirs)		/* add recursive directories to list */
 | |
| 		    {
 | |
| 		      if (firstmalloc == 0)
 | |
| 		        firstmalloc = e;
 | |
| 		      e->next = lastlink;
 | |
| 		      lastlink = dirlist;
 | |
| 		      count += ndirs;
 | |
| 		    }
 | |
| 		}
 | |
| 
 | |
| 	      nextlink = (struct globval *) malloc (sizeof (struct globval));
 | |
| 	      if (firstmalloc == 0)
 | |
| 		firstmalloc = nextlink;
 | |
| 	      sdlen = strlen (subdir);
 | |
| 	      nextname = (char *) malloc (sdlen + 1);
 | |
| 	      if (nextlink == 0 || nextname == 0)
 | |
| 		{
 | |
| 		  free (subdir);
 | |
| 		  lose = 1;
 | |
| 		  break;
 | |
| 		}
 | |
| 	      nextlink->next = lastlink;
 | |
| 	      lastlink = nextlink;
 | |
| 	      nextlink->name = nextname;
 | |
| 	      bcopy (subdir, nextname, sdlen + 1);
 | |
| 	      free (subdir);
 | |
| 	      ++count;
 | |
| 	      continue;
 | |
| 	    }
 | |
| 
 | |
| 	  convfn = fnx_fromfs (dp->d_name, D_NAMLEN (dp));
 | |
| 	  if (strmatch (pat, convfn, mflags) != FNM_NOMATCH)
 | |
| 	    {
 | |
| 	      if (nalloca < ALLOCA_MAX)
 | |
| 		{
 | |
| 		  nextlink = (struct globval *) alloca (sizeof (struct globval));
 | |
| 		  nalloca += sizeof (struct globval);
 | |
| 		}
 | |
| 	      else
 | |
| 		{
 | |
| 		  nextlink = (struct globval *) malloc (sizeof (struct globval));
 | |
| 		  if (firstmalloc == 0)
 | |
| 		    firstmalloc = nextlink;
 | |
| 		}
 | |
| 
 | |
| 	      nextname = (char *) malloc (D_NAMLEN (dp) + 1);
 | |
| 	      if (nextlink == 0 || nextname == 0)
 | |
| 		{
 | |
| 		  lose = 1;
 | |
| 		  break;
 | |
| 		}
 | |
| 	      nextlink->next = lastlink;
 | |
| 	      lastlink = nextlink;
 | |
| 	      nextlink->name = nextname;
 | |
| 	      bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1);
 | |
| 	      ++count;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       (void) closedir (d);
 | |
|     }
 | |
| 
 | |
|   /* compat: if GX_ADDCURDIR, add the passed directory also.  Add an empty
 | |
|      directory name as a placeholder if GX_NULLDIR (in which case the passed
 | |
|      directory name is "."). */
 | |
|   if (add_current)
 | |
|     {
 | |
|       sdlen = strlen (dir);
 | |
|       nextname = (char *)malloc (sdlen + 1);
 | |
|       nextlink = (struct globval *) malloc (sizeof (struct globval));
 | |
|       if (nextlink == 0 || nextname == 0)
 | |
| 	lose = 1;
 | |
|       else
 | |
| 	{
 | |
| 	  nextlink->name = nextname;
 | |
| 	  nextlink->next = lastlink;
 | |
| 	  lastlink = nextlink;
 | |
| 	  if (flags & GX_NULLDIR)
 | |
| 	    nextname[0] = '\0';
 | |
| 	  else
 | |
| 	    bcopy (dir, nextname, sdlen + 1);
 | |
| 	  ++count;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (lose == 0)
 | |
|     {
 | |
|       name_vector = (char **) malloc ((count + 1) * sizeof (char *));
 | |
|       lose |= name_vector == NULL;
 | |
|     }
 | |
| 
 | |
|   /* Have we run out of memory?	 */
 | |
|   if (lose)
 | |
|     {
 | |
|       tmplink = 0;
 | |
| 
 | |
|       /* Here free the strings we have got.  */
 | |
|       while (lastlink)
 | |
| 	{
 | |
| 	  /* Since we build the list in reverse order, the first N entries
 | |
| 	     will be allocated with malloc, if firstmalloc is set, from
 | |
| 	     lastlink to firstmalloc. */
 | |
| 	  if (firstmalloc)
 | |
| 	    {
 | |
| 	      if (lastlink == firstmalloc)
 | |
| 		firstmalloc = 0;
 | |
| 	      tmplink = lastlink;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    tmplink = 0;
 | |
| 	  free (lastlink->name);
 | |
| 	  lastlink = lastlink->next;
 | |
| 	  FREE (tmplink);
 | |
| 	}
 | |
| 
 | |
|       QUIT;
 | |
| 
 | |
|       return ((char **)NULL);
 | |
|     }
 | |
| 
 | |
|   /* Copy the name pointers from the linked list into the vector.  */
 | |
|   for (tmplink = lastlink, i = 0; i < count; ++i)
 | |
|     {
 | |
|       name_vector[i] = tmplink->name;
 | |
|       tmplink = tmplink->next;
 | |
|     }
 | |
| 
 | |
|   name_vector[count] = NULL;
 | |
| 
 | |
|   /* If we allocated some of the struct globvals, free them now. */
 | |
|   if (firstmalloc)
 | |
|     {
 | |
|       tmplink = 0;
 | |
|       while (lastlink)
 | |
| 	{
 | |
| 	  tmplink = lastlink;
 | |
| 	  if (lastlink == firstmalloc)
 | |
| 	    lastlink = firstmalloc = 0;
 | |
| 	  else
 | |
| 	    lastlink = lastlink->next;
 | |
| 	  free (tmplink);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return (name_vector);
 | |
| }
 | |
| 
 | |
| /* Return a new array which is the concatenation of each string in ARRAY
 | |
|    to DIR.  This function expects you to pass in an allocated ARRAY, and
 | |
|    it takes care of free()ing that array.  Thus, you might think of this
 | |
|    function as side-effecting ARRAY.  This should handle GX_MARKDIRS. */
 | |
| static char **
 | |
| glob_dir_to_array (dir, array, flags)
 | |
|      char *dir, **array;
 | |
|      int flags;
 | |
| {
 | |
|   register unsigned int i, l;
 | |
|   int add_slash;
 | |
|   char **result, *new;
 | |
|   struct stat sb;
 | |
| 
 | |
|   l = strlen (dir);
 | |
|   if (l == 0)
 | |
|     {
 | |
|       if (flags & GX_MARKDIRS)
 | |
| 	for (i = 0; array[i]; i++)
 | |
| 	  {
 | |
| 	    if ((stat (array[i], &sb) == 0) && S_ISDIR (sb.st_mode))
 | |
| 	      {
 | |
| 		l = strlen (array[i]);
 | |
| 		new = (char *)realloc (array[i], l + 2);
 | |
| 		if (new == 0)
 | |
| 		  return NULL;
 | |
| 		new[l] = '/';
 | |
| 		new[l+1] = '\0';
 | |
| 		array[i] = new;
 | |
| 	      }
 | |
| 	  }
 | |
|       return (array);
 | |
|     }
 | |
| 
 | |
|   add_slash = dir[l - 1] != '/';
 | |
| 
 | |
|   i = 0;
 | |
|   while (array[i] != NULL)
 | |
|     ++i;
 | |
| 
 | |
|   result = (char **) malloc ((i + 1) * sizeof (char *));
 | |
|   if (result == NULL)
 | |
|     return (NULL);
 | |
| 
 | |
|   for (i = 0; array[i] != NULL; i++)
 | |
|     {
 | |
|       /* 3 == 1 for NUL, 1 for slash at end of DIR, 1 for GX_MARKDIRS */
 | |
|       result[i] = (char *) malloc (l + strlen (array[i]) + 3);
 | |
| 
 | |
|       if (result[i] == NULL)
 | |
| 	return (NULL);
 | |
| 
 | |
|       strcpy (result[i], dir);
 | |
|       if (add_slash)
 | |
| 	result[i][l] = '/';
 | |
|       strcpy (result[i] + l + add_slash, array[i]);
 | |
|       if (flags & GX_MARKDIRS)
 | |
| 	{
 | |
| 	  if ((stat (result[i], &sb) == 0) && S_ISDIR (sb.st_mode))
 | |
| 	    {
 | |
| 	      size_t rlen;
 | |
| 	      rlen = strlen (result[i]);
 | |
| 	      result[i][rlen] = '/';
 | |
| 	      result[i][rlen+1] = '\0';
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   result[i] = NULL;
 | |
| 
 | |
|   /* Free the input array.  */
 | |
|   for (i = 0; array[i] != NULL; i++)
 | |
|     free (array[i]);
 | |
|   free ((char *) array);
 | |
| 
 | |
|   return (result);
 | |
| }
 | |
| 
 | |
| /* Do globbing on PATHNAME.  Return an array of pathnames that match,
 | |
|    marking the end of the array with a null-pointer as an element.
 | |
|    If no pathnames match, then the array is empty (first element is null).
 | |
|    If there isn't enough memory, then return NULL.
 | |
|    If a file system error occurs, return -1; `errno' has the error code.  */
 | |
| char **
 | |
| glob_filename (pathname, flags)
 | |
|      char *pathname;
 | |
|      int flags;
 | |
| {
 | |
|   char **result;
 | |
|   unsigned int result_size;
 | |
|   char *directory_name, *filename, *dname;
 | |
|   unsigned int directory_len;
 | |
|   int free_dirname;			/* flag */
 | |
|   int dflags;
 | |
| 
 | |
|   result = (char **) malloc (sizeof (char *));
 | |
|   result_size = 1;
 | |
|   if (result == NULL)
 | |
|     return (NULL);
 | |
| 
 | |
|   result[0] = NULL;
 | |
| 
 | |
|   directory_name = NULL;
 | |
| 
 | |
|   /* Find the filename.  */
 | |
|   filename = strrchr (pathname, '/');
 | |
|   if (filename == NULL)
 | |
|     {
 | |
|       filename = pathname;
 | |
|       directory_name = "";
 | |
|       directory_len = 0;
 | |
|       free_dirname = 0;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       directory_len = (filename - pathname) + 1;
 | |
|       directory_name = (char *) malloc (directory_len + 1);
 | |
| 
 | |
|       if (directory_name == 0)		/* allocation failed? */
 | |
| 	return (NULL);
 | |
| 
 | |
|       bcopy (pathname, directory_name, directory_len);
 | |
|       directory_name[directory_len] = '\0';
 | |
|       ++filename;
 | |
|       free_dirname = 1;
 | |
|     }
 | |
| 
 | |
|   /* If directory_name contains globbing characters, then we
 | |
|      have to expand the previous levels.  Just recurse. */
 | |
|   if (glob_pattern_p (directory_name))
 | |
|     {
 | |
|       char **directories;
 | |
|       register unsigned int i;
 | |
| 
 | |
|       dflags = flags & ~GX_MARKDIRS;
 | |
|       if ((flags & GX_GLOBSTAR) && directory_name[0] == '*' && directory_name[1] == '*' && (directory_name[2] == '/' || directory_name[2] == '\0'))
 | |
| 	dflags |= GX_ALLDIRS|GX_ADDCURDIR;
 | |
| 
 | |
|       if (directory_name[directory_len - 1] == '/')
 | |
| 	directory_name[directory_len - 1] = '\0';
 | |
| 
 | |
|       directories = glob_filename (directory_name, dflags);
 | |
| 
 | |
|       if (free_dirname)
 | |
| 	{
 | |
| 	  free (directory_name);
 | |
| 	  directory_name = NULL;
 | |
| 	}
 | |
| 
 | |
|       if (directories == NULL)
 | |
| 	goto memory_error;
 | |
|       else if (directories == (char **)&glob_error_return)
 | |
| 	{
 | |
| 	  free ((char *) result);
 | |
| 	  return ((char **) &glob_error_return);
 | |
| 	}
 | |
|       else if (*directories == NULL)
 | |
| 	{
 | |
| 	  free ((char *) directories);
 | |
| 	  free ((char *) result);
 | |
| 	  return ((char **) &glob_error_return);
 | |
| 	}
 | |
| 
 | |
|       /* We have successfully globbed the preceding directory name.
 | |
| 	 For each name in DIRECTORIES, call glob_vector on it and
 | |
| 	 FILENAME.  Concatenate the results together.  */
 | |
|       for (i = 0; directories[i] != NULL; ++i)
 | |
| 	{
 | |
| 	  char **temp_results;
 | |
| 
 | |
| 	  /* XXX -- we've recursively scanned any directories resulting from
 | |
| 	     a `**', so turn off the flag.  We turn it on again below if
 | |
| 	     filename is `**' */
 | |
| 	  /* Scan directory even on a NULL filename.  That way, `*h/'
 | |
| 	     returns only directories ending in `h', instead of all
 | |
| 	     files ending in `h' with a `/' appended. */
 | |
| 	  dname = directories[i];
 | |
| 	  dflags = flags & ~(GX_MARKDIRS|GX_ALLDIRS|GX_ADDCURDIR);
 | |
| 	  if ((flags & GX_GLOBSTAR) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
 | |
| 	    dflags |= GX_ALLDIRS|GX_ADDCURDIR;
 | |
| 	  if (dname[0] == '\0' && filename[0])
 | |
| 	    {
 | |
| 	      dflags |= GX_NULLDIR;
 | |
| 	      dname = ".";	/* treat null directory name and non-null filename as current directory */
 | |
| 	    }
 | |
| 	  temp_results = glob_vector (filename, dname, dflags);
 | |
| 
 | |
| 	  /* Handle error cases. */
 | |
| 	  if (temp_results == NULL)
 | |
| 	    goto memory_error;
 | |
| 	  else if (temp_results == (char **)&glob_error_return)
 | |
| 	    /* This filename is probably not a directory.  Ignore it.  */
 | |
| 	    ;
 | |
| 	  else
 | |
| 	    {
 | |
| 	      char **array;
 | |
| 	      register unsigned int l;
 | |
| 
 | |
| 	      /* If we're expanding **, we don't need to glue the directory
 | |
| 		 name to the results; we've already done it in glob_vector */
 | |
| 	      if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
 | |
| 		array = temp_results;
 | |
| 	      else
 | |
| 		array = glob_dir_to_array (directories[i], temp_results, flags);
 | |
| 	      l = 0;
 | |
| 	      while (array[l] != NULL)
 | |
| 		++l;
 | |
| 
 | |
| 	      result =
 | |
| 		(char **)realloc (result, (result_size + l) * sizeof (char *));
 | |
| 
 | |
| 	      if (result == NULL)
 | |
| 		goto memory_error;
 | |
| 
 | |
| 	      for (l = 0; array[l] != NULL; ++l)
 | |
| 		result[result_size++ - 1] = array[l];
 | |
| 
 | |
| 	      result[result_size - 1] = NULL;
 | |
| 
 | |
| 	      /* Note that the elements of ARRAY are not freed.  */
 | |
| 	      if (array != temp_results)
 | |
| 		free ((char *) array);
 | |
| 	    }
 | |
| 	}
 | |
|       /* Free the directories.  */
 | |
|       for (i = 0; directories[i]; i++)
 | |
| 	free (directories[i]);
 | |
| 
 | |
|       free ((char *) directories);
 | |
| 
 | |
|       return (result);
 | |
|     }
 | |
| 
 | |
|   /* If there is only a directory name, return it. */
 | |
|   if (*filename == '\0')
 | |
|     {
 | |
|       result = (char **) realloc ((char *) result, 2 * sizeof (char *));
 | |
|       if (result == NULL)
 | |
| 	return (NULL);
 | |
|       /* Handle GX_MARKDIRS here. */
 | |
|       result[0] = (char *) malloc (directory_len + 1);
 | |
|       if (result[0] == NULL)
 | |
| 	goto memory_error;
 | |
|       bcopy (directory_name, result[0], directory_len + 1);
 | |
|       if (free_dirname)
 | |
| 	free (directory_name);
 | |
|       result[1] = NULL;
 | |
|       return (result);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       char **temp_results;
 | |
| 
 | |
|       /* There are no unquoted globbing characters in DIRECTORY_NAME.
 | |
| 	 Dequote it before we try to open the directory since there may
 | |
| 	 be quoted globbing characters which should be treated verbatim. */
 | |
|       if (directory_len > 0)
 | |
| 	dequote_pathname (directory_name);
 | |
| 
 | |
|       /* We allocated a small array called RESULT, which we won't be using.
 | |
| 	 Free that memory now. */
 | |
|       free (result);
 | |
| 
 | |
|       /* Just return what glob_vector () returns appended to the
 | |
| 	 directory name. */
 | |
|       /* If flags & GX_ALLDIRS, we're called recursively */
 | |
|       dflags = flags & ~GX_MARKDIRS;
 | |
|       if (directory_len == 0)
 | |
| 	dflags |= GX_NULLDIR;
 | |
|       if ((flags & GX_GLOBSTAR) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
 | |
| 	{
 | |
| 	  dflags |= GX_ALLDIRS|GX_ADDCURDIR;
 | |
| #if 0
 | |
| 	  /* If we want all directories (dflags & GX_ALLDIRS) and we're not
 | |
| 	     being called recursively as something like `echo [star][star]/[star].o'
 | |
| 	     ((flags & GX_ALLDIRS) == 0), we want to prevent glob_vector from
 | |
| 	     adding a null directory name to the front of the temp_results
 | |
| 	     array.  We turn off ADDCURDIR if not called recursively and
 | |
| 	     dlen == 0 */
 | |
| #endif
 | |
| 	  if (directory_len == 0 && (flags & GX_ALLDIRS) == 0)
 | |
| 	    dflags &= ~GX_ADDCURDIR;
 | |
| 	}
 | |
|       temp_results = glob_vector (filename,
 | |
| 				  (directory_len == 0 ? "." : directory_name),
 | |
| 				  dflags);
 | |
| 
 | |
|       if (temp_results == NULL || temp_results == (char **)&glob_error_return)
 | |
| 	{
 | |
| 	  if (free_dirname)
 | |
| 	    free (directory_name);
 | |
| 	  return (temp_results);
 | |
| 	}
 | |
| 
 | |
|       result = glob_dir_to_array ((dflags & GX_ALLDIRS) ? "" : directory_name, temp_results, flags);
 | |
|       if (free_dirname)
 | |
| 	free (directory_name);
 | |
|       return (result);
 | |
|     }
 | |
| 
 | |
|   /* We get to memory_error if the program has run out of memory, or
 | |
|      if this is the shell, and we have been interrupted. */
 | |
|  memory_error:
 | |
|   if (result != NULL)
 | |
|     {
 | |
|       register unsigned int i;
 | |
|       for (i = 0; result[i] != NULL; ++i)
 | |
| 	free (result[i]);
 | |
|       free ((char *) result);
 | |
|     }
 | |
| 
 | |
|   if (free_dirname && directory_name)
 | |
|     free (directory_name);
 | |
| 
 | |
|   QUIT;
 | |
| 
 | |
|   return (NULL);
 | |
| }
 | |
| 
 | |
| #if defined (TEST)
 | |
| 
 | |
| main (argc, argv)
 | |
|      int argc;
 | |
|      char **argv;
 | |
| {
 | |
|   unsigned int i;
 | |
| 
 | |
|   for (i = 1; i < argc; ++i)
 | |
|     {
 | |
|       char **value = glob_filename (argv[i], 0);
 | |
|       if (value == NULL)
 | |
| 	puts ("Out of memory.");
 | |
|       else if (value == &glob_error_return)
 | |
| 	perror (argv[i]);
 | |
|       else
 | |
| 	for (i = 0; value[i] != NULL; i++)
 | |
| 	  puts (value[i]);
 | |
|     }
 | |
| 
 | |
|   exit (0);
 | |
| }
 | |
| #endif	/* TEST.  */
 | 
