Smack: add the execute lable to ldconfig
[platform/upstream/glibc.git] / posix / glob.c
index 630d540..5b92776 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2015 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -12,9 +12,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 
 #include <pwd.h>
 
+#if defined HAVE_STDINT_H || defined _LIBC
+# include <stdint.h>
+#elif !defined UINTPTR_MAX
+# define UINTPTR_MAX (~((size_t) 0))
+#endif
+
 #include <errno.h>
 #ifndef __set_errno
 # define __set_errno(val) errno = (val)
 # define GET_LOGIN_NAME_MAX()  (-1)
 #endif
 \f
-static const char *next_brace_sub (const char *begin, int flags) __THROW;
+static const char *next_brace_sub (const char *begin, int flags) __THROWNL;
 
 #endif /* !defined _LIBC || !defined GLOB_ONLY_P */
 
+#ifndef attribute_hidden
+# define attribute_hidden
+#endif
+
 static int glob_in_dir (const char *pattern, const char *directory,
                        int flags, int (*errfunc) (const char *, int),
-                       glob_t *pglob);
+                       glob_t *pglob, size_t alloca_used);
+extern int __glob_pattern_type (const char *pattern, int quote)
+    attribute_hidden;
 
 #if !defined _LIBC || !defined GLOB_ONLY_P
-static int prefix_array (const char *prefix, char **array, size_t n) __THROW;
-static int collated_compare (const void *, const void *) __THROW;
+static int prefix_array (const char *prefix, char **array, size_t n) __THROWNL;
+static int collated_compare (const void *, const void *) __THROWNL;
 
 
 /* Find the end of the sub-pattern in a brace expression.  */
 static const char *
 next_brace_sub (const char *cp, int flags)
 {
-  unsigned int depth = 0;
+  size_t depth = 0;
   while (*cp != '\0')
     if ((flags & GLOB_NOESCAPE) == 0 && *cp == '\\')
       {
@@ -246,10 +257,18 @@ glob (pattern, flags, errfunc, pglob)
      glob_t *pglob;
 {
   const char *filename;
-  const char *dirname;
+  char *dirname = NULL;
   size_t dirlen;
   int status;
   size_t oldcount;
+  int meta;
+  int dirname_modified;
+  int malloc_dirname = 0;
+  glob_t dirs;
+  int retval = 0;
+#ifdef _LIBC
+  size_t alloca_used = 0;
+#endif
 
   if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0)
     {
@@ -257,6 +276,11 @@ glob (pattern, flags, errfunc, pglob)
       return -1;
     }
 
+  /* POSIX requires all slashes to be matched.  This means that with
+     a trailing slash we must match only directories.  */
+  if (pattern[0] && pattern[strlen (pattern) - 1] == '/')
+    flags |= GLOB_ONLYDIR;
+
   if (!(flags & GLOB_DOOFFS))
     /* Have to do this so `globfree' knows where to start freeing.  It
        also makes all the code that uses gl_offs simpler. */
@@ -298,20 +322,26 @@ glob (pattern, flags, errfunc, pglob)
          const char *next;
          const char *rest;
          size_t rest_len;
-#ifdef __GNUC__
-         char onealt[strlen (pattern) - 1];
-#else
-         char *onealt = (char *) malloc (strlen (pattern) - 1);
-         if (onealt == NULL)
+         char *onealt;
+         size_t pattern_len = strlen (pattern) - 1;
+#ifdef _LIBC
+         int alloca_onealt = __libc_use_alloca (alloca_used + pattern_len);
+         if (alloca_onealt)
+           onealt = alloca_account (pattern_len, alloca_used);
+         else
+#endif
            {
-             if (!(flags & GLOB_APPEND))
+             onealt = (char *) malloc (pattern_len);
+             if (onealt == NULL)
                {
-                 pglob->gl_pathc = 0;
-                 pglob->gl_pathv = NULL;
+                 if (!(flags & GLOB_APPEND))
+                   {
+                     pglob->gl_pathc = 0;
+                     pglob->gl_pathv = NULL;
+                   }
+                 return GLOB_NOSPACE;
                }
-             return GLOB_NOSPACE;
            }
-#endif
 
          /* We know the prefix for all sub-patterns.  */
          alt_start = mempcpy (onealt, pattern, begin - pattern);
@@ -322,9 +352,11 @@ glob (pattern, flags, errfunc, pglob)
          if (next == NULL)
            {
              /* It is an illegal expression.  */
-#ifndef __GNUC__
-             free (onealt);
+           illegal_brace:
+#ifdef _LIBC
+             if (__glibc_unlikely (!alloca_onealt))
 #endif
+               free (onealt);
              return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
            }
 
@@ -334,13 +366,8 @@ glob (pattern, flags, errfunc, pglob)
            {
              rest = next_brace_sub (rest + 1, flags);
              if (rest == NULL)
-               {
-                 /* It is an illegal expression.  */
-#ifndef __GNUC__
-                 free (onealt);
-#endif
-                 return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
-               }
+               /* It is an illegal expression.  */
+               goto illegal_brace;
            }
          /* Please note that we now can be sure the brace expression
             is well-formed.  */
@@ -376,9 +403,10 @@ glob (pattern, flags, errfunc, pglob)
              /* If we got an error, return it.  */
              if (result && result != GLOB_NOMATCH)
                {
-#ifndef __GNUC__
-                 free (onealt);
+#ifdef _LIBC
+                 if (__glibc_unlikely (!alloca_onealt))
 #endif
+                   free (onealt);
                  if (!(flags & GLOB_APPEND))
                    {
                      globfree (pglob);
@@ -396,9 +424,10 @@ glob (pattern, flags, errfunc, pglob)
              assert (next != NULL);
            }
 
-#ifndef __GNUC__
-         free (onealt);
+#ifdef _LIBC
+         if (__glibc_unlikely (!alloca_onealt))
 #endif
+           free (onealt);
 
          if (pglob->gl_pathc != firstc)
            /* We found some entries.  */
@@ -408,6 +437,30 @@ glob (pattern, flags, errfunc, pglob)
        }
     }
 
+  if (!(flags & GLOB_APPEND))
+    {
+      pglob->gl_pathc = 0;
+      if (!(flags & GLOB_DOOFFS))
+       pglob->gl_pathv = NULL;
+      else
+       {
+         size_t i;
+
+         if (pglob->gl_offs >= ~((size_t) 0) / sizeof (char *))
+           return GLOB_NOSPACE;
+
+         pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1)
+                                             * sizeof (char *));
+         if (pglob->gl_pathv == NULL)
+           return GLOB_NOSPACE;
+
+         for (i = 0; i <= pglob->gl_offs; ++i)
+           pglob->gl_pathv[i] = NULL;
+       }
+    }
+
+  oldcount = pglob->gl_pathc + pglob->gl_offs;
+
   /* Find the filename.  */
   filename = strrchr (pattern, '/');
 #if defined __MSDOS__ || defined WINDOWS32
