changeset 9351:7ab454b59c21

Merge glibc changes into lib/glob.c. * lib/glob.c (glob_in_dir): Sync with glibc/posix/glob.c, dated 2007-10-15 04:59:03 UTC. Here are the changes: 2007-10-14 Ulrich Drepper <drepper@redhat.com> * lib/glob.c: Reimplement link_exists_p to use fstatat64. * lib/glob.c: Add some branch prediction throughout. 2007-10-07 Ulrich Drepper <drepper@redhat.com> [BZ #5103] * lib/glob.c (glob): Recognize patterns starting \/. 2007-02-14 Jakub Jelinek <jakub@redhat.com> [BZ #3996] * lib/glob.c (attribute_hidden): Define if not defined. (glob): Unescape dirname, filename or username when needed and not GLOB_NOESCAPE. Handle \/ correctly. Handle GLOB_MARK if filename is NULL. Handle unescaped [ in pattern without closing ]. Don't pass GLOB_CHECK down to recursive glob for directories. (__glob_pattern_type): New function. (__glob_pattern_p): Implement using __glob_pattern_type. (glob_in_dir): Handle GLOB_NOCHECK patterns containing no meta characters and backslashes if not GLOB_NOESCAPE or unterminated [. Remove unreachable code. 2006-09-30 Ulrich Drepper <drepper@redhat.com> * lib/glob.c (glob_in_dir): Add some comments and asserts to explain why there are no leaks. 2006-09-25 Jakub Jelinek <jakub@redhat.com> [BZ #3253] * lib/glob.c (glob_in_dir): Don't alloca one struct globlink at a time, rather allocate increasingly bigger arrays of pointers, if possible with alloca, if too large with malloc. 2007-10-16 Paul Eggert <eggert@cs.ucla.edu>
author Paul Eggert <eggert@cs.ucla.edu>
date Tue, 16 Oct 2007 16:39:39 -0700
parents 4e234ce80517
children 970d5cb5b003
files ChangeLog lib/glob.c
diffstat 2 files changed, 482 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,47 @@
+2007-10-16  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Merge glibc changes into lib/glob.c.
+
+	* lib/glob.c (glob_in_dir): Sync with glibc/posix/glob.c, dated
+	2007-10-15 04:59:03 UTC.  Here are the changes:
+
+	2007-10-14  Ulrich Drepper  <drepper@redhat.com>
+
+	* lib/glob.c: Reimplement link_exists_p to use fstatat64.
+
+	* lib/glob.c: Add some branch prediction throughout.
+
+	2007-10-07  Ulrich Drepper  <drepper@redhat.com>
+
+	[BZ #5103]
+	* lib/glob.c (glob): Recognize patterns starting \/.
+
+	2007-02-14  Jakub Jelinek  <jakub@redhat.com>
+
+	[BZ #3996]
+	* lib/glob.c (attribute_hidden): Define if not defined.
+	(glob): Unescape dirname, filename or username when needed and not
+	GLOB_NOESCAPE.  Handle \/ correctly.  Handle GLOB_MARK if filename
+	is NULL.  Handle unescaped [ in pattern without closing ].
+	Don't pass GLOB_CHECK down to recursive glob for directories.
+	(__glob_pattern_type): New function.
+	(__glob_pattern_p): Implement using __glob_pattern_type.
+	(glob_in_dir): Handle GLOB_NOCHECK patterns containing no meta
+	characters and backslashes if not GLOB_NOESCAPE or unterminated [.
+	Remove unreachable code.
+
+	2006-09-30  Ulrich Drepper  <drepper@redhat.com>
+
+	* lib/glob.c (glob_in_dir): Add some comments and asserts to
+	explain why there are no leaks.
+
+	2006-09-25  Jakub Jelinek  <jakub@redhat.com>
+
+	[BZ #3253]
+	* lib/glob.c (glob_in_dir): Don't alloca one struct globlink at a
+	time, rather allocate increasingly bigger arrays of pointers, if
+	possible with alloca, if too large with malloc.
+
 2007-10-16  Paul Eggert  <eggert@cs.ucla.edu>
 
 	Check for 64-bit int errors in HP-UX 10.20 preprocessor.
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -1,4 +1,5 @@
-/* Copyright (C) 1991-2002,2003,2004,2005,2006,2007 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2002, 2003, 2004, 2005, 2006, 2007
+   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
@@ -159,14 +160,45 @@
 
 #endif /* !defined _LIBC || !defined GLOB_ONLY_P */
 
+#ifndef attribute_hidden
+# define attribute_hidden
+#endif
+
+#ifndef __attribute_noinline__
+# if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 1)
+#  define __attribute_noinline__ /* Ignore */
+#else
+#  define __attribute_noinline__ __attribute__ ((__noinline__))
+# endif
+#endif
+
+#if ! defined __builtin_expect && __GNUC__ < 3
+# define __builtin_expect(expr, expected) (expr)
+#endif
+
+#ifndef _LIBC
 /* The results of opendir() in this file are not used with dirfd and fchdir,
    therefore save some unnecessary work in fchdir.c.  */
-#undef opendir
-#undef closedir
+# undef opendir
+# undef closedir
+
+# if HAVE_ALLOCA
+/* The OS usually guarantees only one guard page at the bottom of the stack,
+   and a page size can be as small as 4096 bytes.  So we cannot safely
+   allocate anything larger than 4096 bytes.  Also care for the possibility
+   of a few compiler-allocated temporary stack slots.  */
+#  define __libc_use_alloca(n) ((n) < 4032)
+# else
+/* alloca is implemented with malloc, so just use malloc.  */
+#  define __libc_use_alloca(n) 0
+# endif
+#endif
 
 static int glob_in_dir (const char *pattern, const char *directory,
 			int flags, int (*errfunc) (const char *, int),
 			glob_t *pglob);
+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;
@@ -222,6 +254,9 @@
   size_t dirlen;
   int status;
   size_t oldcount;
+  int meta;
+  int dirname_modified;
+  glob_t dirs;
 
   if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0)
     {
@@ -390,6 +425,7 @@
   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
@@ -415,9 +451,11 @@
 	  dirlen = 0;
 	}
     }
-  else if (filename == pattern)
+  else if (filename == pattern
+	   || (filename == pattern + 1 && pattern[0] == '\\'
+	       && (flags & GLOB_NOESCAPE) == 0))
     {
-      /* "/pattern".  */
+      /* "/pattern" or "\\/pattern".  */
       dirname = "/";
       dirlen = 1;
       ++filename;
@@ -458,10 +496,33 @@
 	  && dirlen > 1)
 	/* "pattern/".  Expand "pattern", appending slashes.  */
 	{
-	  int val = glob (dirname, flags | GLOB_MARK, errfunc, pglob);
+	  int orig_flags = flags;
+	  int val;
+	  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);
+		}
+	    }
+	  val = glob (dirname, flags | GLOB_MARK, errfunc, pglob);
 	  if (val == 0)
 	    pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
 			       | (flags & GLOB_MARK));
