No specific user configuration
[platform/upstream/bash.git] / lib / glob / glob.c
index 27099f7..ac59e8d 100644 (file)
@@ -1,20 +1,22 @@
 /* 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;
 
@@ -78,10 +102,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 *));
@@ -90,9 +116,15 @@ static void wdequote_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
@@ -118,38 +150,100 @@ glob_pattern_p (pattern)
      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] != '.' &&
@@ -158,7 +252,7 @@ skipname (pat, dname)
          (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;
@@ -167,57 +261,108 @@ skipname (pat, dname)
 }
 
 #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 */
 
@@ -235,10 +380,11 @@ udequote_pathname (pathname)
 
       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
@@ -250,22 +396,19 @@ wdequote_pathname (pathname)
   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]; )
     {
@@ -274,15 +417,19 @@ wdequote_pathname (pathname)
 
       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
@@ -302,20 +449,28 @@ dequote_pathname (pathname)
 #  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)
@@ -324,6 +479,74 @@ 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.
@@ -346,33 +569,38 @@ 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')
     {
-      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)
@@ -388,6 +616,8 @@ glob_vector (pat, dir, flags)
       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
@@ -397,14 +627,18 @@ glob_vector (pat, dir, flags)
       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);
@@ -418,10 +652,18 @@ glob_vector (pat, dir, flags)
            {
              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
            {
@@ -439,7 +681,7 @@ glob_vector (pat, dir, flags)
         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
 
@@ -459,6 +701,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.
@@ -466,12 +710,17 @@ glob_vector (pat, dir, flags)
       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;
@@ -480,24 +729,103 @@ glob_vector (pat, dir, flags)
          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);
@@ -508,6 +836,33 @@ 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)
+       {
+         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 *));
@@ -517,26 +872,56 @@ glob_vector (pat, dir, flags)
   /* 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);
 }
 
@@ -590,7 +975,13 @@ glob_dir_to_array (dir, array, flags)
       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)
@@ -629,8 +1020,10 @@ glob_filename (pathname, flags)
 {
   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;
@@ -639,35 +1032,115 @@ glob_filename (pathname, flags)
 
   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;
@@ -683,17 +1156,42 @@ glob_filename (pathname, flags)
          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)
@@ -706,7 +1204,43 @@ 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' || 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;
@@ -723,7 +1257,13 @@ 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);
+             else if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
+               free (temp_results);    /* expanding ** case above */
+
+             if (shouldbreak)
+               break;
            }
        }
       /* Free the directories.  */
@@ -735,6 +1275,7 @@ glob_filename (pathname, flags)
       return (result);
     }
 
+only_filename:
   /* If there is only a directory name, return it. */
   if (*filename == '\0')
     {
@@ -746,6 +1287,8 @@ glob_filename (pathname, flags)
       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);
     }
@@ -765,14 +1308,42 @@ 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)
-       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
@@ -786,7 +1357,11 @@ glob_filename (pathname, flags)
       free ((char *) result);
     }
 
+  if (free_dirname && directory_name)
+    free (directory_name);
+
   QUIT;
+  run_pending_traps ();
 
   return (NULL);
 }