@@ -418,13 +471,14 @@ glob (pattern, flags, errfunc, pglob)
   if (filename == NULL)
     filename = strchr (pattern, ':');
 #endif /* __MSDOS__ || WINDOWS32 */
+  dirname_modified = 0;
   if (filename == NULL)
     {
       /* This can mean two things: a simple name or "~name".  The latter
         case is nothing but a notation for a directory.  */
       if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~')
        {
-         dirname = pattern;
+         dirname = (char *) pattern;
          dirlen = strlen (pattern);
 
          /* Set FILENAME to NULL as a special flag.  This is ugly but
@@ -434,19 +488,27 @@ glob (pattern, flags, errfunc, pglob)
        }
       else
        {
+         if (__glibc_unlikely (pattern[0] == '\0'))
+           {
+             dirs.gl_pathv = NULL;
+             goto no_matches;
+           }
+
          filename = pattern;
 #ifdef _AMIGA
-         dirname = "";
+         dirname = (char *) "";
 #else
-         dirname = ".";
+         dirname = (char *) ".";
 #endif
          dirlen = 0;
        }
     }
-  else if (filename == pattern)
+  else if (filename == pattern
+          || (filename == pattern + 1 && pattern[0] == '\\'
+              && (flags & GLOB_NOESCAPE) == 0))
     {
-      /* "/pattern".  */
-      dirname = "/";
+      /* "/pattern" or "\\/pattern".  */
+      dirname = (char *) "/";
       dirlen = 1;
       ++filename;
     }
@@ -472,62 +534,78 @@ glob (pattern, flags, errfunc, pglob)
             from "d:/", since "d:" and "d:/" are not the same.*/
        }
 #endif
-      newp = (char *) __alloca (dirlen + 1);
+#ifdef _LIBC
+      if (__libc_use_alloca (alloca_used + dirlen + 1))
+       newp = alloca_account (dirlen + 1, alloca_used);
+      else
+#endif
+       {
+         newp = malloc (dirlen + 1);
+         if (newp == NULL)
+           return GLOB_NOSPACE;
+         malloc_dirname = 1;
+       }
       *((char *) mempcpy (newp, pattern, dirlen)) = '\0';
       dirname = newp;
       ++filename;
 
       if (filename[0] == '\0'
 #if defined __MSDOS__ || defined WINDOWS32
-          && dirname[dirlen - 1] != ':'
+         && dirname[dirlen - 1] != ':'
          && (dirlen < 3 || dirname[dirlen - 2] != ':'
              || dirname[dirlen - 1] != '/')
 #endif
          && dirlen > 1)
        /* "pattern/".  Expand "pattern", appending slashes.  */
        {
+         int orig_flags = flags;
+         if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\')
+           {
+             /* "pattern\\/".  Remove the final backslash if it hasn't
+                been quoted.  */
+             char *p = (char *) &dirname[dirlen - 1];
+
+             while (p > dirname && p[-1] == '\\') --p;
+             if ((&dirname[dirlen] - p) & 1)
+               {
+                 *(char *) &dirname[--dirlen] = '\0';
+                 flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
+               }
+           }
          int val = glob (dirname, flags | GLOB_MARK, errfunc, pglob);
          if (val == 0)
            pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
                               | (flags & GLOB_MARK));
-         return val;
-       }
-    }
-
-  if (!(flags & GLOB_APPEND))
-    {
-      pglob->gl_pathc = 0;
-      if (!(flags & GLOB_DOOFFS))
-        pglob->gl_pathv = NULL;
-      else
-       {
-         size_t i;
-         pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1)
-                                             * sizeof (char *));
-         if (pglob->gl_pathv == NULL)
-           return GLOB_NOSPACE;
-
-         for (i = 0; i <= pglob->gl_offs; ++i)
-           pglob->gl_pathv[i] = NULL;
+         else if (val == GLOB_NOMATCH && flags != orig_flags)
+           {
+             /* Make sure globfree (&dirs); is a nop.  */
+             dirs.gl_pathv = NULL;
+             flags = orig_flags;
+             oldcount = pglob->gl_pathc + pglob->gl_offs;
+             goto no_matches;
+           }
+         retval = val;
+         goto out;
        }
     }
 