+	  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;
+	    }
 	  return val;
 	}
     }
@@ -487,7 +548,9 @@
 
   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");
@@ -564,7 +627,10 @@
 # endif
 	  /* Now construct the full directory.  */
 	  if (dirname[1] == '\0')
-	    dirname = home_dir;
+	    {
+	      dirname = home_dir;
+	      dirlen = strlen (dirname);
+	    }
 	  else
 	    {
 	      char *newp;
@@ -573,7 +639,9 @@
 	      mempcpy (mempcpy (newp, home_dir, home_len),
 		       &dirname[1], dirlen);
 	      dirname = newp;
+	      dirlen += home_len - 1;
 	    }
+	  dirname_modified = 1;
 	}
 # if !defined _AMIGA && !defined WINDOWS32
       else
@@ -581,7 +649,19 @@
 	  char *end_name = strchr (dirname, '/');
 	  const char *user_name;
 	  const char *home_dir;
+	  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
@@ -590,6 +670,33 @@
 	      newp = __alloca (end_name - dirname);
 	      *((char *) mempcpy (newp, dirname + 1, end_name - dirname))
 		= '\0';
+	      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;
 	    }
 
@@ -643,6 +750,8 @@
 	      *((char *) mempcpy (mempcpy (newp, home_dir, home_len),
 				  end_name, rest_len)) = '\0';
 	      dirname = newp;
+	      dirlen = home_len + rest_len;
+	      dirname_modified = 1;
 	    }
 	  else
 	    if (flags & GLOB_TILDE_CHECK)
@@ -662,7 +771,7 @@
 
       /* 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)))))
@@ -682,9 +791,22 @@
 	    }
 	  pglob->gl_pathv = new_gl_pathv;
 
-	   pglob->gl_pathv[newcount] = strdup (dirname);
-	  if (pglob->gl_pathv[newcount] == NULL)
-	    goto nospace;
+	  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;
@@ -696,15 +818,31 @@
       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 (__builtin_expect ((flags & GLOB_ALTDIRFUNC) != 0, 0))
 	{
 	  /* Use the alternative access functions also in the recursive
 	     call.  */
@@ -716,12 +854,16 @@
 	}
 
       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,
