summaryrefslogtreecommitdiff
path: root/lib/glob/glob.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/glob/glob.c')
-rw-r--r--lib/glob/glob.c289
1 files changed, 249 insertions, 40 deletions
diff --git a/lib/glob/glob.c b/lib/glob/glob.c
index 08a7da8..c77618f 100644
--- a/lib/glob/glob.c
+++ b/lib/glob/glob.c
@@ -1,20 +1,22 @@
/* glob.c -- file-name wildcard pattern matching for Bash.
- Copyright (C) 1985-2005 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
@@ -45,7 +47,8 @@
#include "stdc.h"
#include "memalloc.h"
-#include "quit.h"
+
+#include "shell.h"
#include "glob.h"
#include "strmatch.h"
@@ -72,8 +75,15 @@
# 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;
@@ -88,10 +98,12 @@ 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 *));
+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 *));
@@ -154,9 +166,10 @@ glob_pattern_p (pattern)
with matching leading `.'. */
static int
-skipname (pat, dname)
+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 `..' */
@@ -179,8 +192,9 @@ skipname (pat, dname)
characters in PAT and DNAME. Mostly concerned with matching leading `.'. */
static int
-mbskipname (pat, dname)
+mbskipname (pat, dname, flags)
char *pat, *dname;
+ int flags;
{
int ret;
wchar_t *pat_wc, *dn_wc;
@@ -232,7 +246,8 @@ udequote_pathname (pathname)
if (pathname[i - 1] == 0)
break;
}
- pathname[j] = '\0';
+ if (pathname)
+ pathname[j] = '\0';
}
#if HANDLE_MULTIBYTE
@@ -265,7 +280,8 @@ wdequote_pathname (pathname)
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));
@@ -293,9 +309,9 @@ dequote_pathname (pathname)
# define GLOB_TESTNAME(name) (lstat (name, &finfo))
#else /* !HAVE_LSTAT */
# if !defined (AFS)
-# define GLOB_TESTNAME(name) (sh_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 */
@@ -306,6 +322,7 @@ glob_testdir (dir)
{
struct stat finfo;
+/*itrace("glob_testdir: testing %s", dir);*/
if (stat (dir, &finfo) < 0)
return (-1);
@@ -315,6 +332,75 @@ glob_testdir (dir)
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.
@@ -337,31 +423,28 @@ glob_vector (pat, dir, flags)
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')
{
@@ -387,6 +470,8 @@ glob_vector (pat, dir, flags)
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
@@ -400,8 +485,8 @@ glob_vector (pat, dir, flags)
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;
else
@@ -463,6 +548,8 @@ glob_vector (pat, dir, flags)
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.
@@ -490,14 +577,71 @@ glob_vector (pat, dir, flags)
#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);
+ 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)
{
@@ -510,6 +654,7 @@ glob_vector (pat, dir, flags)
if (firstmalloc == 0)
firstmalloc = nextlink;
}
+
nextname = (char *) malloc (D_NAMLEN (dp) + 1);
if (nextlink == 0 || nextname == 0)
{
@@ -527,6 +672,29 @@ glob_vector (pat, dir, flags)
(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 *));
@@ -585,7 +753,7 @@ glob_vector (pat, dir, flags)
free (tmplink);
}
}
-
+
return (name_vector);
}
@@ -678,9 +846,10 @@ glob_filename (pathname, flags)
{
char **result;
unsigned int result_size;
- char *directory_name, *filename;
+ char *directory_name, *filename, *dname;
unsigned int directory_len;
int free_dirname; /* flag */
+ int dflags;
result = (char **) malloc (sizeof (char *));
result_size = 1;
@@ -721,10 +890,14 @@ glob_filename (pathname, flags)
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, flags & ~GX_MARKDIRS);
+ directories = glob_filename (directory_name, dflags);
if (free_dirname)
{
@@ -753,10 +926,22 @@ glob_filename (pathname, flags)
{
char **temp_results;
- /* Scan directory even on a NULL pathname. That way, `*h/'
+ /* 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)
@@ -769,7 +954,12 @@ glob_filename (pathname, flags)
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')
+ array = temp_results;
+ else
+ array = glob_dir_to_array (directories[i], temp_results, flags);
l = 0;
while (array[l] != NULL)
++l;
@@ -786,7 +976,8 @@ glob_filename (pathname, flags)
result[result_size - 1] = NULL;
/* Note that the elements of ARRAY are not freed. */
- free ((char *) array);
+ if (array != temp_results)
+ free ((char *) array);
}
}
/* Free the directories. */
@@ -830,9 +1021,27 @@ glob_filename (pathname, flags)
/* 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)
{
@@ -841,7 +1050,7 @@ glob_filename (pathname, flags)
return (temp_results);
}
- result = 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);