-  oldcount = pglob->gl_pathc + pglob->gl_offs;
-
 #ifndef VMS
   if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~')
     {
-      if (dirname[1] == '\0' || dirname[1] == '/')
+      if (dirname[1] == '\0' || dirname[1] == '/'
+         || (!(flags & GLOB_NOESCAPE) && dirname[1] == '\\'
+             && (dirname[2] == '\0' || dirname[2] == '/')))
        {
          /* Look up home directory.  */
-         const char *home_dir = getenv ("HOME");
+         char *home_dir = getenv ("HOME");
+         int malloc_home_dir = 0;
 # ifdef _AMIGA
          if (home_dir == NULL || home_dir[0] == '\0')
            home_dir = "SYS:";
 # else
 #  ifdef WINDOWS32
          if (home_dir == NULL || home_dir[0] == '\0')
-            home_dir = "c:/users/default"; /* poor default */
+           home_dir = "c:/users/default"; /* poor default */
 #  else
          if (home_dir == NULL || home_dir[0] == '\0')
            {
@@ -539,7 +617,7 @@ glob (pattern, flags, errfunc, pglob)
                /* `sysconf' does not support _SC_LOGIN_NAME_MAX.  Try
                   a moderate value.  */
                buflen = 20;
-             name = (char *) __alloca (buflen);
+             name = alloca_account (buflen, alloca_used);
 
              success = getlogin_r (name, buflen) == 0;
              if (success)
@@ -549,6 +627,7 @@ glob (pattern, flags, errfunc, pglob)
                  long int pwbuflen = GETPW_R_SIZE_MAX ();
                  char *pwtmpbuf;
                  struct passwd pwbuf;
+                 int malloc_pwtmpbuf = 0;
                  int save = errno;
 
 #    ifndef _LIBC
@@ -557,7 +636,18 @@ glob (pattern, flags, errfunc, pglob)
                       Try a moderate value.  */
                    pwbuflen = 1024;
 #    endif
-                 pwtmpbuf = (char *) __alloca (pwbuflen);
+                 if (__libc_use_alloca (alloca_used + pwbuflen))
+                   pwtmpbuf = alloca_account (pwbuflen, alloca_used);
+                 else
+                   {
+                     pwtmpbuf = malloc (pwbuflen);
+                     if (pwtmpbuf == NULL)
+                       {
+                         retval = GLOB_NOSPACE;
+                         goto out;
+                       }
+                     malloc_pwtmpbuf = 1;
+                   }
 
                  while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p)
                         != 0)
@@ -567,59 +657,181 @@ glob (pattern, flags, errfunc, pglob)
                          p = NULL;
                          break;
                        }
-#    ifdef _LIBC
-                     pwtmpbuf = extend_alloca (pwtmpbuf, pwbuflen,
+
+                     if (!malloc_pwtmpbuf
+                         && __libc_use_alloca (alloca_used
+                                               + 2 * pwbuflen))
+                       pwtmpbuf = extend_alloca_account (pwtmpbuf, pwbuflen,
+                                                         2 * pwbuflen,
+                                                         alloca_used);
+                     else
+                       {
+                         char *newp = realloc (malloc_pwtmpbuf
+                                               ? pwtmpbuf : NULL,
                                                2 * pwbuflen);
-#    else
-                     pwbuflen *= 2;
-                     pwtmpbuf = (char *) __alloca (pwbuflen);
-#    endif
+                         if (newp == NULL)
+                           {
+                             if (__glibc_unlikely (malloc_pwtmpbuf))
+                               free (pwtmpbuf);
+                             retval = GLOB_NOSPACE;
+                             goto out;
+                           }
+                         pwtmpbuf = newp;
+                         pwbuflen = 2 * pwbuflen;
+                         malloc_pwtmpbuf = 1;
+                       }
                      __set_errno (save);
                    }
 #   else
                  p = getpwnam (name);
 #   endif
                  if (p != NULL)
-                   home_dir = p->pw_dir;
+                   {
+                     if (!malloc_pwtmpbuf)
+                       home_dir = p->pw_dir;
+                     else
+                       {
+                         size_t home_dir_len = strlen (p->pw_dir) + 1;
+                         if (__libc_use_alloca (alloca_used + home_dir_len))
+                           home_dir = alloca_account (home_dir_len,
+                                                      alloca_used);
+                         else
+                           {
+                             home_dir = malloc (home_dir_len);
+                             if (home_dir == NULL)
+                               {
+                                 free (pwtmpbuf);
+                                 retval = GLOB_NOSPACE;
+                                 goto out;
+                               }
+                             malloc_home_dir = 1;
+                           }
+                         memcpy (home_dir, p->pw_dir, home_dir_len);
+
+                         free (pwtmpbuf);
+                       }
+                   }
                }
            }
          if (home_dir == NULL || home_dir[0] == '\0')
            {
              if (flags & GLOB_TILDE_CHECK)
-               return GLOB_NOMATCH;
+               {
+                 if (__glibc_unlikely (malloc_home_dir))
+                   free (home_dir);
+                 retval = GLOB_NOMATCH;
+                 goto out;
+               }
              else
-               home_dir = "~"; /* No luck.  */
+               home_dir = (char *) "~"; /* No luck.  */
            }
 #  endif /* WINDOWS32 */
 # endif
          /* Now construct the full directory.  */
          if (dirname[1] == '\0')
-           dirname = home_dir;
+           {
+             if (__glibc_unlikely (malloc_dirname))
+               free (dirname);
+
+             dirname = home_dir;
+             dirlen = strlen (dirname);
+             malloc_dirname = malloc_home_dir;
+           }
          else
            {
              char *newp;
              size_t home_len = strlen (home_dir);
-             newp = (char *) __alloca (home_len + dirlen);
+             int use_alloca = __libc_use_alloca (alloca_used
+                                                 + home_len + dirlen);
+             if (use_alloca)
+               newp = alloca_account (home_len + dirlen, alloca_used);
+             else
+               {
+                 newp = malloc (home_len + dirlen);
+                 if (newp == NULL)
+                   {
+                     if (__glibc_unlikely (malloc_home_dir))
+                       free (home_dir);
+                     retval = GLOB_NOSPACE;
+                     goto out;
+                   }
+               }
+
              mempcpy (mempcpy (newp, home_dir, home_len),
                       &dirname[1], dirlen);
+
+             if (__glibc_unlikely (malloc_dirname))
+               free (dirname);
+
              dirname = newp;
+             dirlen += home_len - 1;
+             malloc_dirname = !use_alloca;
            }