@@ -779,6 +921,7 @@
 	 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)
 	    {
@@ -821,10 +964,44 @@
   else
     {
       int old_pathc = pglob->gl_pathc;
+      int orig_flags = flags;
 
+      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);
       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)
 	{
@@ -840,6 +1017,33 @@
 	}
     }
 
+  if (flags & GLOB_MARK)
+    {
+      /* Append slashes to directory names.  */
+      size_t i;
+      struct stat st;
+      struct_stat64 st64;
+
+      for (i = oldcount; i < pglob->gl_pathc + pglob->gl_offs; ++i)
+	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;
+	    char *new = realloc (pglob->gl_pathv[i], len);
+ 	    if (new == NULL)
+	      {
+		globfree (pglob);
+		pglob->gl_pathc = 0;
+		return GLOB_NOSPACE;
+	      }
+	    strcpy (&new[len - 2], "/");
+	    pglob->gl_pathv[i] = new;
+	  }
+    }
+
   if (!(flags & GLOB_NOSORT))
     {
       /* Sort the vector.  */
@@ -955,15 +1159,13 @@
 
 /* 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;
+  int ret = 0;
 
   for (p = pattern; *p != '\0'; ++p)
     switch (*p)
@@ -973,21 +1175,35 @@
 	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)
@@ -1000,22 +1216,43 @@
 /* We put this in a separate function mainly to allow the memory
    allocated with alloca to be recycled.  */
 #if !defined _LIBC || !defined GLOB_ONLY_P
-static bool
-is_dir_p (const char *dir, size_t dirlen, const char *fname,
-	  glob_t *pglob, int flags)
+static int
+__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 = __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)
-	  ? (*pglob->gl_stat) (fullname, &st) == 0 && S_ISDIR (st.st_mode)
-	  : __stat64 (fullname, &st64) == 0 && S_ISDIR (st64.st_mode));
+# 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
 
 
@@ -1030,27 +1267,35 @@
 {
   size_t dirlen = strlen (directory);
   void *stream = NULL;
-  struct globlink
+  struct globnames
     {
-      struct globlink *next;
-      char *name;
+      struct globnames *next;
+      size_t count;
+      char *name[64];
     };
-  struct globlink *names = NULL;
-  size_t nfound;
+#define INITIAL_COUNT sizeof (init_names.name) / sizeof (init_names.name[0])
+  struct globnames init_names;
+  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;
+  int result;
 
-  meta = __glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));
+  init_names.next = NULL;
+  init_names.count = INITIAL_COUNT;
+
+  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
 	 characters and we must not return an error therefore the
 	 result will always contain exactly one name.  */
       flags |= GLOB_NOCHECK;
-      nfound = 0;
     }
-  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.  */
@@ -1062,134 +1307,117 @@
       mempcpy (mempcpy (mempcpy (fullname, directory, dirlen),
 			"/", 1),
 	       pattern, patlen + 1);
-      if (((flags & GLOB_ALTDIRFUNC)
+      if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
 	   ? (*pglob->gl_stat) (fullname, &st)
 	   : __stat64 (fullname, &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;
-
-      nfound = 0;
     }
   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 = __alloca (sizeof (struct globlink));
-	  names->name = malloc (1);
-	  if (names->name == NULL)
-	    goto memory_error;
-	  names->name[0] = '\0';
-	  names->next = NULL;
-	  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;
-	      nfound = 0;
-	      meta = 0;
-	    }
-	  else
-	    {
-	      int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
-			       | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
-#if defined _AMIGA || defined __VMS
-			       | FNM_CASEFOLD
+#ifdef _LIBC
+	  int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
+		     ? -1 : dirfd ((DIR *) stream));
 #endif
-			       );
-	      nfound = 0;
-	      flags |= GLOB_MAGCHAR;
+	  int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
+			   | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
+#if defined _AMIGA || defined VMS
+			   | FNM_CASEFOLD
+#endif
+			   );
+	  flags |= GLOB_MAGCHAR;
 
