diff options
author | Jari Aalto <jari.aalto@cante.net> | 2009-01-12 13:36:28 +0000 |
---|---|---|
committer | Jari Aalto <jari.aalto@cante.net> | 2009-09-12 16:47:00 +0000 |
commit | 3185942a5234e26ab13fa02f9c51d340cec514f8 (patch) | |
tree | 2c6fcd01779faef1f91c5d84d63981ca3479137d /lib/glob/glob.c | |
parent | f1be666c7d78939ad775078d290bec2758fa29a2 (diff) | |
download | android_external_bash-3185942a5234e26ab13fa02f9c51d340cec514f8.tar.gz android_external_bash-3185942a5234e26ab13fa02f9c51d340cec514f8.tar.bz2 android_external_bash-3185942a5234e26ab13fa02f9c51d340cec514f8.zip |
Imported from ../bash-4.0-rc1.tar.gz.
Diffstat (limited to 'lib/glob/glob.c')
-rw-r--r-- | lib/glob/glob.c | 240 |
1 files changed, 209 insertions, 31 deletions
diff --git a/lib/glob/glob.c b/lib/glob/glob.c index 08a7da8..dceb2e5 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; @@ -315,6 +329,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) + 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 +420,27 @@ 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; 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; 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') { @@ -463,6 +542,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,13 +571,69 @@ 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 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; + } + if (strmatch (pat, dp->d_name, mflags) != FNM_NOMATCH) { if (nalloca < ALLOCA_MAX) @@ -510,6 +647,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 +665,27 @@ glob_vector (pat, dir, flags) (void) closedir (d); } + /* compat: if GX_ALLDIRS, add the passed directory also */ + 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 +744,7 @@ glob_vector (pat, dir, flags) free (tmplink); } } - + return (name_vector); } @@ -678,9 +837,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 +881,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 +917,19 @@ glob_filename (pathname, flags) { char **temp_results; - /* Scan directory even on a NULL pathname. That way, `*h/' + /* 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; + 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) @@ -830,9 +1003,14 @@ glob_filename (pathname, flags) /* Just return what glob_vector () returns appended to the directory name. */ + 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; 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 +1019,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); |