+         dirname_modified = 1;
        }
 # if !defined _AMIGA && !defined WINDOWS32
       else
        {
          char *end_name = strchr (dirname, '/');
-         const char *user_name;
-         const char *home_dir;
+         char *user_name;
+         int malloc_user_name = 0;
+         char *unescape = NULL;
 
+         if (!(flags & GLOB_NOESCAPE))
+           {
+             if (end_name == NULL)
+               {
+                 unescape = strchr (dirname, '\\');
+                 if (unescape)
+                   end_name = strchr (unescape, '\0');
+               }
+             else
+               unescape = memchr (dirname, '\\', end_name - dirname);
+           }
          if (end_name == NULL)
            user_name = dirname + 1;
          else
            {
              char *newp;
-             newp = (char *) __alloca (end_name - dirname);
-             *((char *) mempcpy (newp, dirname + 1, end_name - dirname))
-               = '\0';
+             if (__libc_use_alloca (alloca_used + (end_name - dirname)))
+               newp = alloca_account (end_name - dirname, alloca_used);
+             else
+               {
+                 newp = malloc (end_name - dirname);
+                 if (newp == NULL)
+                   {
+                     retval = GLOB_NOSPACE;
+                     goto out;
+                   }
+                 malloc_user_name = 1;
+               }
+             if (unescape != NULL)
+               {
+                 char *p = mempcpy (newp, dirname + 1,
+                                    unescape - dirname - 1);
+                 char *q = unescape;
+                 while (*q != '\0')
+                   {
+                     if (*q == '\\')
+                       {
+                         if (q[1] == '\0')
+                           {
+                             /* "~fo\\o\\" unescape to user_name "foo\\",
+                                but "~fo\\o\\/" unescape to user_name
+                                "foo".  */
+                             if (filename == NULL)
+                               *p++ = '\\';
+                             break;
+                           }
+                         ++q;
+                       }
+                     *p++ = *q++;
+                   }
+                 *p = '\0';
+               }
+             else
+               *((char *) mempcpy (newp, dirname + 1, end_name - dirname))
+                 = '\0';
              user_name = newp;
            }
 
@@ -629,6 +841,7 @@ glob (pattern, flags, errfunc, pglob)
 #  if defined HAVE_GETPWNAM_R || defined _LIBC
            long int buflen = GETPW_R_SIZE_MAX ();
            char *pwtmpbuf;
+           int malloc_pwtmpbuf = 0;
            struct passwd pwbuf;
            int save = errno;
 
@@ -638,7 +851,21 @@ glob (pattern, flags, errfunc, pglob)
                 moderate value.  */
              buflen = 1024;
 #   endif
-           pwtmpbuf = (char *) __alloca (buflen);
+           if (__libc_use_alloca (alloca_used + buflen))
+             pwtmpbuf = alloca_account (buflen, alloca_used);
+           else
+             {
+               pwtmpbuf = malloc (buflen);
+               if (pwtmpbuf == NULL)
+                 {
+                 nomem_getpw:
+                   if (__glibc_unlikely (malloc_user_name))
+                     free (user_name);
+                   retval = GLOB_NOSPACE;
+                   goto out;
+                 }
+               malloc_pwtmpbuf = 1;
+             }
 
            while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0)
              {
@@ -647,38 +874,77 @@ glob (pattern, flags, errfunc, pglob)
                    p = NULL;
                    break;
                  }
-#   ifdef _LIBC
-               pwtmpbuf = extend_alloca (pwtmpbuf, buflen, 2 * buflen);
-#   else
-               buflen *= 2;
-               pwtmpbuf = __alloca (buflen);
-#   endif
+               if (!malloc_pwtmpbuf
+                   && __libc_use_alloca (alloca_used + 2 * buflen))
+                 pwtmpbuf = extend_alloca_account (pwtmpbuf, buflen,
+                                                   2 * buflen, alloca_used);
+               else
+                 {
+                   char *newp = realloc (malloc_pwtmpbuf ? pwtmpbuf : NULL,
+                                         2 * buflen);
+                   if (newp == NULL)
+                     {
+                       if (__glibc_unlikely (malloc_pwtmpbuf))
+                         free (pwtmpbuf);
+                       goto nomem_getpw;
+                     }
+                   pwtmpbuf = newp;
+                   malloc_pwtmpbuf = 1;
+                 }
                __set_errno (save);
              }
 #  else
            p = getpwnam (user_name);
 #  endif
+
+           if (__glibc_unlikely (malloc_user_name))
+             free (user_name);
+
+           /* If we found a home directory use this.  */
            if (p != NULL)
-             home_dir = p->pw_dir;
+             {
+               size_t home_len = strlen (p->pw_dir);
+               size_t rest_len = end_name == NULL ? 0 : strlen (end_name);
+
+               if (__glibc_unlikely (malloc_dirname))
+                 free (dirname);
+               malloc_dirname = 0;
+
+               if (__libc_use_alloca (alloca_used + home_len + rest_len + 1))
+                 dirname = alloca_account (home_len + rest_len + 1,
+                                           alloca_used);
+               else
+                 {
+                   dirname = malloc (home_len + rest_len + 1);
+                   if (dirname == NULL)
+                     {
+                       if (__glibc_unlikely (malloc_pwtmpbuf))
+                         free (pwtmpbuf);
+                       retval = GLOB_NOSPACE;
+                       goto out;
+                     }
+                   malloc_dirname = 1;
+                 }
+               *((char *) mempcpy (mempcpy (dirname, p->pw_dir, home_len),
+                                   end_name, rest_len)) = '\0';
+
+               dirlen = home_len + rest_len;
+               dirname_modified = 1;
+
+               if (__glibc_unlikely (malloc_pwtmpbuf))
+                 free (pwtmpbuf);
+             }
            else
-             home_dir = NULL;
+             {
+               if (__glibc_unlikely (malloc_pwtmpbuf))
+                 free (pwtmpbuf);
+
+               if (flags & GLOB_TILDE_CHECK)
+                 /* We have to regard it as an error if we cannot find the
+                    home directory.  */
+                 return GLOB_NOMATCH;
+             }
          }
-         /* If we found a home directory use this.  */
-         if (home_dir != NULL)
-           {
-             char *newp;
-             size_t home_len = strlen (home_dir);
-             size_t rest_len = end_name == NULL ? 0 : strlen (end_name);
-             newp = (char *) __alloca (home_len + rest_len + 1);
-             *((char *) mempcpy (mempcpy (newp, home_dir, home_len),
-                                 end_name, rest_len)) = '\0';
-             dirname = newp;
-           }
-         else
-           if (flags & GLOB_TILDE_CHECK)
-             /* We have to regard it as an error if we cannot find the
-                home directory.  */
-             return GLOB_NOMATCH;
        }
 # endif        /* Not Amiga && not WINDOWS32.  */
     }
