/* glob.c -- file-name wildcard pattern matching for Bash.
- Copyright (C) 1985-2002 Free Software Foundation, Inc.
+ Copyright (C) 1985-2009 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ 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 2, or (at your option)
- any later version.
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful,
+ 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 this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+ 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
#include "stdc.h"
#include "memalloc.h"
-#include "quit.h"
+
+#include <signal.h>
+
+#include "shell.h"
#include "glob.h"
#include "strmatch.h"
# 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 test_eaccess __P((char *, int));
+extern int sh_eaccess __P((char *, int));
+extern char *sh_makepath __P((const char *, const char *, int));
+extern int signal_is_pending __P((int));
+extern void run_pending_traps __P((void));
extern int extended_glob;
/* 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 *));
+static int skipname __P((char *, char *, int));
#if HANDLE_MULTIBYTE
-static int mbskipname __P((char *, char *));
+static int mbskipname __P((char *, char *, int));
#endif
#if HANDLE_MULTIBYTE
static void udequote_pathname __P((char *));
# define dequote_pathname udequote_pathname
#endif
static void dequote_pathname __P((char *));
-static int glob_testdir __P((char *));
+static int glob_testdir __P((char *, int));
static char **glob_dir_to_array __P((char *, char **, int));
+/* Make sure these names continue to agree with what's in smatch.c */
+extern char *glob_patscan __P((char *, char *, int));
+extern wchar_t *glob_patscan_wc __P((wchar_t *, wchar_t *, int));
+
+extern char *glob_dirscan __P((char *, int));
+
/* Compile `glob_loop.c' for single-byte characters. */
#define CHAR unsigned char
#define INT int
const char *pattern;
{
#if HANDLE_MULTIBYTE
- mbstate_t ps;
size_t n;
wchar_t *wpattern;
int r;
if (MB_CUR_MAX == 1)
- return (internal_glob_pattern_p (pattern));
+ return (internal_glob_pattern_p ((unsigned char *)pattern));
/* Convert strings to wide chars, and call the multibyte version. */
- memset (&ps, '\0', sizeof (ps));
- n = xmbsrtowcs (NULL, (const char **)&pattern, 0, &ps);
+ 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 (pattern));
- wpattern = (wchar_t *)xmalloc ((n + 1) * sizeof (wchar_t));
- (void) xmbsrtowcs (wpattern, (const char **)&pattern, n + 1, &ps);
+ 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
}
+#if EXTENDED_GLOB
+/* Return 1 if all subpatterns in the extended globbing pattern PAT indicate
+ that the name should be skipped. XXX - doesn't handle pattern negation,
+ not sure if it should */
+static int
+extglob_skipname (pat, dname, flags)
+ char *pat, *dname;
+ int flags;
+{
+ char *pp, *pe, *t, *se;
+ int n, r, negate;
+
+ negate = *pat == '!';
+ pp = pat + 2;
+ se = pp + strlen (pp) - 1; /* end of string */
+ pe = glob_patscan (pp, se, 0); /* end of extglob pattern (( */
+ /* we should check for invalid extglob pattern here */
+ if (pe == 0)
+ return 0;
+
+ /* if pe != se we have more of the pattern at the end of the extglob
+ pattern. Check the easy case first ( */
+ if (pe == se && *pe == ')' && (t = strchr (pp, '|')) == 0)
+ {
+ *pe = '\0';
+#if defined (HANDLE_MULTIBYTE)
+ r = mbskipname (pp, dname, flags);
+#else
+ r = skipname (pp, dname, flags); /*(*/
+#endif
+ *pe = ')';
+ return r;
+ }
+
+ /* check every subpattern */
+ while (t = glob_patscan (pp, pe, '|'))
+ {
+ n = t[-1];
+ t[-1] = '\0';
+#if defined (HANDLE_MULTIBYTE)
+ r = mbskipname (pp, dname, flags);
+#else
+ r = skipname (pp, dname, flags);
+#endif
+ t[-1] = n;
+ if (r == 0) /* if any pattern says not skip, we don't skip */
+ return r;
+ pp = t;
+ } /*(*/
+
+ /* glob_patscan might find end of pattern */
+ if (pp == se)
+ return r;
+
+ /* but if it doesn't then we didn't match a leading dot */
+ return 0;
+}
+#endif
+
/* Return 1 if DNAME should be skipped according to PAT. Mostly concerned
with matching leading `.'. */
-
static int
-skipname (pat, dname)
+skipname (pat, dname, flags)
char *pat;
char *dname;
+ int flags;
{
+#if EXTENDED_GLOB
+ if (extglob_pattern_p (pat)) /* XXX */
+ return (extglob_skipname (pat, dname, flags));
+#endif
+
/* 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] != '.' &&
(dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))))
return 1;
- /* If a dot must be explicity matched, check to see if they do. */
+ /* If a dot must be explicitly matched, check to see if they do. */
else if (noglob_dot_filenames && dname[0] == '.' && pat[0] != '.' &&
(pat[0] != '\\' || pat[1] != '.'))
return 1;
}
#if HANDLE_MULTIBYTE
+
+static int
+wchkname (pat_wc, dn_wc)
+ wchar_t *pat_wc, *dn_wc;
+{
+ /* 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'))))
+ return 1;
+
+ /* If a leading dot must be explicitly 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'.'))
+ return 1;
+
+ return 0;
+}
+
+static int
+wextglob_skipname (pat, dname, flags)
+ wchar_t *pat, *dname;
+ int flags;
+{
+#if EXTENDED_GLOB
+ wchar_t *pp, *pe, *t, n, *se;
+ int r, negate;
+
+ negate = *pat == L'!';
+ pp = pat + 2;
+ se = pp + wcslen (pp) - 1; /*(*/
+ pe = glob_patscan_wc (pp, se, 0);
+
+ if (pe == se && *pe == ')' && (t = wcschr (pp, L'|')) == 0)
+ {
+ *pe = L'\0';
+ r = wchkname (pp, dname); /*(*/
+ *pe = L')';
+ return r;
+ }
+
+ /* check every subpattern */
+ while (t = glob_patscan_wc (pp, pe, '|'))
+ {
+ n = t[-1];
+ t[-1] = L'\0';
+ r = wchkname (pp, dname);
+ t[-1] = n;
+ if (r == 0)
+ return 0;
+ pp = t;
+ }
+
+ if (pp == pe) /* glob_patscan_wc might find end of pattern */
+ return r;
+
+ /* but if it doesn't then we didn't match a leading dot */
+ return 0;
+#else
+ return (wchkname (pat, dname));
+#endif
+}
+
/* 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)
+mbskipname (pat, dname, flags)
char *pat, *dname;
+ int flags;
{
- char *pat_bak, *dn_bak;
+ int ret, ext;
wchar_t *pat_wc, *dn_wc;
- mbstate_t pat_ps, dn_ps;
- size_t pat_n, dn_n, n;
+ size_t pat_n, dn_n;
- n = strlen(pat);
- pat_bak = (char *) alloca (n + 1);
- memcpy (pat_bak, pat, n + 1);
+ if (mbsmbchar (dname) == 0 && mbsmbchar (pat) == 0)
+ return (skipname (pat, dname, flags));
- n = strlen(dname);
- dn_bak = (char *) alloca (n + 1);
- memcpy (dn_bak, dname, n + 1);
+ ext = 0;
+#if EXTENDED_GLOB
+ ext = extglob_pattern_p (pat);
+#endif
- memset(&pat_ps, '\0', sizeof(mbstate_t));
- memset(&dn_ps, '\0', sizeof(mbstate_t));
+ pat_wc = dn_wc = (wchar_t *)NULL;
- pat_n = xmbsrtowcs (NULL, (const char **)&pat_bak, 0, &pat_ps);
- dn_n = xmbsrtowcs (NULL, (const char **)&dn_bak, 0, &dn_ps);
+ pat_n = xdupmbstowcs (&pat_wc, NULL, pat);
+ if (pat_n != (size_t)-1)
+ dn_n = xdupmbstowcs (&dn_wc, NULL, dname);
+ ret = 0;
if (pat_n != (size_t)-1 && dn_n !=(size_t)-1)
- {
- pat_wc = (wchar_t *) alloca ((pat_n + 1) * sizeof(wchar_t));
- dn_wc = (wchar_t *) alloca ((dn_n + 1) * sizeof(wchar_t));
-
- (void) xmbsrtowcs (pat_wc, (const char **)&pat_bak, pat_n + 1, &pat_ps);
- (void) xmbsrtowcs (dn_wc, (const char **)&dn_bak, dn_n + 1, &dn_ps);
-
- /* 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'))))
- return 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'.'))
- return 1;
- }
+ ret = ext ? wextglob_skipname (pat_wc, dn_wc, flags) : wchkname (pat_wc, dn_wc);
+ else
+ ret = skipname (pat, dname, flags);
- return 0;
+ FREE (pat_wc);
+ FREE (dn_wc);
+
+ return ret;
}
#endif /* HANDLE_MULTIBYTE */
pathname[j++] = pathname[i++];
- if (!pathname[i - 1])
+ if (pathname[i - 1] == 0)
break;
}
- pathname[j] = '\0';
+ if (pathname)
+ pathname[j] = '\0';
}
#if HANDLE_MULTIBYTE
mbstate_t ps;
size_t len, n;
wchar_t *wpathname;
- char *pathname_bak;
int i, j;
+ wchar_t *orig_wpathname;
len = strlen (pathname);
- pathname_bak = (char *) alloca (len + 1);
- memcpy (pathname_bak, pathname , len + 1);
-
/* Convert the strings into wide characters. */
- memset (&ps, '\0', sizeof (ps));
- n = xmbsrtowcs (NULL, (const char **)&pathname_bak, 0, &ps);
+ n = xdupmbstowcs (&wpathname, NULL, pathname);
if (n == (size_t) -1)
- /* Something wrong. */
- return;
-
- wpathname = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
- (void) xmbsrtowcs (wpathname, (const char **)&pathname_bak, n + 1, &ps);
+ {
+ /* Something wrong. Fall back to single-byte */
+ udequote_pathname (pathname);
+ return;
+ }
+ orig_wpathname = wpathname;
for (i = j = 0; wpathname && wpathname[i]; )
{
wpathname[j++] = wpathname[i++];
- if (!wpathname[i - 1])
+ if (wpathname[i - 1] == L'\0')
break;
}
- wpathname[j] = L'\0';
+ 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
# define GLOB_TESTNAME(name) (lstat (name, &finfo))
#else /* !HAVE_LSTAT */
# if !defined (AFS)
-# define GLOB_TESTNAME(name) (test_eaccess (nextname, F_OK))
+# define GLOB_TESTNAME(name) (sh_eaccess (name, F_OK))
# else /* AFS */
-# define GLOB_TESTNAME(name) (access (nextname, F_OK))
+# 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)
+glob_testdir (dir, flags)
char *dir;
+ int flags;
{
struct stat finfo;
+ int r;
- if (stat (dir, &finfo) < 0)
+/*itrace("glob_testdir: testing %s" flags = %d, dir, flags);*/
+#if defined (HAVE_LSTAT)
+ r = (flags & GX_ALLDIRS) ? lstat (dir, &finfo) : stat (dir, &finfo);
+#else
+ r = stat (dir, &finfo);
+#endif
+ if (r < 0)
return (-1);
if (S_ISDIR (finfo.st_mode) == 0)
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.
char *dir;
int flags;
{
- struct globval
- {
- struct globval *next;
- char *name;
- };
-
DIR *d;
register struct dirent *dp;
- struct globval *lastlink;
+ struct globval *lastlink, *e, *dirlist;
register struct globval *nextlink;
- register char *nextname, *npat;
+ register char *nextname, *npat, *subdir;
unsigned int count;
- int lose, skip;
+ 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 = 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)
+ if (glob_testdir (dir, 0) < 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)
skip = 1;
}
+ patlen = (pat && *pat) ? strlen (pat) : 0;
+
/* 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
int dirlen;
struct stat finfo;
- if (glob_testdir (dir) < 0)
+ if (glob_testdir (dir, 0) < 0)
return ((char **) &glob_error_return);
dirlen = strlen (dir);
- nextname = (char *)malloc (dirlen + strlen (pat) + 2);
- npat = (char *)malloc (strlen (pat) + 1);
+ nextname = (char *)malloc (dirlen + patlen + 2);
+ npat = (char *)malloc (patlen + 1);
if (nextname == 0 || npat == 0)
- lose = 1;
+ {
+ FREE (nextname);
+ FREE (npat);
+ lose = 1;
+ }
else
{
strcpy (npat, pat);
{
free (nextname);
nextlink = (struct globval *)alloca (sizeof (struct globval));
- nextlink->next = (struct globval *)0;
- lastlink = nextlink;
- nextlink->name = npat;
- count = 1;
+ if (nextlink)
+ {
+ nextlink->next = (struct globval *)0;
+ lastlink = nextlink;
+ nextlink->name = npat;
+ count = 1;
+ }
+ else
+ {
+ free (npat);
+ lose = 1;
+ }
}
else
{
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)
+ if (glob_testdir (dir, 0) < 0)
return ((char **) &glob_error_return);
#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.
while (1)
{
/* Make globbing interruptible in the shell. */
- if (interrupt_state)
+ if (interrupt_state || terminating_signal)
{
lose = 1;
break;
}
-
+ else if (signal_is_pending (SIGINT)) /* XXX - make SIGINT traps responsive */
+ {
+ lose = 1;
+ break;
+ }
+
dp = readdir (d);
if (dp == NULL)
break;
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))
+ if (MB_CUR_MAX > 1 && mbskipname (pat, dp->d_name, flags))
continue;
else
#endif
- if (skipname (pat, dp->d_name))
+ if (skipname (pat, dp->d_name, flags))
continue;
- if (strmatch (pat, dp->d_name, mflags) != FNM_NOMATCH)
+ /* 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, flags);
+ if (isdir < 0 && (flags & GX_MATCHDIRS))
+ {
+ free (subdir);
+ continue;
+ }
+ }
+
+ if (flags & GX_ALLDIRS)
{
- nextlink = (struct globval *) alloca (sizeof (struct globval));
+ 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 (nextlink);
+ FREE (nextname);
+ free (subdir);
+ lose = 1;
+ break;
+ }
nextlink->next = lastlink;
+ lastlink = nextlink;
+ nextlink->name = nextname;
+ bcopy (subdir, nextname, sdlen + 1);
+ free (subdir);
+ ++count;
+ continue;
+ }
+ else if (flags & GX_MATCHDIRS)
+ free (subdir);
+
+ 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 (nextname == NULL)
+ if (nextlink == 0 || nextname == 0)
{
+ FREE (nextlink);
+ FREE (nextname);
lose = 1;
break;
}
+ nextlink->next = lastlink;
lastlink = nextlink;
nextlink->name = nextname;
bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1);
(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)
+ {
+ FREE (nextlink);
+ FREE (nextname);
+ 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 *));
/* 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;
+ /* Don't call QUIT; here; let higher layers deal with it. */
return ((char **)NULL);
}
/* Copy the name pointers from the linked list into the vector. */
- for (i = 0; i < count; ++i)
+ for (tmplink = lastlink, i = 0; i < count; ++i)
{
- name_vector[i] = lastlink->name;
- lastlink = lastlink->next;
+ 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);
}
result[i] = (char *) malloc (l + strlen (array[i]) + 3);
if (result[i] == NULL)
- return (NULL);
+ {
+ int ind;
+ for (ind = 0; ind < i; ind++)
+ free (result[ind]);
+ free (result);
+ return (NULL);
+ }
strcpy (result[i], dir);
if (add_slash)
{
char **result;
unsigned int result_size;
- char *directory_name, *filename;
+ char *directory_name, *filename, *dname, *fn;
unsigned int directory_len;
+ int free_dirname; /* flag */
+ int dflags;
result = (char **) malloc (sizeof (char *));
result_size = 1;
result[0] = NULL;
+ directory_name = NULL;
+
/* Find the filename. */
filename = strrchr (pathname, '/');
+#if defined (EXTENDED_GLOB)
+ if (filename && extended_glob)
+ {
+ fn = glob_dirscan (pathname, '/');
+#if DEBUG_MATCHING
+ if (fn != filename)
+ fprintf (stderr, "glob_filename: glob_dirscan: fn (%s) != filename (%s)\n", fn ? fn : "(null)", filename);
+#endif
+ filename = fn;
+ }
+#endif
+
if (filename == NULL)
{
filename = pathname;
directory_name = "";
directory_len = 0;
+ free_dirname = 0;
}
else
{
directory_len = (filename - pathname) + 1;
- directory_name = (char *) alloca (directory_len + 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))
+ if (directory_len > 0 && glob_pattern_p (directory_name))
{
- char **directories;
+ char **directories, *d, *p;
register unsigned int i;
+ int all_starstar, last_starstar;
+
+ all_starstar = last_starstar = 0;
+ d = directory_name;
+ dflags = flags & ~GX_MARKDIRS;
+ /* Collapse a sequence of ** patterns separated by one or more slashes
+ to a single ** terminated by a slash or NUL */
+ if ((flags & GX_GLOBSTAR) && d[0] == '*' && d[1] == '*' && (d[2] == '/' || d[2] == '\0'))
+ {
+ p = d;
+ while (d[0] == '*' && d[1] == '*' && (d[2] == '/' || d[2] == '\0'))
+ {
+ p = d;
+ if (d[2])
+ {
+ d += 3;
+ while (*d == '/')
+ d++;
+ if (*d == 0)
+ break;
+ }
+ }
+ if (*d == 0)
+ all_starstar = 1;
+ d = p;
+ dflags |= GX_ALLDIRS|GX_ADDCURDIR;
+ directory_len = strlen (d);
+ }
- if (directory_name[directory_len - 1] == '/')
- directory_name[directory_len - 1] = '\0';
+ /* If there is a non [star][star]/ component in directory_name, we
+ still need to collapse trailing sequences of [star][star]/ into
+ a single one and note that the directory name ends with [star][star],
+ so we can compensate if filename is [star][star] */
+ if ((flags & GX_GLOBSTAR) && all_starstar == 0)
+ {
+ int dl, prev;
+ prev = dl = directory_len;
+ while (dl >= 4 && d[dl - 1] == '/' &&
+ d[dl - 2] == '*' &&
+ d[dl - 3] == '*' &&
+ d[dl - 4] == '/')
+ prev = dl, dl -= 3;
+ if (dl != directory_len)
+ last_starstar = 1;
+ directory_len = prev;
+ }
- directories = glob_filename (directory_name, flags & ~GX_MARKDIRS);
+ /* If the directory name ends in [star][star]/ but the filename is
+ [star][star], just remove the final [star][star] from the directory
+ so we don't have to scan everything twice. */
+ if (last_starstar && directory_len > 4 &&
+ filename[0] == '*' && filename[1] == '*' && filename[2] == 0)
+ {
+ directory_len -= 3;
+ }
+
+ if (d[directory_len - 1] == '/')
+ d[directory_len - 1] = '\0';
+
+ directories = glob_filename (d, dflags);
+
+ if (free_dirname)
+ {
+ free (directory_name);
+ directory_name = NULL;
+ }
if (directories == NULL)
goto memory_error;
return ((char **) &glob_error_return);
}
+ /* If we have something like [star][star]/[star][star], it's no use to
+ glob **, then do it again, and throw half the results away. */
+ if (all_starstar && filename[0] == '*' && filename[1] == '*' && filename[2] == 0)
+ {
+ free ((char *) directories);
+ free (directory_name);
+ directory_name = NULL;
+ directory_len = 0;
+ goto only_filename;
+ }
+
/* 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;
+ int shouldbreak;
- /* Scan directory even on a NULL pathname. That way, `*h/'
+ shouldbreak = 0;
+ /* 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. */
- temp_results = glob_vector (filename, directories[i], flags & ~GX_MARKDIRS);
+ 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)
char **array;
register unsigned int l;
- array = glob_dir_to_array (directories[i], temp_results, flags);
+ /* 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' || filename[2] == '/'))
+ {
+ /* When do we remove null elements from temp_results? And
+ how to avoid duplicate elements in the final result? */
+ /* If (dflags & GX_NULLDIR) glob_filename potentially left a
+ NULL placeholder in the temp results just in case
+ glob_vector/glob_dir_to_array did something with it, but
+ if it didn't, and we're not supposed to be passing them
+ through for some reason ((flags & GX_NULLDIR) == 0) we
+ need to remove all the NULL elements from the beginning
+ of TEMP_RESULTS. */
+ /* If we have a null directory name and ** as the filename,
+ we have just searched for everything from the current
+ directory on down. Break now (shouldbreak = 1) to avoid
+ duplicate entries in the final result. */
+#define NULL_PLACEHOLDER(x) ((x) && *(x) && **(x) == 0)
+ if ((dflags & GX_NULLDIR) && (flags & GX_NULLDIR) == 0 &&
+ NULL_PLACEHOLDER (temp_results))
+#undef NULL_PLACEHOLDER
+ {
+ register int i, n;
+ for (n = 0; temp_results[n] && *temp_results[n] == 0; n++)
+ ;
+ i = n;
+ do
+ temp_results[i - n] = temp_results[i];
+ while (temp_results[i++] != 0);
+ array = temp_results;
+ shouldbreak = 1;
+ }
+ else
+ array = temp_results;
+ }
+ else
+ array = glob_dir_to_array (directories[i], temp_results, flags);
l = 0;
while (array[l] != NULL)
++l;
result[result_size - 1] = NULL;
/* Note that the elements of ARRAY are not freed. */
- free ((char *) array);
+ if (array != temp_results)
+ free ((char *) array);
+ else if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
+ free (temp_results); /* expanding ** case above */
+
+ if (shouldbreak)
+ break;
}
}
/* Free the directories. */
return (result);
}
+only_filename:
/* If there is only a directory name, return it. */
if (*filename == '\0')
{
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);
}
/* 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),
- flags & ~GX_MARKDIRS);
+ dflags);
if (temp_results == NULL || temp_results == (char **)&glob_error_return)
- return (temp_results);
+ {
+ if (free_dirname)
+ free (directory_name);
+ QUIT; /* XXX - shell */
+ run_pending_traps ();
+ return (temp_results);
+ }
- return (glob_dir_to_array (directory_name, temp_results, flags));
+ 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
free ((char *) result);
}
+ if (free_dirname && directory_name)
+ free (directory_name);
+
QUIT;
+ run_pending_traps ();
return (NULL);
}