-	      while (1)
+	  while (1)
+	    {
+	      const char *name;
+	      size_t len;
+#if defined _LIBC && !defined COMPILE_GLOB64
+	      struct dirent64 *d;
+	      union
 		{
-		  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;
+
+	      if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
+		{
+		  struct dirent *d32 = (*pglob->gl_readdir) (stream);
+		  if (d32 != NULL)
 		    {
-		      struct dirent64 d64;
-		      char room [offsetof (struct dirent64, d_name[0])
-				 + NAME_MAX + 1];
-		    }
-		  d64buf;
-
-		  if (flags & GLOB_ALTDIRFUNC)
-		    {
-		      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)
-				      ? ((*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))
 		    {
-		      /* ISDIR will often be incorrectly set to false
-		         when not in GLOB_ONLYDIR || GLOB_MARK mode, but we
-		         don't care.  It won't be used and we save the
-		         expensive call to stat.  */
-		      int need_dir_test =
-			(GLOB_MARK | (DIRENT_MIGHT_BE_SYMLINK (d)
-				      ? GLOB_ONLYDIR : 0));
-		      bool isdir = (DIRENT_MUST_BE (d, DT_DIR)
-				    || ((flags & need_dir_test)
-				        && is_dir_p (directory, dirlen, name,
-						     pglob, flags)));
-
-		      /* In GLOB_ONLYDIR mode, skip non-dirs.  */
-		      if ((flags & GLOB_ONLYDIR) && !isdir)
-			  continue;
-
+		      if (cur == names->count)
 			{
-			  struct globlink *new =
-			    __alloca (sizeof (struct globlink));
-			  char *p;
-			  len = _D_EXACT_NAMLEN (d);
-			  new->name =
-			    malloc (len + 1 + ((flags & GLOB_MARK) && isdir));
-			  if (new->name == NULL)
+			  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;
-			  p = mempcpy (new->name, name, len);
-			  if ((flags & GLOB_MARK) && isdir)
-			      *p++ = '/';
-			  *p = '\0';
-			  new->next = names;
-			  names = new;
-			  ++nfound;
+			  newnames->count = count;
+			  newnames->next = names;
+			  names = newnames;
+			  cur = 0;
 			}
+		      len = _D_EXACT_NAMLEN (d);
+		      names->name[cur] = malloc (len + 1);
+		      if (names->name[cur] == NULL)
+			goto memory_error;
+		      *((char *) mempcpy (names->name[cur++], name, len))
+			= '\0';
+		      ++nfound;
 		    }
 		}
 	    }
@@ -1200,59 +1428,89 @@
     {
       size_t len = strlen (pattern);
       nfound = 1;
-      names = __alloca (sizeof (struct globlink));
-      names->next = NULL;
-      names->name = malloc (len + 1);
-      if (names->name == NULL)
+      names->name[cur] = malloc (len + 1);
+      if (names->name[cur] == NULL)
 	goto memory_error;
-      *((char *) mempcpy (names->name, pattern, len)) = '\0';
+      *((char *) mempcpy (names->name[cur++], pattern, len)) = '\0';
     }
 
+  result = GLOB_NOMATCH;
   if (nfound != 0)
     {
-      char **new_gl_pathv;
-
-      new_gl_pathv
+      char **new_gl_pathv
 	= realloc (pglob->gl_pathv,
 		   (pglob->gl_pathc + pglob->gl_offs + nfound + 1)
 		   * sizeof (char *));
-      if (new_gl_pathv == NULL)
-	goto memory_error;
-      pglob->gl_pathv = new_gl_pathv;
+      result = 0;
 
-      for (; names != NULL; names = names->next)
-	pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc++] = names->name;
-      pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+      if (new_gl_pathv == NULL)
+	{
+	memory_error:
+	  while (1)
+	    {
+	      struct globnames *old = names;
+	      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)
+		{
+		  assert (old == &init_names);
+		  break;
+		}
+	      cur = names->count;
+	      if (old == names_alloca)
+		names_alloca = names;
+	      else
+		free (old);
+	    }
+	  result = GLOB_NOSPACE;
+	}
+      else
+	{
+	  while (1)
+	    {
+	      struct globnames *old = names;
+	      for (size_t i = 0; i < cur; ++i)
+		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)
+		{
+		  assert (old == &init_names);
+		  break;
+		}
+	      cur = names->count;
+	      if (old == names_alloca)
+		names_alloca = names;
+	      else
+		free (old);
+	    }
 
-      pglob->gl_flags = flags;
+	  pglob->gl_pathv = new_gl_pathv;
+
+	  pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+
+	  pglob->gl_flags = flags;
+	}
     }
 
-  save = errno;
   if (stream != NULL)
     {
-      if (flags & GLOB_ALTDIRFUNC)
+      save = errno;
+      if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
 	(*pglob->gl_closedir) (stream);
       else
 	closedir (stream);
+      __set_errno (save);
     }
-  __set_errno (save);
-
-  return nfound == 0 ? GLOB_NOMATCH : 0;
 
- memory_error:
-  {
-    int save = errno;
-    if (flags & GLOB_ALTDIRFUNC)
-      (*pglob->gl_closedir) (stream);
-    else
-      closedir (stream);
-    __set_errno (save);
-  }
-  while (names != NULL)
-    {
-      if (names->name != NULL)
-	free (names->name);
-      names = names->next;
-    }
-  return GLOB_NOSPACE;
+  return result;
 }