@@ -693,18 +959,16 @@ glob (pattern, flags, errfunc, pglob)
 
       /* Return the directory if we don't check for error or if it exists.  */
       if ((flags & GLOB_NOCHECK)
-         || (((flags & GLOB_ALTDIRFUNC)
+         || (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
               ? ((*pglob->gl_stat) (dirname, &st) == 0
                  && S_ISDIR (st.st_mode))
               : (__stat64 (dirname, &st64) == 0 && S_ISDIR (st64.st_mode)))))
        {
-         int newcount = pglob->gl_pathc + pglob->gl_offs;
+         size_t newcount = pglob->gl_pathc + pglob->gl_offs;
          char **new_gl_pathv;
 
-         new_gl_pathv
-           = (char **) realloc (pglob->gl_pathv,
-                                (newcount + 1 + 1) * sizeof (char *));
-         if (new_gl_pathv == NULL)
+         if (newcount > UINTPTR_MAX - (1 + 1)
+             || newcount + 1 + 1 > ~((size_t) 0) / sizeof (char *))
            {
            nospace:
              free (pglob->gl_pathv);
@@ -712,11 +976,30 @@ glob (pattern, flags, errfunc, pglob)
              pglob->gl_pathc = 0;
              return GLOB_NOSPACE;
            }
-         pglob->gl_pathv = new_gl_pathv;
 
-          pglob->gl_pathv[newcount] = strdup (dirname);
-         if (pglob->gl_pathv[newcount] == NULL)
+         new_gl_pathv
+           = (char **) realloc (pglob->gl_pathv,
+                                (newcount + 1 + 1) * sizeof (char *));
+         if (new_gl_pathv == NULL)
            goto nospace;
+         pglob->gl_pathv = new_gl_pathv;
+
+         if (flags & GLOB_MARK)
+           {
+             char *p;
+             pglob->gl_pathv[newcount] = malloc (dirlen + 2);
+             if (pglob->gl_pathv[newcount] == NULL)
+               goto nospace;
+             p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen);
+             p[0] = '/';
+             p[1] = '\0';
+           }
+         else
+           {
+             pglob->gl_pathv[newcount] = strdup (dirname);
+             if (pglob->gl_pathv[newcount] == NULL)
+               goto nospace;
+           }
          pglob->gl_pathv[++newcount] = NULL;
          ++pglob->gl_pathc;
          pglob->gl_flags = flags;
@@ -728,15 +1011,31 @@ glob (pattern, flags, errfunc, pglob)
       return GLOB_NOMATCH;
     }
 
