summaryrefslogtreecommitdiffstats
path: root/lib/glob/glob.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/glob/glob.c1554
1 files changed, 1554 insertions, 0 deletions
diff --git a/lib/glob/glob.c b/lib/glob/glob.c
new file mode 100644
index 0000000..eb6277f
--- /dev/null
+++ b/lib/glob/glob.c
@@ -0,0 +1,1554 @@
+/* glob.c -- file-name wildcard pattern matching for Bash.
+
+ Copyright (C) 1985-2020 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 <signal.h>
+
+#include "shell.h"
+#include "general.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 PARAMS((void));
+extern int sh_eaccess PARAMS((const char *, int));
+extern char *sh_makepath PARAMS((const char *, const char *, int));
+extern int signal_is_pending PARAMS((int));
+extern void run_pending_traps PARAMS((void));
+
+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 controlling whether globbing ever returns . or ..
+ regardless of the pattern. If set to 1, no glob pattern will ever
+ match `.' or `..'. Disabled by default. */
+int glob_always_skip_dot_and_dotdot = 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 PARAMS((char *, char *, int));
+#if HANDLE_MULTIBYTE
+static int mbskipname PARAMS((char *, char *, int));
+#endif
+void udequote_pathname PARAMS((char *));
+#if HANDLE_MULTIBYTE
+void wcdequote_pathname PARAMS((wchar_t *));
+static void wdequote_pathname PARAMS((char *));
+#else
+# define dequote_pathname udequote_pathname
+#endif
+static void dequote_pathname PARAMS((char *));
+static int glob_testdir PARAMS((char *, int));
+static char **glob_dir_to_array PARAMS((char *, char **, int));
+
+/* Make sure these names continue to agree with what's in smatch.c */
+extern char *glob_patscan PARAMS((char *, char *, int));
+extern wchar_t *glob_patscan_wc PARAMS((wchar_t *, wchar_t *, int));
+
+/* And this from gmisc.c/gm_loop.c */
+extern int wextglob_pattern_p PARAMS((wchar_t *));
+
+extern char *glob_dirscan PARAMS((char *, int));
+
+/* Compile `glob_loop.c' for single-byte characters. */
+#define GCHAR unsigned char
+#define CHAR 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 GCHAR wchar_t
+#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 || mbsmbchar (pattern) == 0)
+ 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 ((unsigned char *)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, wild, nullpat;
+
+ negate = *pat == '!';
+ wild = *pat == '*' || *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 = ')';
+ if (wild && pe[1]) /* if we can match zero instances, check further */
+ return (skipname (pe+1, dname, flags));
+ return r;
+ }
+
+ /* Is the extglob pattern between the parens the null pattern? The null
+ pattern can match nothing, so should we check any remaining portion of
+ the pattern? */
+ nullpat = pe >= (pat + 2) && pe[-2] == '(' && pe[-1] == ')';
+
+ /* check every subpattern */
+ while (t = glob_patscan (pp, pe, '|'))
+ {
+ n = t[-1]; /* ( */
+ if (extglob_pattern_p (pp) && n == ')')
+ t[-1] = n; /* no-op for now */
+ else
+ 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 string */
+ if (pp == se)
+ return r;
+
+ /* but if it doesn't then we didn't match a leading dot */
+ if (wild && *pe) /* if we can match zero instances, check further */
+ return (skipname (pe, dname, flags));
+ return 1;
+}
+#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 EXTENDED_GLOB
+ if (extglob_pattern_p (pat)) /* XXX */
+ return (extglob_skipname (pat, dname, flags));
+#endif
+
+ if (glob_always_skip_dot_and_dotdot && DOT_OR_DOTDOT (dname))
+ return 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[0] != '.' &&
+ (pat[0] != '\\' || pat[1] != '.') &&
+ DOT_OR_DOTDOT (dname))
+ return 1;
+
+ /* 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;
+
+ return 0;
+}
+
+#if HANDLE_MULTIBYTE
+
+static int
+wskipname (pat, dname, flags)
+ wchar_t *pat, *dname;
+ int flags;
+{
+ if (glob_always_skip_dot_and_dotdot && WDOT_OR_DOTDOT (dname))
+ return 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[0] != L'.' &&
+ (pat[0] != L'\\' || pat[1] != L'.') &&
+ WDOT_OR_DOTDOT (dname))
+ 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 && dname[0] == L'.' &&
+ pat[0] != L'.' &&
+ (pat[0] != L'\\' || pat[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, wild, nullpat;
+
+ negate = *pat == L'!';
+ wild = *pat == L'*' || *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 = wskipname (pp, dname, flags); /*(*/
+ *pe = L')';
+ if (wild && pe[1] != L'\0')
+ return (wskipname (pe+1, dname, flags));
+ return r;
+ }
+
+ /* Is the extglob pattern between the parens the null pattern? The null
+ pattern can match nothing, so should we check any remaining portion of
+ the pattern? */
+ nullpat = pe >= (pat + 2) && pe[-2] == L'(' && pe[-1] == L')';
+
+ /* check every subpattern */
+ while (t = glob_patscan_wc (pp, pe, '|'))
+ {
+ n = t[-1]; /* ( */
+ if (wextglob_pattern_p (pp) && n == L')')
+ t[-1] = n; /* no-op for now */
+ else
+ t[-1] = L'\0';
+ r = wskipname (pp, dname, flags);
+ 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 */
+ if (wild && *pe != L'\0')
+ return (wskipname (pe, dname, flags));
+ return 1;
+#else
+ return (wskipname (pat, dname, flags));
+#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, flags)
+ char *pat, *dname;
+ int flags;
+{
+ int ret, ext;
+ wchar_t *pat_wc, *dn_wc;
+ size_t pat_n, dn_n;
+
+ if (mbsmbchar (dname) == 0 && mbsmbchar (pat) == 0)
+ return (skipname (pat, dname, flags));
+
+ ext = 0;
+#if EXTENDED_GLOB
+ ext = extglob_pattern_p (pat);
+#endif
+
+ pat_wc = dn_wc = (wchar_t *)NULL;
+
+ 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)
+ ret = ext ? wextglob_skipname (pat_wc, dn_wc, flags) : wskipname (pat_wc, dn_wc, flags);
+ else
+ ret = skipname (pat, dname, flags);
+
+ FREE (pat_wc);
+ FREE (dn_wc);
+
+ return ret;
+}
+#endif /* HANDLE_MULTIBYTE */
+
+/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
+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. */
+void
+wcdequote_pathname (wpathname)
+ wchar_t *wpathname;
+{
+ int i, j;
+
+ 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';
+}
+
+static void
+wdequote_pathname (pathname)
+ char *pathname;
+{
+ mbstate_t ps;
+ size_t len, n;
+ wchar_t *wpathname;
+ int i, j;
+ wchar_t *orig_wpathname;
+
+ if (mbsmbchar (pathname) == 0)
+ {
+ udequote_pathname (pathname);
+ return;
+ }
+
+ len = strlen (pathname);
+ /* Convert the strings into wide characters. */
+ n = xdupmbstowcs (&wpathname, NULL, pathname);
+ if (n == (size_t) -1)
+ {
+ /* Something wrong. Fall back to single-byte */
+ udequote_pathname (pathname);
+ return;
+ }
+ orig_wpathname = wpathname;
+
+ wcdequote_pathname (wpathname);
+
+ /* Convert the wide character string into unibyte character set. */
+ memset (&ps, '\0', sizeof(mbstate_t));
+ n = wcsrtombs(pathname, (const wchar_t **)&wpathname, len, &ps);
+ if (n == (size_t)-1 || (wpathname && *wpathname != 0)) /* what? now you tell me? */
+ {
+ wpathname = orig_wpathname;
+ memset (&ps, '\0', sizeof(mbstate_t));
+ n = xwcsrtombs (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, -2 if DIR is a symlink, -1 otherwise. */
+static int
+glob_testdir (dir, flags)
+ char *dir;
+ int flags;
+{
+ struct stat finfo;
+ int r;
+
+/*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 defined (S_ISLNK)
+ if (S_ISLNK (finfo.st_mode))
+ return (-2);
+#endif
+
+ 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 hasglob; /* return value from glob_pattern_p */
+ int nalloca;
+ struct globval *firstmalloc, *tmplink;
+ char *convfn;
+
+ lastlink = 0;
+ count = lose = skip = add_current = 0;
+
+ firstmalloc = 0;
+ nalloca = 0;
+
+ name_vector = NULL;
+
+/*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) < 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 = (pat && *pat) ? strlen (pat) : 0;
+
+ /* If the filename pattern (PAT) does not contain any globbing characters,
+ or contains a pattern with only backslash escapes (hasglob == 2),
+ 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. */
+ hasglob = 0;
+ if (skip == 0 && ((hasglob = glob_pattern_p (pat)) == 0 || hasglob == 2))
+ {
+ int dirlen;
+ struct stat finfo;
+
+ if (glob_testdir (dir, 0) < 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)
+ {
+ FREE (nextname);
+ FREE (npat);
+ 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
+ {
+ free (npat);
+ 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) < 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;
+ }
+ else if (signal_is_pending (SIGINT)) /* XXX - make SIGINT traps responsive */
+ {
+ 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, flags);
+ 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;
+ }
+ }
+
+ /* XXX - should we even add this if it's not a directory? */
+ 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 (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);
+ ++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)
+ {
+ 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 *));
+ 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);
+ }
+
+ /* 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 (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)
+ {
+ int ind;
+ for (ind = 0; ind < i; ind++)
+ free (result[ind]);
+ free (result);
+ return (NULL);
+ }
+
+ strcpy (result[i], dir);
+ if (add_slash)
+ result[i][l] = '/';
+ if (array[i][0])
+ {
+ 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';
+ }
+ }
+ }
+ else
+ result[i][l+add_slash] = '\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, **new_result;
+ unsigned int result_size;
+ char *directory_name, *filename, *dname, *fn;
+ unsigned int directory_len;
+ int free_dirname; /* flag */
+ int dflags, hasglob;
+
+ 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 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 *) malloc (directory_len + 1);
+
+ if (directory_name == 0) /* allocation failed? */
+ {
+ free (result);
+ return (NULL);
+ }
+
+ bcopy (pathname, directory_name, directory_len);
+ directory_name[directory_len] = '\0';
+ ++filename;
+ free_dirname = 1;
+ }
+
+ hasglob = 0;
+ /* If directory_name contains globbing characters, then we
+ have to expand the previous levels. Just recurse.
+ If glob_pattern_p returns != [0,1] we have a pattern that has backslash
+ quotes but no unquoted glob pattern characters. We dequote it below. */
+ if (directory_len > 0 && (hasglob = glob_pattern_p (directory_name)) == 1)
+ {
+ 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 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;
+ }
+
+ /* 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|GX_RECURSE);
+
+ 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);
+ }
+
+ /* 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;
+
+ 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. */
+ dname = directories[i];
+ dflags = flags & ~(GX_MARKDIRS|GX_ALLDIRS|GX_ADDCURDIR);
+ /* last_starstar? */
+ 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 */
+ }
+
+ /* Special handling for symlinks to directories with globstar on */
+ if (all_starstar && (dflags & GX_NULLDIR) == 0)
+ {
+ int dlen;
+
+ /* If we have a directory name that is not null (GX_NULLDIR above)
+ and is a symlink to a directory, we return the symlink if
+ we're not `descending' into it (filename[0] == 0) and return
+ glob_error_return (which causes the code below to skip the
+ name) otherwise. I should fold this into a test that does both
+ checks instead of calling stat twice. */
+ if (glob_testdir (dname, flags|GX_ALLDIRS) == -2 && glob_testdir (dname, 0) == 0)
+ {
+ if (filename[0] != 0)
+ temp_results = (char **)&glob_error_return; /* skip */
+ else
+ {
+ /* Construct array to pass to glob_dir_to_array */
+ temp_results = (char **)malloc (2 * sizeof (char *));
+ if (temp_results == NULL)
+ goto memory_error;
+ temp_results[0] = (char *)malloc (1);
+ if (temp_results[0] == 0)
+ {
+ free (temp_results);
+ goto memory_error;
+ }
+ **temp_results = '\0';
+ temp_results[1] = NULL;
+ dflags |= GX_SYMLINK; /* mostly for debugging */
+ }
+ }
+ else
+ temp_results = glob_vector (filename, dname, dflags);
+ }
+ else
+ 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' || 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 if (dflags & GX_SYMLINK)
+ array = glob_dir_to_array (directories[i], temp_results, flags);
+ else
+ array = glob_dir_to_array (directories[i], temp_results, flags);
+ l = 0;
+ while (array[l] != NULL)
+ ++l;
+
+ new_result = (char **)realloc (result, (result_size + l) * sizeof (char *));
+
+ if (new_result == NULL)
+ {
+ for (l = 0; array[l]; ++l)
+ free (array[l]);
+ free ((char *)array);
+ goto memory_error;
+ }
+ result = new_result;
+
+ 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);
+ else if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
+ free (temp_results); /* expanding ** case above */
+
+ if (shouldbreak)
+ break;
+ }
+ }
+ /* Free the directories. */
+ for (i = 0; directories[i]; i++)
+ free (directories[i]);
+
+ free ((char *) directories);
+
+ return (result);
+ }
+
+only_filename:
+ /* If there is only a directory name, return it. */
+ if (*filename == '\0')
+ {
+ result = (char **) realloc ((char *) result, 2 * sizeof (char *));
+ if (result == NULL)
+ {
+ if (free_dirname)
+ free (directory_name);
+ return (NULL);
+ }
+ /* If we have a directory name with quoted characters, and we are
+ being called recursively to glob the directory portion of a pathname,
+ we need to dequote the directory name before returning it so the
+ caller can read the directory */
+ if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) != 0)
+ {
+ dequote_pathname (directory_name);
+ directory_len = strlen (directory_name);
+ }
+
+ /* We could check whether or not the dequoted directory_name is a
+ directory and return it here, returning the original directory_name
+ if not, but we don't do that. We do return the dequoted directory
+ name if we're not being called recursively and the dequoted name
+ corresponds to an actual directory. For better backwards compatibility,
+ we can return &glob_error_return unconditionally in this case. */
+
+ if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) == 0)
+ {
+ dequote_pathname (directory_name);
+ if (glob_testdir (directory_name, 0) < 0)
+ {
+ if (free_dirname)
+ free (directory_name);
+ return ((char **)&glob_error_return);
+ }
+ }
+
+ /* 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);
+ QUIT; /* XXX - shell */
+ run_pending_traps ();
+ 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;
+ run_pending_traps ();
+
+ 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. */