-  if (__glob_pattern_p (dirname, !(flags & GLOB_NOESCAPE)))
+  meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE));
+  /* meta is 1 if correct glob pattern containing metacharacters.
+     If meta has bit (1 << 2) set, it means there was an unterminated
+     [ which we handle the same, using fnmatch.  Broken unterminated
+     pattern bracket expressions ought to be rare enough that it is
+     not worth special casing them, fnmatch will do the right thing.  */
+  if (meta & 5)
     {
       /* The directory name contains metacharacters, so we
         have to glob for the directory, and then glob for
         the pattern in each directory found.  */
-      glob_t dirs;
       size_t i;
 
-      if ((flags & GLOB_ALTDIRFUNC) != 0)
+      if (!(flags & GLOB_NOESCAPE) && dirlen > 0 && dirname[dirlen - 1] == '\\')
+       {
+         /* "foo\\/bar".  Remove the final backslash from dirname
+            if it has not been quoted.  */
+         char *p = (char *) &dirname[dirlen - 1];
+
+         while (p > dirname && p[-1] == '\\') --p;
+         if ((&dirname[dirlen] - p) & 1)
+           *(char *) &dirname[--dirlen] = '\0';
+       }
+
+      if (__glibc_unlikely ((flags & GLOB_ALTDIRFUNC) != 0))
        {
          /* Use the alternative access functions also in the recursive
             call.  */
@@ -748,19 +1047,23 @@ glob (pattern, flags, errfunc, pglob)
        }
 
       status = glob (dirname,
-                    ((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE
+                    ((flags & (GLOB_ERR | GLOB_NOESCAPE
                                | GLOB_ALTDIRFUNC))
                      | GLOB_NOSORT | GLOB_ONLYDIR),
                     errfunc, &dirs);
       if (status != 0)
-       return status;
+       {
+         if ((flags & GLOB_NOCHECK) == 0 || status != GLOB_NOMATCH)
+           return status;
+         goto no_matches;
+       }
 
       /* We have successfully globbed the preceding directory name.
         For each name we found, call glob_in_dir on it and FILENAME,
         appending the results to PGLOB.  */
       for (i = 0; i < dirs.gl_pathc; ++i)
        {
-         int old_pathc;
+         size_t old_pathc;
 
 #ifdef SHELL
          {
@@ -779,7 +1082,7 @@ glob (pattern, flags, errfunc, pglob)
          status = glob_in_dir (filename, dirs.gl_pathv[i],
                                ((flags | GLOB_APPEND)
                                 & ~(GLOB_NOCHECK | GLOB_NOMAGIC)),
-                               errfunc, pglob);
+                               errfunc, pglob, alloca_used);
          if (status == GLOB_NOMATCH)
            /* No matches in this directory.  Try the next.  */
            continue;
@@ -811,20 +1114,26 @@ glob (pattern, flags, errfunc, pglob)
         flag was set we must return the input pattern itself.  */
       if (pglob->gl_pathc + pglob->gl_offs == oldcount)
        {
+       no_matches:
          /* No matches.  */
          if (flags & GLOB_NOCHECK)
            {
-             int newcount = pglob->gl_pathc + pglob->gl_offs;
+             size_t newcount = pglob->gl_pathc + pglob->gl_offs;
              char **new_gl_pathv;
 
-             new_gl_pathv = (char **) realloc (pglob->gl_pathv,
-                                               (newcount + 2)
-                                               * sizeof (char *));
-             if (new_gl_pathv == NULL)
+             if (newcount > UINTPTR_MAX - 2
+                 || newcount + 2 > ~((size_t) 0) / sizeof (char *))
                {
+               nospace2:
                  globfree (&dirs);
                  return GLOB_NOSPACE;
                }
+
+             new_gl_pathv = (char **) realloc (pglob->gl_pathv,
+                                               (newcount + 2)
+                                               * sizeof (char *));
+             if (new_gl_pathv == NULL)
+               goto nospace2;
              pglob->gl_pathv = new_gl_pathv;
 
              pglob->gl_pathv[newcount] = __strdup (pattern);
@@ -853,11 +1162,46 @@ glob (pattern, flags, errfunc, pglob)
     }
   else
     {
-      int old_pathc = pglob->gl_pathc;
+      size_t old_pathc = pglob->gl_pathc;
+      int orig_flags = flags;
 
-      status = glob_in_dir (filename, dirname, flags, errfunc, pglob);
+      if (meta & 2)
+       {
+         char *p = strchr (dirname, '\\'), *q;
+         /* We need to unescape the dirname string.  It is certainly
+            allocated by alloca, as otherwise filename would be NULL
+            or dirname wouldn't contain backslashes.  */
+         q = p;
+         do
+           {
+             if (*p == '\\')
+               {
+                 *q = *++p;
+                 --dirlen;
+               }
+             else
+               *q = *p;
+             ++q;
+           }
+         while (*p++ != '\0');
+         dirname_modified = 1;
+       }
+      if (dirname_modified)
+       flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
+      status = glob_in_dir (filename, dirname, flags, errfunc, pglob,
+                           alloca_used);
       if (status != 0)
-       return status;
+       {
+         if (status == GLOB_NOMATCH && flags != orig_flags
+             && pglob->gl_pathc + pglob->gl_offs == oldcount)
+           {
+             /* Make sure globfree (&dirs); is a nop.  */
+             dirs.gl_pathv = NULL;
+             flags = orig_flags;
+             goto no_matches;
+           }
+         return status;
+       }
 
       if (dirlen > 0)
        {
@@ -881,15 +1225,15 @@ glob (pattern, flags, errfunc, pglob)
       struct_stat64 st64;
 
       for (i = oldcount; i < pglob->gl_pathc + pglob->gl_offs; ++i)
-       if (((flags & GLOB_ALTDIRFUNC)
+       if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
             ? ((*pglob->gl_stat) (pglob->gl_pathv[i], &st) == 0
                && S_ISDIR (st.st_mode))
             : (__stat64 (pglob->gl_pathv[i], &st64) == 0
                && S_ISDIR (st64.st_mode))))
          {
-           size_t len = strlen (pglob->gl_pathv[i]) + 2;
+           size_t len = strlen (pglob->gl_pathv[i]) + 2;
            char *new = realloc (pglob->gl_pathv[i], len);
-           if (new == NULL)
+           if (new == NULL)
              {
                globfree (pglob);
                pglob->gl_pathc = 0;
@@ -908,7 +1252,11 @@ glob (pattern, flags, errfunc, pglob)
             sizeof (char *), collated_compare);
     }
 
-  return 0;
+ out:
+  if (__glibc_unlikely (malloc_dirname))
+    free (dirname);
+
+  return retval;
 }
 #if defined _LIBC && !defined glob
 libc_hidden_def (glob)
@@ -920,14 +1268,13 @@ libc_hidden_def (glob)
 /* Free storage allocated in PGLOB by a previous `glob' call.  */
 void
 globfree (pglob)
-     register glob_t *pglob;
+     glob_t *pglob;
 {
   if (pglob->gl_pathv != NULL)
     {
       size_t i;
       for (i = 0; i < pglob->gl_pathc; ++i)
-       if (pglob->gl_pathv[pglob->gl_offs + i] != NULL)
-         free (pglob->gl_pathv[pglob->gl_offs + i]);
+       free (pglob->gl_pathv[pglob->gl_offs + i]);
       free (pglob->gl_pathv);
       pglob->gl_pathv = NULL;
     }
@@ -961,7 +1308,7 @@ collated_compare (const void *a, const void *b)
 static int
 prefix_array (const char *dirname, char **array, size_t n)
 {
-  register size_t i;
+  size_t i;
   size_t dirlen = strlen (dirname);
 #if defined __MSDOS__ || defined WINDOWS32
   int sep_char = '/';
@@ -1015,15 +1362,13 @@ prefix_array (const char *dirname, char **array, size_t n)
 
 /* We must not compile this function twice.  */
 #if !defined _LIBC || !defined NO_GLOB_PATTERN_P
-/* Return nonzero if PATTERN contains any metacharacters.
-   Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
 int
-__glob_pattern_p (pattern, quote)
+__glob_pattern_type (pattern, quote)
      const char *pattern;
      int quote;
 {
-  register const char *p;
-  int open = 0;
+  const char *p;
+  int ret = 0;
 
   for (p = pattern; *p != '\0'; ++p)
     switch (*p)
@@ -1033,21 +1378,35 @@ __glob_pattern_p (pattern, quote)
        return 1;
 
       case '\\':
-       if (quote && p[1] != '\0')
-         ++p;
+       if (quote)
+         {
+           if (p[1] != '\0')
+             ++p;
+           ret |= 2;
+         }
        break;
 
       case '[':
-       open = 1;
+       ret |= 4;
        break;
 
       case ']':
-       if (open)
+       if (ret & 4)
          return 1;
        break;
       }
 
-  return 0;
+  return ret;
+}
+
+/* Return nonzero if PATTERN contains any metacharacters.
+   Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
+int
+__glob_pattern_p (pattern, quote)
+     const char *pattern;
+     int quote;
+{
+  return __glob_pattern_type (pattern, quote) == 1;
 }
 # ifdef _LIBC
 weak_alias (__glob_pattern_p, glob_pattern_p)
@@ -1061,21 +1420,42 @@ weak_alias (__glob_pattern_p, glob_pattern_p)
    allocated with alloca to be recycled.  */
 #if !defined _LIBC || !defined GLOB_ONLY_P
 static int
-link_exists_p (const char *dir, size_t dirlen, const char *fname,
-              glob_t *pglob, int flags)
+__attribute_noinline__
+link_exists2_p (const char *dir, size_t dirlen, const char *fname,
+              glob_t *pglob
+# ifndef _LIBC
+               , int flags
+# endif
+               )
 {
   size_t fnamelen = strlen (fname);
   char *fullname = (char *) __alloca (dirlen + 1 + fnamelen + 1);
   struct stat st;
+# ifndef _LIBC
   struct_stat64 st64;
+# endif
 
   mempcpy (mempcpy (mempcpy (fullname, dir, dirlen), "/", 1),
           fname, fnamelen + 1);
 
-  return (((flags & GLOB_ALTDIRFUNC)
+# ifdef _LIBC
+  return (*pglob->gl_stat) (fullname, &st) == 0;
+# else
+  return ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
           ? (*pglob->gl_stat) (fullname, &st)
           : __stat64 (fullname, &st64)) == 0);
+# endif
 }
+# ifdef _LIBC
+#  define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \
+  (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)                             \
+   ? link_exists2_p (dirname, dirnamelen, fname, pglob)                              \
+   : ({ struct stat64 st64;                                                  \
+       __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0) == 0; }))
+# else
+#  define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \
+  link_exists2_p (dirname, dirnamelen, fname, pglob, flags)
+# endif
 #endif
 
 
@@ -1086,7 +1466,7 @@ link_exists_p (const char *dir, size_t dirlen, const char *fname,
 static int
 glob_in_dir (const char *pattern, const char *directory, int flags,
             int (*errfunc) (const char *, int),
-            glob_t *pglob)
+            glob_t *pglob, size_t alloca_used)
 {
   size_t dirlen = strlen (directory);
   void *stream = NULL;
@@ -1101,15 +1481,16 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
   struct globnames *names = &init_names;
   struct globnames *names_alloca = &init_names;
   size_t nfound = 0;
-  size_t allocasize = sizeof (init_names);
   size_t cur = 0;
   int meta;
   int save;
 
+  alloca_used += sizeof (init_names);
+
   init_names.next = NULL;
   init_names.count = INITIAL_COUNT;
 
-  meta = __glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));
+  meta = __glob_pattern_type (pattern, !(flags & GLOB_NOESCAPE));
   if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
     {
       /* We need not do any tests.  The PATTERN contains no meta
@@ -1117,141 +1498,145 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
         result will always contain exactly one name.  */
       flags |= GLOB_NOCHECK;
     }
-  else if (meta == 0 &&
-          ((flags & GLOB_NOESCAPE) || strchr (pattern, '\\') == NULL))
+  else if (meta == 0)
     {
       /* Since we use the normal file functions we can also use stat()
         to verify the file is there.  */
-      struct stat st;
-      struct_stat64 st64;
+      union
+      {
+       struct stat st;
+       struct_stat64 st64;
+      } ust;
       size_t patlen = strlen (pattern);
-      char *fullname = (char *) __alloca (dirlen + 1 + patlen + 1);
+      int alloca_fullname = __libc_use_alloca (alloca_used
+                                              + dirlen + 1 + patlen + 1);
+      char *fullname;
+      if (alloca_fullname)
+       fullname = alloca_account (dirlen + 1 + patlen + 1, alloca_used);
+      else
+       {
+         fullname = malloc (dirlen + 1 + patlen + 1);
+         if (fullname == NULL)
+           return GLOB_NOSPACE;
+       }
 
       mempcpy (mempcpy (mempcpy (fullname, directory, dirlen),
                        "/", 1),
               pattern, patlen + 1);
-      if (((flags & GLOB_ALTDIRFUNC)
-          ? (*pglob->gl_stat) (fullname, &st)
-          : __stat64 (fullname, &st64)) == 0)
+      if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
+          ? (*pglob->gl_stat) (fullname, &ust.st)
+          : __stat64 (fullname, &ust.st64)) == 0)
        /* We found this file to be existing.  Now tell the rest
           of the function to copy this name into the result.  */
        flags |= GLOB_NOCHECK;
+
+      if (__glibc_unlikely (!alloca_fullname))
+       free (fullname);
     }
   else
     {
-      if (pattern[0] == '\0')
+      stream = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
+               ? (*pglob->gl_opendir) (directory)
+               : opendir (directory));
+      if (stream == NULL)
        {
-         /* This is a special case for matching directories like in
-            "*a/".  */
-         names->name[cur] = (char *) malloc (1);
-         if (names->name[cur] == NULL)
-           goto memory_error;
-         *names->name[cur++] = '\0';
-         nfound = 1;
-         meta = 0;
+         if (errno != ENOTDIR
+             && ((errfunc != NULL && (*errfunc) (directory, errno))
+                 || (flags & GLOB_ERR)))
+           return GLOB_ABORTED;
        }
       else
        {
-         stream = ((flags & GLOB_ALTDIRFUNC)
-                   ? (*pglob->gl_opendir) (directory)
-                   : opendir (directory));
-         if (stream == NULL)
-           {
-             if (errno != ENOTDIR
-                 && ((errfunc != NULL && (*errfunc) (directory, errno))
-                     || (flags & GLOB_ERR)))
-               return GLOB_ABORTED;
-             meta = 0;
-           }
-         else
-           {
-             int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
-                              | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
+#ifdef _LIBC
+         int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
+                    ? -1 : dirfd ((DIR *) stream));
+#endif
+         int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
+                          | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
 #if defined _AMIGA || defined VMS
-                              | FNM_CASEFOLD
+                          | FNM_CASEFOLD
 #endif
-                              );
-             flags |= GLOB_MAGCHAR;
+                          );
+         flags |= GLOB_MAGCHAR;
 
-             while (1)
-               {
-                 const char *name;
-                 size_t len;
+         while (1)
+           {
+             const char *name;
+             size_t len;
 #if defined _LIBC && !defined COMPILE_GLOB64
-                 struct dirent64 *d;
-                 union
-                   {
-                     struct dirent64 d64;
-                     char room [offsetof (struct dirent64, d_name[0])
-                                + NAME_MAX + 1];
-                   }
-                 d64buf;
+             struct dirent64 *d;
+             union
+               {
+                 struct dirent64 d64;
+                 char room [offsetof (struct dirent64, d_name[0])
+                            + NAME_MAX + 1];
+               }
+             d64buf;
 
-                 if (flags & GLOB_ALTDIRFUNC)
+             if (__glibc_unlikely (flags & GLOB_ALTDIRFUNC))
+               {
+                 struct dirent *d32 = (*pglob->gl_readdir) (stream);
+                 if (d32 != NULL)
                    {
-                     struct dirent *d32 = (*pglob->gl_readdir) (stream);
-                     if (d32 != NULL)
-                       {
-                         CONVERT_DIRENT_DIRENT64 (&d64buf.d64, d32);
-                         d = &d64buf.d64;
-                       }
-                     else
-                       d = NULL;
+                     CONVERT_DIRENT_DIRENT64 (&d64buf.d64, d32);
+                     d = &d64buf.d64;
                    }
                  else
-                   d = __readdir64 (stream);
+                   d = NULL;
+               }
+             else
+               d = __readdir64 (stream);
 #else
-                 struct dirent *d = ((flags & GLOB_ALTDIRFUNC)
-                                     ? ((struct dirent *)
-                                        (*pglob->gl_readdir) (stream))
-                                     : __readdir (stream));
+             struct dirent *d = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
+                                 ? ((struct dirent *)
+                                    (*pglob->gl_readdir) (stream))
+                                 : __readdir (stream));
 #endif
-                 if (d == NULL)
-                   break;
-                 if (! REAL_DIR_ENTRY (d))
-                   continue;
+             if (d == NULL)
+               break;
+             if (! REAL_DIR_ENTRY (d))
+               continue;
 
-                 /* If we shall match only directories use the information
-                    provided by the dirent call if possible.  */
-                 if ((flags & GLOB_ONLYDIR) && !DIRENT_MIGHT_BE_DIR (d))
-                   continue;
+             /* If we shall match only directories use the information
+                provided by the dirent call if possible.  */
+             if ((flags & GLOB_ONLYDIR) && !DIRENT_MIGHT_BE_DIR (d))
+               continue;
 
-                 name = d->d_name;
+             name = d->d_name;
 
-                 if (fnmatch (pattern, name, fnm_flags) == 0)
+             if (fnmatch (pattern, name, fnm_flags) == 0)
+               {
+                 /* If the file we found is a symlink we have to
+                    make sure the target file exists.  */
+                 if (!DIRENT_MIGHT_BE_SYMLINK (d)
+                     || link_exists_p (dfd, directory, dirlen, name, pglob,
+                                       flags))
                    {
-                     /* If the file we found is a symlink we have to
-                        make sure the target file exists.  */
-                     if (!DIRENT_MIGHT_BE_SYMLINK (d)
-                         || link_exists_p (directory, dirlen, name, pglob,
-                                           flags))
+                     if (cur == names->count)
                        {
-                         if (cur == names->count)
-                           {
-                             struct globnames *newnames;
-                             size_t count = names->count * 2;
-                             size_t size = (sizeof (struct globnames)
-                                            + ((count - INITIAL_COUNT)
-                                               * sizeof (char *)));
-                             allocasize += size;
-                             if (__libc_use_alloca (allocasize))
-                               newnames = names_alloca = __alloca (size);
-                             else if ((newnames = malloc (size))
-                                      == NULL)
-                               goto memory_error;
-                             newnames->count = count;
-                             newnames->next = names;
-                             names = newnames;
-                             cur = 0;
-                           }
-                         len = NAMLEN (d);
-                         names->name[cur] = (char *) malloc (len + 1);
-                         if (names->name[cur] == NULL)
+                         struct globnames *newnames;
+                         size_t count = names->count * 2;
+                         size_t size = (sizeof (struct globnames)
+                                        + ((count - INITIAL_COUNT)
+                                           * sizeof (char *)));
+                         if (__libc_use_alloca (alloca_used + size))
+                           newnames = names_alloca
+                             = alloca_account (size, alloca_used);
+                         else if ((newnames = malloc (size))
+                                  == NULL)
                            goto memory_error;
-                         *((char *) mempcpy (names->name[cur++], name, len))
-                           = '\0';
-                         ++nfound;
+                         newnames->count = count;
+                         newnames->next = names;
+                         names = newnames;
+                         cur = 0;
                        }
+                     len = NAMLEN (d);
+                     names->name[cur] = (char *) malloc (len + 1);
+                     if (names->name[cur] == NULL)
+                       goto memory_error;
+                     *((char *) mempcpy (names->name[cur++], name, len))
+                       = '\0';
+                     ++nfound;
                    }
                }
            }
@@ -1273,6 +1658,13 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
     {
       result = 0;
 
+      if (pglob->gl_pathc > UINTPTR_MAX - pglob->gl_offs
+         || pglob->gl_pathc + pglob->gl_offs > UINTPTR_MAX - nfound
+         || pglob->gl_pathc + pglob->gl_offs + nfound > UINTPTR_MAX - 1
+         || (pglob->gl_pathc + pglob->gl_offs + nfound + 1
+             > UINTPTR_MAX / sizeof (char *)))
+       goto memory_error;
+
       char **new_gl_pathv;
       new_gl_pathv
        = (char **) realloc (pglob->gl_pathv,
@@ -1287,8 +1679,15 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
              for (size_t i = 0; i < cur; ++i)
                free (names->name[i]);
              names = names->next;
+             /* NB: we will not leak memory here if we exit without
+                freeing the current block assigned to OLD.  At least
+                the very first block is always allocated on the stack
+                and this is the block assigned to OLD here.  */
              if (names == NULL)
-               break;
+               {
+                 assert (old == &init_names);
+                 break;
+               }
              cur = names->count;
              if (old == names_alloca)
                names_alloca = names;
@@ -1306,8 +1705,15 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
                new_gl_pathv[pglob->gl_offs + pglob->gl_pathc++]
                  = names->name[i];
              names = names->next;
+             /* NB: we will not leak memory here if we exit without
+                freeing the current block assigned to OLD.  At least
+                the very first block is always allocated on the stack
+                and this is the block assigned to OLD here.  */
              if (names == NULL)
-               break;
+               {
+                 assert (old == &init_names);
+                 break;
+               }
              cur = names->count;
              if (old == names_alloca)
                names_alloca = names;
@@ -1326,7 +1732,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
   if (stream != NULL)
     {
       save = errno;
-      if (flags & GLOB_ALTDIRFUNC)
+      if (__glibc_unlikely (flags & GLOB_ALTDIRFUNC))
        (*pglob->gl_closedir) (stream);
       else
        closedir (stream);