diff glob/glob.c @ 4096:66d7394f5822

[project @ 2002-10-09 18:47:16 by jwe]
author jwe
date Wed, 09 Oct 2002 18:47:16 +0000
parents 655b1615eb54
children
line wrap: on
line diff
--- a/glob/glob.c	Wed Oct 09 14:44:44 2002 +0000
+++ b/glob/glob.c	Wed Oct 09 18:47:16 2002 +0000
@@ -1,38 +1,44 @@
-/* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
+/* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
 
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
 
-This library 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
-Library General Public License for more details.
+   This library 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
+   Library General Public License for more details.
 
-You should have received a copy of the GNU Library General Public
-License along with this library; see the file COPYING.LIB.  If
-not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA  02111-1307, USA.  */
+   You should have received a copy of the GNU Library General Public
+   License along with this library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
 
 /* AIX requires this to be the first thing in the file.  */
-#if defined (_AIX) && !defined (__GNUC__)
+#if defined _AIX && !defined __GNUC__
  #pragma alloca
 #endif
 
 #ifdef	HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
 #endif
 
 /* Enable GNU extensions in glob.h.  */
 #ifndef _GNU_SOURCE
-#define	_GNU_SOURCE	1
+# define _GNU_SOURCE	1
 #endif
 
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
+/* Outcomment the following line for production quality code.  */
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+#include <stdio.h>		/* Needed on stupid SunOS for assert.  */
+
 
 /* Comment out all this code if we are using the GNU C Library, and are not
    actually compiling the library itself.  This code is part of the GNU C
@@ -43,42 +49,45 @@
    it is simpler to just do this in the source for each such file.  */
 
 #define GLOB_INTERFACE_VERSION 1
-#if !defined (_LIBC) && defined (__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1
-#include <gnu-versions.h>
-#if _GNU_GLOB_INTERFACE_VERSION == GLOB_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
+#if !defined _LIBC && defined __GNU_LIBRARY__ && __GNU_LIBRARY__ > 1
+# include <gnu-versions.h>
+# if _GNU_GLOB_INTERFACE_VERSION == GLOB_INTERFACE_VERSION
+#  define ELIDE_CODE
+# endif
 #endif
 
 #ifndef ELIDE_CODE
 
-#ifdef	STDC_HEADERS
-#include <stddef.h>
+#if defined STDC_HEADERS || defined __GNU_LIBRARY__
+# include <stddef.h>
 #endif
 
-#ifdef	HAVE_UNISTD_H
-#include <unistd.h>
-#ifndef POSIX
-#ifdef	_POSIX_VERSION
-#define	POSIX
-#endif
-#endif
+#if defined HAVE_UNISTD_H || defined _LIBC
+# include <unistd.h>
+# ifndef POSIX
+#  ifdef _POSIX_VERSION
+#   define POSIX
+#  endif
+# endif
 #endif
 
-#if !defined (_AMIGA) && !defined (VMS) && !defined(WIN32)
-#include <pwd.h>
+#if !defined _AMIGA && !defined VMS && !defined WINDOWS32
+# include <pwd.h>
 #endif
 
-#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
+#if !defined __GNU_LIBRARY__ && !defined STDC_HEADERS
 extern int errno;
 #endif
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
 
 #ifndef	NULL
-#define	NULL	0
+# define NULL	0
 #endif
 
 
-#if defined (HAVE_DIRENT_H) || defined (__GNU_LIBRARY__)
+#if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__
 # include <dirent.h>
 # define NAMLEN(dirent) strlen((dirent)->d_name)
 #else
@@ -101,36 +110,42 @@
 
 /* In GNU systems, <dirent.h> defines this macro for us.  */
 #ifdef _D_NAMLEN
-#undef NAMLEN
-#define NAMLEN(d) _D_NAMLEN(d)
+# undef NAMLEN
+# define NAMLEN(d) _D_NAMLEN(d)
+#endif
+
+/* When used in the GNU libc the symbol _DIRENT_HAVE_D_TYPE is available
+   if the `d_type' member for `struct dirent' is available.  */
+#ifdef _DIRENT_HAVE_D_TYPE
+# define HAVE_D_TYPE	1
 #endif
 
 
-#if (defined (POSIX) || defined (WIN32)) && !defined (__GNU_LIBRARY__)
+#if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__
 /* Posix does not require that the d_ino field be present, and some
    systems do not provide it. */
-#define REAL_DIR_ENTRY(dp) 1
+# define REAL_DIR_ENTRY(dp) 1
 #else
-#define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
+# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
 #endif /* POSIX */
 
-#if	(defined (STDC_HEADERS) || defined (__GNU_LIBRARY__))
-#include <stdlib.h>
-#include <string.h>
-#define	ANSI_STRING
+#if defined STDC_HEADERS || defined __GNU_LIBRARY__
+# include <stdlib.h>
+# include <string.h>
+# define	ANSI_STRING
 #else	/* No standard headers.  */
 
 extern char *getenv ();
 
-#ifdef HAVE_STRING_H
-#include <string.h>
-#define	ANSI_STRING
-#else
-#include <strings.h>
-#endif
-#ifdef	HAVE_MEMORY_H
-#include <memory.h>
-#endif
+# ifdef HAVE_STRING_H
+#  include <string.h>
+#  define ANSI_STRING
+# else
+#  include <strings.h>
+# endif
+# ifdef	HAVE_MEMORY_H
+#  include <memory.h>
+# endif
 
 extern char *malloc (), *realloc ();
 extern void free ();
@@ -142,35 +157,40 @@
 
 #ifndef	ANSI_STRING
 
-#ifndef	bzero
+# ifndef bzero
 extern void bzero ();
-#endif
-#ifndef	bcopy
+# endif
+# ifndef bcopy
 extern void bcopy ();
-#endif
+# endif
 
-#define	memcpy(d, s, n)	bcopy ((s), (d), (n))
-#define	strrchr	rindex
+# define memcpy(d, s, n)	bcopy ((s), (d), (n))
+# define strrchr	rindex
 /* memset is only used for zero here, but let's be paranoid.  */
-#define	memset(s, better_be_zero, n) \
+# define memset(s, better_be_zero, n) \
   ((void) ((better_be_zero) == 0 ? (bzero((s), (n)), 0) : (abort(), 0)))
 #endif	/* Not ANSI_STRING.  */
 
-#ifndef	HAVE_STRCOLL
-#define	strcoll	strcmp
+#if !defined HAVE_STRCOLL && !defined _LIBC
+# define strcoll	strcmp
 #endif
 
+#if !defined HAVE_MEMPCPY && __GLIBC__ - 0 == 2 && __GLIBC_MINOR__ >= 1
+# define HAVE_MEMPCPY	1
+# undef  mempcpy
+# define mempcpy(Dest, Src, Len) __mempcpy (Dest, Src, Len)
+#endif
 
 #ifndef	__GNU_LIBRARY__
-#ifdef	__GNUC__
+# ifdef	__GNUC__
 __inline
-#endif
-#ifndef __SASC
-#ifdef WIN32
+# endif
+# ifndef __SASC
+#  ifdef WINDOWS32
 static void *
-#else
+#  else
 static char *
-#endif
+# endif
 my_realloc (p, n)
      char *p;
      unsigned int n;
@@ -181,82 +201,162 @@
     return (char *) malloc (n);
   return (char *) realloc (p, n);
 }
-#define	realloc	my_realloc
-#endif /* __SASC */
+# define	realloc	my_realloc
+# endif /* __SASC */
 #endif /* __GNU_LIBRARY__ */
 
 
-#if	!defined(__alloca) && !defined(__GNU_LIBRARY__)
+#if !defined __alloca && !defined __GNU_LIBRARY__
 
-#ifdef	__GNUC__
-#undef	alloca
-#define	alloca(n)	__builtin_alloca (n)
-#else	/* Not GCC.  */
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
-#else	/* Not HAVE_ALLOCA_H.  */
-#ifndef	_AIX
-#ifdef WIN32
-#include <malloc.h>
-#else
+# ifdef	__GNUC__
+#  undef alloca
+#  define alloca(n)	__builtin_alloca (n)
+# else	/* Not GCC.  */
+#  ifdef HAVE_ALLOCA_H
+#   include <alloca.h>
+#  else	/* Not HAVE_ALLOCA_H.  */
+#   ifndef _AIX
+#    ifdef WINDOWS32
+#     include <malloc.h>
+#    else
 extern char *alloca ();
-#endif /* WIN32 */
-#endif	/* Not _AIX.  */
-#endif	/* sparc or HAVE_ALLOCA_H.  */
-#endif	/* GCC.  */
+#    endif /* WINDOWS32 */
+#   endif /* Not _AIX.  */
+#  endif /* sparc or HAVE_ALLOCA_H.  */
+# endif	/* GCC.  */
 
-#define	__alloca	alloca
+# define __alloca	alloca
 
 #endif
 
 #ifndef __GNU_LIBRARY__
-#define __stat stat
-#ifdef STAT_MACROS_BROKEN
-#undef S_ISDIR
-#endif
-#ifndef S_ISDIR
-#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
-#endif
+# define __stat stat
+# ifdef STAT_MACROS_BROKEN
+#  undef S_ISDIR
+# endif
+# ifndef S_ISDIR
+#  define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+# endif
 #endif
 
-#ifndef	STDC_HEADERS
-#undef	size_t
-#define	size_t	unsigned int
+#ifdef _LIBC
+# undef strdup
+# define strdup(str) __strdup (str)
+# define sysconf(id) __sysconf (id)
+# define closedir(dir) __closedir (dir)
+# define opendir(name) __opendir (name)
+# define readdir(str) __readdir (str)
+# define getpwnam_r(name, bufp, buf, len, res) \
+   __getpwnam_r (name, bufp, buf, len, res)
+# ifndef __stat
+#  define __stat(fname, buf) __xstat (_STAT_VER, fname, buf)
+# endif
+#endif
+
+#if !(defined STDC_HEADERS || defined __GNU_LIBRARY__)
+# undef	size_t
+# define size_t	unsigned int
 #endif
 
 /* Some system header files erroneously define these.
    We want our own definitions from <fnmatch.h> to take precedence.  */
-#undef	FNM_PATHNAME
-#undef	FNM_NOESCAPE
-#undef	FNM_PERIOD
+#ifndef __GNU_LIBRARY__
+# undef	FNM_PATHNAME
+# undef	FNM_NOESCAPE
+# undef	FNM_PERIOD
+#endif
 #include <fnmatch.h>
 
 /* Some system header files erroneously define these.
    We want our own definitions from <glob.h> to take precedence.  */
-#undef	GLOB_ERR
-#undef	GLOB_MARK
-#undef	GLOB_NOSORT
-#undef	GLOB_DOOFFS
-#undef	GLOB_NOCHECK
-#undef	GLOB_APPEND
-#undef	GLOB_NOESCAPE
-#undef	GLOB_PERIOD
+#ifndef __GNU_LIBRARY__
+# undef	GLOB_ERR
+# undef	GLOB_MARK
+# undef	GLOB_NOSORT
+# undef	GLOB_DOOFFS
+# undef	GLOB_NOCHECK
+# undef	GLOB_APPEND
+# undef	GLOB_NOESCAPE
+# undef	GLOB_PERIOD
+#endif
 #include <glob.h>
+
+#ifdef HAVE_GETLOGIN_R
+extern int getlogin_r __P ((char *, size_t));
+#else
+extern char *getlogin __P ((void));
+#endif
 
-static int glob_pattern_p __P ((const char *pattern, int quote));
+static
+#if __GNUC__ - 0 >= 2
+inline
+#endif
+const char *next_brace_sub __P ((const char *begin));
 static int glob_in_dir __P ((const char *pattern, const char *directory,
 			     int flags,
-			     int (*errfunc) __P ((const char *, int)),
+			     int (*errfunc) (const char *, int),
 			     glob_t *pglob));
 static int prefix_array __P ((const char *prefix, char **array, size_t n));
 static int collated_compare __P ((const __ptr_t, const __ptr_t));
 
+#ifdef VMS
+/* these compilers like prototypes */
+#if !defined _LIBC || !defined NO_GLOB_PATTERN_P
+int __glob_pattern_p (const char *pattern, int quote);
+#endif
+#endif
+
+/* Find the end of the sub-pattern in a brace expression.  We define
+   this as an inline function if the compiler permits.  */
+static
+#if __GNUC__ - 0 >= 2
+inline
+#endif
+const char *
+next_brace_sub (begin)
+     const char *begin;
+{
+  unsigned int depth = 0;
+  const char *cp = begin;
+
+  while (1)
+    {
+      if (depth == 0)
+	{
+	  if (*cp != ',' && *cp != '}' && *cp != '\0')
+	    {
+	      if (*cp == '{')
+		++depth;
+	      ++cp;
+	      continue;
+	    }
+	}
+      else
+	{
+	  while (*cp != '\0' && (*cp != '}' || depth > 0))
+	    {
+	      if (*cp == '}')
+		--depth;
+	      ++cp;
+	    }
+	  if (*cp == '\0')
+	    /* An incorrectly terminated brace expression.  */
+	    return NULL;
+
+	  continue;
+	}
+      break;
+    }
+
+  return cp;
+}
+
 /* Do glob searching for PATTERN, placing results in PGLOB.
    The bits defined above may be set in FLAGS.
    If a directory cannot be opened or read and ERRFUNC is not nil,
    it is called with the pathname that caused the error, and the
    `errno' value from the failing call; if it returns non-zero
-   `glob' returns GLOB_ABEND; if it returns zero, the error is ignored.
+   `glob' returns GLOB_ABORTED; if it returns zero, the error is ignored.
    If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
    Otherwise, `glob' returns zero.  */
 int
@@ -267,14 +367,14 @@
      glob_t *pglob;
 {
   const char *filename;
-  char *dirname;
+  const char *dirname;
   size_t dirlen;
   int status;
   int oldcount;
 
   if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0)
     {
-      errno = EINVAL;
+      __set_errno (EINVAL);
       return -1;
     }
 
@@ -283,38 +383,63 @@
       const char *begin = strchr (pattern, '{');
       if (begin != NULL)
 	{
+	  /* Allocate working buffer large enough for our work.  Note that
+	    we have at least an opening and closing brace.  */
 	  int firstc;
-	  size_t restlen;
-	  const char *p, *end, *next;
-	  unsigned int depth = 0;
+	  char *alt_start;
+	  const char *p;
+	  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)
+	    {
+	      if (!(flags & GLOB_APPEND))
+		globfree (pglob);
+	      return GLOB_NOSPACE;
+	    }
+#endif
 
-	  /* Find the end of the brace expression, by counting braces.
-	     While we're at it, notice the first comma at top brace level.  */
-	  end = begin + 1;
-	  next = NULL;
-	  while (1)
+	  /* We know the prefix for all sub-patterns.  */
+#ifdef HAVE_MEMPCPY
+	  alt_start = mempcpy (onealt, pattern, begin - pattern);
+#else
+	  memcpy (onealt, pattern, begin - pattern);
+	  alt_start = &onealt[begin - pattern];
+#endif
+
+	  /* Find the first sub-pattern and at the same time find the
+	     rest after the closing brace.  */
+	  next = next_brace_sub (begin + 1);
+	  if (next == NULL)
 	    {
-	      switch (*end++)
+	      /* It is an illegal expression.  */
+#ifndef __GNUC__
+	      free (onealt);
+#endif
+	      return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
+	    }
+
+	  /* Now find the end of the whole brace expression.  */
+	  rest = next;
+	  while (*rest != '}')
+	    {
+	      rest = next_brace_sub (rest + 1);
+	      if (rest == NULL)
 		{
-		case ',':
-		  if (depth == 0 && next == NULL)
-		    next = end;
-		  continue;
-		case '{':
-		  ++depth;
-		  continue;
-		case '}':
-		  if (depth-- == 0)
-		    break;
-		  continue;
-		case '\0':
-		  return glob (pattern, flags &~ GLOB_BRACE, errfunc, pglob);
+		  /* It is an illegal expression.  */
+#ifndef __GNUC__
+		  free (onealt);
+#endif
+		  return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
 		}
-	      break;
 	    }
-	  restlen = strlen (end) + 1;
-	  if (next == NULL)
-	    next = end;
+	  /* Please note that we now can be sure the brace expression
+	     is well-formed.  */
+	  rest_len = strlen (++rest) + 1;
 
 	  /* We have a brace expression.  BEGIN points to the opening {,
 	     NEXT points past the terminator of the first element, and END
@@ -331,111 +456,149 @@
 	    }
 	  firstc = pglob->gl_pathc;
 
-	  /* In this loop P points to the beginning of the current element
-	     and NEXT points past its terminator.  */
 	  p = begin + 1;
 	  while (1)
 	    {
-	      /* Construct a whole name that is one of the brace
-		 alternatives in a temporary buffer.  */
 	      int result;
-	      size_t bufsz = (begin - pattern) + (next - 1 - p) + restlen;
-#ifdef __GNUC__
-	      char onealt[bufsz];
+
+	      /* Construct the new glob expression.  */
+#ifdef HAVE_MEMPCPY
+	      mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len);
 #else
-	      char *onealt = malloc (bufsz);
-	      if (onealt == NULL)
-		{
-		  if (!(flags & GLOB_APPEND))
-		    globfree (pglob);
-		  return GLOB_NOSPACE;
-		}
+	      memcpy (alt_start, p, next - p);
+	      memcpy (&alt_start[next - p], rest, rest_len);
 #endif
-	      memcpy (onealt, pattern, begin - pattern);
-	      memcpy (&onealt[begin - pattern], p, next - 1 - p);
-	      memcpy (&onealt[(begin - pattern) + (next - 1 - p)],
-		      end, restlen);
+
 	      result = glob (onealt,
-			     ((flags & ~(GLOB_NOCHECK|GLOB_NOMAGIC)) |
-			      GLOB_APPEND), errfunc, pglob);
-#ifndef __GNUC__
-	      free (onealt);
-#endif
+			     ((flags & ~(GLOB_NOCHECK|GLOB_NOMAGIC))
+			      | GLOB_APPEND), errfunc, pglob);
 
 	      /* If we got an error, return it.  */
 	      if (result && result != GLOB_NOMATCH)
 		{
+#ifndef __GNUC__
+		  free (onealt);
+#endif
 		  if (!(flags & GLOB_APPEND))
 		    globfree (pglob);
 		  return result;
 		}
 
-	      /* Advance past this alternative and process the next.  */
-	      p = next;
-	      depth = 0;
-	    scan:
-	      switch (*p++)
-		{
-		case ',':
-		  if (depth == 0)
-		    {
-		      /* Found the next alternative.  Loop to glob it.  */
-		      next = p;
-		      continue;
-		    }
-		  goto scan;
-		case '{':
-		  ++depth;
-		  goto scan;
-		case '}':
-		  if (depth-- == 0)
-		    /* End of the brace expression.  Break out of the loop.  */
-		    break;
-		  goto scan;
-		}
+	      if (*next == '}')
+		/* We saw the last entry.  */
+		break;
+
+	      p = next + 1;
+	      next = next_brace_sub (p);
+	      assert (next != NULL);
 	    }
 
-	  if (pglob->gl_pathc == firstc &&
-	      !(flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
+#ifndef __GNUC__
+	  free (onealt);
+#endif
+
+	  if (pglob->gl_pathc != firstc)
+	    /* We found some entries.  */
+	    return 0;
+	  else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
 	    return GLOB_NOMATCH;
 	}
     }
 
   /* Find the filename.  */
   filename = strrchr (pattern, '/');
+#if defined __MSDOS__ || defined WINDOWS32
+  /* The case of "d:pattern".  Since `:' is not allowed in
+     file names, we can safely assume that wherever it
+     happens in pattern, it signals the filename part.  This
+     is so we could some day support patterns like "[a-z]:foo".  */
+  if (filename == NULL)
+    filename = strchr (pattern, ':');
+#endif /* __MSDOS__ || WINDOWS32 */
   if (filename == NULL)
     {
-      filename = pattern;
+      /* This can mean two things: a simple name or "~name".  The later
+	 case is nothing but a notation for a directory.  */
+      if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~')
+	{
+	  dirname = pattern;
+	  dirlen = strlen (pattern);
+
+	  /* Set FILENAME to NULL as a special flag.  This is ugly but
+	     other solutions would require much more code.  We test for
+	     this special case below.  */
+	  filename = NULL;
+	}
+      else
+	{
+	  filename = pattern;
 #ifdef _AMIGA
-      dirname = (char *) "";
+	  dirname = "";
 #else
-      dirname = (char *) ".";
+	  dirname = ".";
 #endif
-      dirlen = 0;
+	  dirlen = 0;
+	}
     }
   else if (filename == pattern)
     {
       /* "/pattern".  */
-      dirname = (char *) "/";
+      dirname = "/";
       dirlen = 1;
       ++filename;
     }
   else
     {
+      char *newp;
       dirlen = filename - pattern;
-      dirname = (char *) __alloca (dirlen + 1);
-      memcpy (dirname, pattern, dirlen);
-      dirname[dirlen] = '\0';
-      ++filename;
-    }
+#if defined __MSDOS__ || defined WINDOWS32
+      if (*filename == ':'
+	  || (filename > pattern + 1 && filename[-1] == ':'))
+	{
+	  char *drive_spec;
 
-  if (filename[0] == '\0' && dirlen > 1)
-    /* "pattern/".  Expand "pattern", appending slashes.  */
-    {
-      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;
+	  ++dirlen;
+	  drive_spec = (char *) __alloca (dirlen + 1);
+#ifdef HAVE_MEMPCPY
+	  *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0';
+#else
+	  memcpy (drive_spec, pattern, dirlen);
+	  drive_spec[dirlen] = '\0';
+#endif
+	  /* For now, disallow wildcards in the drive spec, to
+	     prevent infinite recursion in glob.  */
+	  if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE)))
+	    return GLOB_NOMATCH;
+	  /* If this is "d:pattern", we need to copy `:' to DIRNAME
+	     as well.  If it's "d:/pattern", don't remove the slash
+	     from "d:/", since "d:" and "d:/" are not the same.*/
+	}
+#endif
+      newp = (char *) __alloca (dirlen + 1);
+#ifdef HAVE_MEMPCPY
+      *((char *) mempcpy (newp, pattern, dirlen)) = '\0';
+#else
+      memcpy (newp, pattern, dirlen);
+      newp[dirlen] = '\0';
+#endif
+      dirname = newp;
+      ++filename;
+
+      if (filename[0] == '\0'
+#if defined __MSDOS__ || defined WINDOWS32
+          && dirname[dirlen - 1] != ':'
+	  && (dirlen < 3 || dirname[dirlen - 2] != ':'
+	      || dirname[dirlen - 1] != '/')
+#endif
+	  && dirlen > 1)
+	/* "pattern/".  Expand "pattern", appending slashes.  */
+	{
+	  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))
@@ -447,57 +610,247 @@
   oldcount = pglob->gl_pathc;
 
 #ifndef VMS
-  if ((flags & GLOB_TILDE) && dirname[0] == '~')
+  if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~')
     {
-      if (dirname[1] == '\0')
+      if (dirname[1] == '\0' || dirname[1] == '/')
 	{
 	  /* Look up home directory.  */
-	  dirname = getenv ("HOME");
-#ifdef _AMIGA
-	  if (dirname == NULL || dirname[0] == '\0')
-	    dirname = "SYS:";
-#else
-#ifdef WIN32
-	  if (dirname == NULL || dirname[0] == '\0')
-            dirname = "c:/users/default"; /* poor default */
+#ifdef VMS
+/* This isn't obvious, RTLs of DECC and VAXC know about "HOME" */
+          const char *home_dir = getenv ("SYS$LOGIN");
 #else
-	  if (dirname == NULL || dirname[0] == '\0')
+          const char *home_dir = getenv ("HOME");
+#endif
+# 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 */
+#  else
+#   ifdef VMS
+/* Again, this isn't obvious, if "HOME" isn't known "SYS$LOGIN" should be set */
+	  if (home_dir == NULL || home_dir[0] == '\0')
+	    home_dir = "SYS$DISK:[]";
+#   else
+	  if (home_dir == NULL || home_dir[0] == '\0')
 	    {
-	      extern char *getlogin __P ((void));
-	      char *name = getlogin ();
-	      if (name != NULL)
+	      int success;
+	      char *name;
+#   if defined HAVE_GETLOGIN_R || defined _LIBC
+	      size_t buflen = sysconf (_SC_LOGIN_NAME_MAX) + 1;
+
+	      if (buflen == 0)
+		/* `sysconf' does not support _SC_LOGIN_NAME_MAX.  Try
+		   a moderate value.  */
+		buflen = 20;
+	      name = (char *) __alloca (buflen);
+
+	      success = getlogin_r (name, buflen) >= 0;
+#   else
+	      success = (name = getlogin ()) != NULL;
+#   endif
+	      if (success)
 		{
-		  struct passwd *p = getpwnam (name);
+		  struct passwd *p;
+#   if defined HAVE_GETPWNAM_R || defined _LIBC
+		  size_t pwbuflen = sysconf (_SC_GETPW_R_SIZE_MAX);
+		  char *pwtmpbuf;
+		  struct passwd pwbuf;
+		  int save = errno;
+
+		  if (pwbuflen == -1)
+		    /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.
+		       Try a moderate value.  */
+		    pwbuflen = 1024;
+		  pwtmpbuf = (char *) __alloca (pwbuflen);
+
+		  while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p)
+			 != 0)
+		    {
+		      if (errno != ERANGE)
+			{
+			  p = NULL;
+			  break;
+			}
+		      pwbuflen *= 2;
+		      pwtmpbuf = (char *) __alloca (pwbuflen);
+		      __set_errno (save);
+		    }
+#   else
+		  p = getpwnam (name);
+#   endif
 		  if (p != NULL)
-		    dirname = p->pw_dir;
+		    home_dir = p->pw_dir;
 		}
 	    }
-	  if (dirname == NULL || dirname[0] == '\0')
-	    dirname = (char *) "~"; /* No luck.  */
-#endif /* WIN32 */
-#endif
+	  if (home_dir == NULL || home_dir[0] == '\0')
+	    {
+	      if (flags & GLOB_TILDE_CHECK)
+		return GLOB_NOMATCH;
+	      else
+		home_dir = "~"; /* No luck.  */
+	    }
+#   endif /* VMS */
+#  endif /* WINDOWS32 */
+# endif
+	  /* Now construct the full directory.  */
+	  if (dirname[1] == '\0')
+	    dirname = home_dir;
+	  else
+	    {
+	      char *newp;
+	      size_t home_len = strlen (home_dir);
+	      newp = (char *) __alloca (home_len + dirlen);
+# ifdef HAVE_MEMPCPY
+	      mempcpy (mempcpy (newp, home_dir, home_len),
+		       &dirname[1], dirlen);
+# else
+	      memcpy (newp, home_dir, home_len);
+	      memcpy (&newp[home_len], &dirname[1], dirlen);
+# endif
+	      dirname = newp;
+	    }
 	}
+# if !defined _AMIGA && !defined WINDOWS32 && !defined VMS
       else
 	{
-#ifdef _AMIGA
-	  if (dirname == NULL || dirname[0] == '\0')
-	    dirname = "SYS:";
-#else
-#ifdef WIN32
-	  if (dirname == NULL || dirname[0] == '\0')
-            dirname = "c:/users/default"; /* poor default */
-#else
+	  char *end_name = strchr (dirname, '/');
+	  const char *user_name;
+	  const char *home_dir;
+
+	  if (end_name == NULL)
+	    user_name = dirname + 1;
+	  else
+	    {
+	      char *newp;
+	      newp = (char *) __alloca (end_name - dirname);
+# ifdef HAVE_MEMPCPY
+	      *((char *) mempcpy (newp, dirname + 1, end_name - dirname))
+		= '\0';
+# else
+	      memcpy (newp, dirname + 1, end_name - dirname);
+	      newp[end_name - dirname - 1] = '\0';
+# endif
+	      user_name = newp;
+	    }
+
 	  /* Look up specific user's home directory.  */
-	  struct passwd *p = getpwnam (dirname + 1);
-	  if (p != NULL)
-	    dirname = p->pw_dir;
-#endif /* WIN32 */
-#endif
+	  {
+	    struct passwd *p;
+#  if defined HAVE_GETPWNAM_R || defined _LIBC
+	    size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
+	    char *pwtmpbuf;
+	    struct passwd pwbuf;
+	    int save = errno;
+
+	    if (buflen == -1)
+	      /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.  Try a
+		 moderate value.  */
+	      buflen = 1024;
+	    pwtmpbuf = (char *) __alloca (buflen);
+
+	    while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0)
+	      {
+		if (errno != ERANGE)
+		  {
+		    p = NULL;
+		    break;
+		  }
+		buflen *= 2;
+		pwtmpbuf = __alloca (buflen);
+		__set_errno (save);
+	      }
+#  else
+	    p = getpwnam (user_name);
+#  endif
+	    if (p != NULL)
+	      home_dir = p->pw_dir;
+	    else
+	      home_dir = NULL;
+	  }
+	  /* 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);
+#  ifdef HAVE_MEMPCPY
+	      *((char *) mempcpy (mempcpy (newp, home_dir, home_len),
+				  end_name, rest_len)) = '\0';
+#  else
+	      memcpy (newp, home_dir, home_len);
+	      memcpy (&newp[home_len], end_name, rest_len);
+	      newp[home_len + rest_len] = '\0';
+#  endif
+	      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 && not VMS.  */
     }
 #endif	/* Not VMS.  */
 
-  if (glob_pattern_p (dirname, !(flags & GLOB_NOESCAPE)))
+  /* Now test whether we looked for "~" or "~NAME".  In this case we
+     can give the answer now.  */
+  if (filename == NULL)
+    {
+      struct stat st;
+
+      /* Return the directory if we don't check for error or if it exists.  */
+      if ((flags & GLOB_NOCHECK)
+	  || (((flags & GLOB_ALTDIRFUNC)
+	       ? (*pglob->gl_stat) (dirname, &st)
+	       : __stat (dirname, &st)) == 0
+	      && S_ISDIR (st.st_mode)))
+	{
+	  pglob->gl_pathv
+	    = (char **) realloc (pglob->gl_pathv,
+				 (pglob->gl_pathc +
+				  ((flags & GLOB_DOOFFS) ?
+				   pglob->gl_offs : 0) +
+				  1 + 1) *
+				 sizeof (char *));
+	  if (pglob->gl_pathv == NULL)
+	    return GLOB_NOSPACE;
+
+	  if (flags & GLOB_DOOFFS)
+	    while (pglob->gl_pathc < pglob->gl_offs)
+	      pglob->gl_pathv[pglob->gl_pathc++] = NULL;
+
+#if defined HAVE_STRDUP || defined _LIBC
+	  pglob->gl_pathv[pglob->gl_pathc] = strdup (dirname);
+#else
+	  {
+	    size_t len = strlen (dirname) + 1;
+	    char *dircopy = malloc (len);
+	    if (dircopy != NULL)
+	      pglob->gl_pathv[pglob->gl_pathc] = memcpy (dircopy, dirname,
+							 len);
+	  }
+#endif
+	  if (pglob->gl_pathv[pglob->gl_pathc] == NULL)
+	    {
+	      free (pglob->gl_pathv);
+	      return GLOB_NOSPACE;
+	    }
+	  pglob->gl_pathv[++pglob->gl_pathc] = NULL;
+	  pglob->gl_flags = flags;
+
+	  return 0;
+	}
+
+      /* Not found.  */
+      return GLOB_NOMATCH;
+    }
+
+  if (__glob_pattern_p (dirname, !(flags & GLOB_NOESCAPE)))
     {
       /* The directory name contains metacharacters, so we
 	 have to glob for the directory, and then glob for
@@ -506,8 +859,8 @@
       register int i;
 
       status = glob (dirname,
-		     ((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE)) |
-		      GLOB_NOSORT),
+		     ((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE))
+		      | GLOB_NOSORT | GLOB_ONLYDIR),
 		     errfunc, &dirs);
       if (status != 0)
 	return status;
@@ -517,7 +870,7 @@
 	 appending the results to PGLOB.  */
       for (i = 0; i < dirs.gl_pathc; ++i)
 	{
-	  int oldcount;
+	  int old_pathc;
 
 #ifdef	SHELL
 	  {
@@ -528,14 +881,15 @@
 	      {
 		globfree (&dirs);
 		globfree (&files);
-		return GLOB_ABEND;
+		return GLOB_ABORTED;
 	      }
 	  }
 #endif /* SHELL.  */
 
-	  oldcount = pglob->gl_pathc;
+	  old_pathc = pglob->gl_pathc;
 	  status = glob_in_dir (filename, dirs.gl_pathv[i],
-				(flags | GLOB_APPEND) & ~GLOB_NOCHECK,
+				((flags | GLOB_APPEND)
+				 & ~(GLOB_NOCHECK | GLOB_ERR)),
 				errfunc, pglob);
 	  if (status == GLOB_NOMATCH)
 	    /* No matches in this directory.  Try the next.  */
@@ -550,8 +904,8 @@
 
 	  /* Stick the directory on the front of each name.  */
 	  if (prefix_array (dirs.gl_pathv[i],
-			    &pglob->gl_pathv[oldcount],
-			    pglob->gl_pathc - oldcount))
+			    &pglob->gl_pathv[old_pathc],
+			    pglob->gl_pathc - old_pathc))
 	    {
 	      globfree (&dirs);
 	      globfree (pglob);
@@ -561,39 +915,87 @@
 
       flags |= GLOB_MAGCHAR;
 
+      /* We have ignored the GLOB_NOCHECK flag in the `glob_in_dir' calls.
+	 But if we have not found any matching entry and thie GLOB_NOCHECK
+	 flag was set we must return the list consisting of the disrectory
+	 names followed by the filename.  */
       if (pglob->gl_pathc == oldcount)
-	/* No matches.  */
-	if (flags & GLOB_NOCHECK)
-	  {
-	    size_t len = strlen (pattern) + 1;
-	    char *patcopy = (char *) malloc (len);
-	    if (patcopy == NULL)
-	      return GLOB_NOSPACE;
-	    memcpy (patcopy, pattern, len);
+	{
+	  /* No matches.  */
+	  if (flags & GLOB_NOCHECK)
+	    {
+	      size_t filename_len = strlen (filename) + 1;
+	      char **new_pathv;
+	      struct stat st;
+
+	      /* This is an pessimistic guess about the size.  */
+	      pglob->gl_pathv
+		= (char **) realloc (pglob->gl_pathv,
+				     (pglob->gl_pathc +
+				      ((flags & GLOB_DOOFFS) ?
+				       pglob->gl_offs : 0) +
+				      dirs.gl_pathc + 1) *
+				     sizeof (char *));
+	      if (pglob->gl_pathv == NULL)
+		{
+		  globfree (&dirs);
+		  return GLOB_NOSPACE;
+		}
+
+	      if (flags & GLOB_DOOFFS)
+		while (pglob->gl_pathc < pglob->gl_offs)
+		  pglob->gl_pathv[pglob->gl_pathc++] = NULL;
+
+	      for (i = 0; i < dirs.gl_pathc; ++i)
+		{
+		  const char *dir = dirs.gl_pathv[i];
+		  size_t dir_len = strlen (dir);
 
-	    pglob->gl_pathv
-	      = (char **) realloc (pglob->gl_pathv,
-				   (pglob->gl_pathc +
-				    ((flags & GLOB_DOOFFS) ?
-				     pglob->gl_offs : 0) +
-				    1 + 1) *
-				   sizeof (char *));
-	    if (pglob->gl_pathv == NULL)
-	      {
-		free (patcopy);
-		return GLOB_NOSPACE;
-	      }
+		  /* First check whether this really is a directory.  */
+		  if (((flags & GLOB_ALTDIRFUNC)
+		       ? (*pglob->gl_stat) (dir, &st) : __stat (dir, &st)) != 0
+		      || !S_ISDIR (st.st_mode))
+		    /* No directory, ignore this entry.  */
+		    continue;
+
+		  pglob->gl_pathv[pglob->gl_pathc] = malloc (dir_len + 1
+							     + filename_len);
+		  if (pglob->gl_pathv[pglob->gl_pathc] == NULL)
+		    {
+		      globfree (&dirs);
+		      globfree (pglob);
+		      return GLOB_NOSPACE;
+		    }
 
-	    if (flags & GLOB_DOOFFS)
-	      while (pglob->gl_pathc < pglob->gl_offs)
-		pglob->gl_pathv[pglob->gl_pathc++] = NULL;
+#ifdef HAVE_MEMPCPY
+		  mempcpy (mempcpy (mempcpy (pglob->gl_pathv[pglob->gl_pathc],
+					     dir, dir_len),
+				    "/", 1),
+			   filename, filename_len);
+#else
+		  memcpy (pglob->gl_pathv[pglob->gl_pathc], dir, dir_len);
+		  pglob->gl_pathv[pglob->gl_pathc][dir_len] = '/';
+		  memcpy (&pglob->gl_pathv[pglob->gl_pathc][dir_len + 1],
+			  filename, filename_len);
+#endif
+		  ++pglob->gl_pathc;
+		}
 
-	    pglob->gl_pathv[pglob->gl_pathc++] = patcopy;
-	    pglob->gl_pathv[pglob->gl_pathc] = NULL;
-	    pglob->gl_flags = flags;
-	  }
-	else
-	  return GLOB_NOMATCH;
+	      pglob->gl_pathv[pglob->gl_pathc] = NULL;
+	      pglob->gl_flags = flags;
+
+	      /* Now we know how large the gl_pathv vector must be.  */
+	      new_pathv = (char **) realloc (pglob->gl_pathv,
+					     ((pglob->gl_pathc + 1)
+					      * sizeof (char *)));
+	      if (new_pathv != NULL)
+		pglob->gl_pathv = new_pathv;
+	    }
+	  else
+	    return GLOB_NOMATCH;
+	}
+
+      globfree (&dirs);
     }
   else
     {
@@ -604,9 +1006,14 @@
       if (dirlen > 0)
 	{
 	  /* Stick the directory on the front of each name.  */
+	  int ignore = oldcount;
+
+	  if ((flags & GLOB_DOOFFS) && ignore < pglob->gl_offs)
+	    ignore = pglob->gl_offs;
+
 	  if (prefix_array (dirname,
-			    &pglob->gl_pathv[oldcount],
-			    pglob->gl_pathc - oldcount))
+			    &pglob->gl_pathv[ignore],
+			    pglob->gl_pathc - ignore))
 	    {
 	      globfree (pglob);
 	      return GLOB_NOSPACE;
@@ -620,10 +1027,10 @@
       int i;
       struct stat st;
       for (i = oldcount; i < pglob->gl_pathc; ++i)
-	if (((flags & GLOB_ALTDIRFUNC) ?
-	     (*pglob->gl_stat) (pglob->gl_pathv[i], &st) :
-	     __stat (pglob->gl_pathv[i], &st)) == 0 &&
-	    S_ISDIR (st.st_mode))
+	if (((flags & GLOB_ALTDIRFUNC)
+	     ? (*pglob->gl_stat) (pglob->gl_pathv[i], &st)
+	     : __stat (pglob->gl_pathv[i], &st)) == 0
+	    && S_ISDIR (st.st_mode))
 	  {
  	    size_t len = strlen (pglob->gl_pathv[i]) + 2;
 	    char *new = realloc (pglob->gl_pathv[i], len);
@@ -638,10 +1045,17 @@
     }
 
   if (!(flags & GLOB_NOSORT))
-    /* Sort the vector.  */
-    qsort ((__ptr_t) &pglob->gl_pathv[oldcount],
-	   pglob->gl_pathc - oldcount,
-	   sizeof (char *), collated_compare);
+    {
+      /* Sort the vector.  */
+      int non_sort = oldcount;
+
+      if ((flags & GLOB_DOOFFS) && pglob->gl_offs > oldcount)
+	non_sort = pglob->gl_offs;
+
+      qsort ((__ptr_t) &pglob->gl_pathv[non_sort],
+	     pglob->gl_pathc - non_sort,
+	     sizeof (char *), collated_compare);
+    }
 
   return 0;
 }
@@ -694,11 +1108,31 @@
 {
   register size_t i;
   size_t dirlen = strlen (dirname);
+#if defined __MSDOS__ || defined WINDOWS32
+  int sep_char = '/';
+# define DIRSEP_CHAR sep_char
+#else
+# define DIRSEP_CHAR '/'
+#endif
 
   if (dirlen == 1 && dirname[0] == '/')
     /* DIRNAME is just "/", so normal prepending would get us "//foo".
        We want "/foo" instead, so don't prepend any chars from DIRNAME.  */
     dirlen = 0;
+#if defined __MSDOS__ || defined WINDOWS32
+  else if (dirlen > 1)
+    {
+      if (dirname[dirlen - 1] == '/' && dirname[dirlen - 2] == ':')
+	/* DIRNAME is "d:/".  Don't prepend the slash from DIRNAME.  */
+	--dirlen;
+      else if (dirname[dirlen - 1] == ':')
+	{
+	  /* DIRNAME is "d:".  Use `:' instead of `/'.  */
+	  --dirlen;
+	  sep_char = ':';
+	}
+    }
+#endif
 
   for (i = 0; i < n; ++i)
     {
@@ -711,9 +1145,17 @@
 	  return 1;
 	}
 
+#ifdef HAVE_MEMPCPY
+      {
+	char *endp = (char *) mempcpy (new, dirname, dirlen);
+	*endp++ = DIRSEP_CHAR;
+	mempcpy (endp, array[i], eltlen);
+      }
+#else
       memcpy (new, dirname, dirlen);
-      new[dirlen] = '/';
+      new[dirlen] = DIRSEP_CHAR;
       memcpy (&new[dirlen + 1], array[i], eltlen);
+#endif
       free ((__ptr_t) array[i]);
       array[i] = new;
     }
@@ -722,10 +1164,12 @@
 }
 
 
+/* 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.  */
-static int
-glob_pattern_p (pattern, quote)
+int
+__glob_pattern_p (pattern, quote)
      const char *pattern;
      int quote;
 {
@@ -756,6 +1200,10 @@
 
   return 0;
 }
+# ifdef _LIBC
+weak_alias (__glob_pattern_p, glob_pattern_p)
+# endif
+#endif
 
 
 /* Like `glob', but PATTERN is a final pathname component,
@@ -770,7 +1218,7 @@
      int (*errfunc) __P ((const char *, int));
      glob_t *pglob;
 {
-  __ptr_t stream;
+  __ptr_t stream = NULL;
 
   struct globlink
     {
@@ -778,69 +1226,136 @@
       char *name;
     };
   struct globlink *names = NULL;
-  size_t nfound = 0;
+  size_t nfound;
+  int meta;
+  int save;
 
-  if (!glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE)))
+#ifdef VMS
+  if (*directory == 0)
+    directory = "[]";
+#endif
+  meta = __glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));
+  if (meta == 0)
     {
-      stream = NULL;
-      flags |= GLOB_NOCHECK;
+      if (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;
+      else
+	{
+	  /* Since we use the normal file functions we can also use stat()
+	     to verify the file is there.  */
+	  struct stat st;
+	  size_t patlen = strlen (pattern);
+	  size_t dirlen = strlen (directory);
+	  char *fullname = (char *) __alloca (dirlen + 1 + patlen + 1);
+
+# ifdef HAVE_MEMPCPY
+	  mempcpy (mempcpy (mempcpy (fullname, directory, dirlen),
+			    "/", 1),
+		   pattern, patlen + 1);
+# else
+	  memcpy (fullname, directory, dirlen);
+	  fullname[dirlen] = '/';
+	  memcpy (&fullname[dirlen + 1], pattern, patlen + 1);
+# endif
+	  if (((flags & GLOB_ALTDIRFUNC)
+	       ? (*pglob->gl_stat) (fullname, &st)
+	       : __stat (fullname, &st)) == 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
     {
-      flags |= GLOB_MAGCHAR;
-
-      stream = ((flags & GLOB_ALTDIRFUNC) ?
-		(*pglob->gl_opendir) (directory) :
-		(__ptr_t) opendir (directory));
-      if (stream == NULL)
+      if (pattern[0] == '\0')
 	{
-	  if ((errfunc != NULL && (*errfunc) (directory, errno)) ||
-	      (flags & GLOB_ERR))
-	    return GLOB_ABEND;
+	  /* This is a special case for matching directories like in
+	     "*a/".  */
+	  names = (struct globlink *) __alloca (sizeof (struct globlink));
+	  names->name = (char *) malloc (1);
+	  if (names->name == NULL)
+	    goto memory_error;
+	  names->name[0] = '\0';
+	  names->next = NULL;
+	  nfound = 1;
+	  meta = 0;
 	}
       else
-	while (1)
-	  {
-	    const char *name;
-	    size_t len;
-	    struct dirent *d = ((flags & GLOB_ALTDIRFUNC) ?
-				(*pglob->gl_readdir) (stream) :
-				readdir ((DIR *) stream));
-	    if (d == NULL)
-	      break;
-	    if (! REAL_DIR_ENTRY (d))
-	      continue;
-
-	    name = d->d_name;
-
-	    if (fnmatch (pattern, name,
-			 (!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) |
-			 ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
-#ifdef _AMIGA
-			 | FNM_CASEFOLD
+	{
+	  stream = ((flags & GLOB_ALTDIRFUNC)
+		    ? (*pglob->gl_opendir) (directory)
+		    : (__ptr_t) 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
 #endif
-			 ) == 0)
-	      {
-		struct globlink *new
-		  = (struct globlink *) __alloca (sizeof (struct globlink));
-		len = NAMLEN (d);
-		new->name
-		  = (char *) malloc (len + 1);
-		if (new->name == NULL)
-		  goto memory_error;
-		memcpy ((__ptr_t) new->name, name, len);
-		new->name[len] = '\0';
-		new->next = names;
-		names = new;
-		++nfound;
-	      }
-	  }
+				   );
+	      nfound = 0;
+	      flags |= GLOB_MAGCHAR;
+
+	      while (1)
+		{
+		  const char *name;
+		  size_t len;
+		  struct dirent *d = ((flags & GLOB_ALTDIRFUNC)
+				      ? (*pglob->gl_readdir) (stream)
+				      : readdir ((DIR *) stream));
+		  if (d == NULL)
+		    break;
+		  if (! REAL_DIR_ENTRY (d))
+		    continue;
+
+#ifdef HAVE_D_TYPE
+		  /* If we shall match only directories use the information
+		     provided by the dirent call if possible.  */
+		  if ((flags & GLOB_ONLYDIR)
+		      && d->d_type != DT_UNKNOWN && d->d_type != DT_DIR)
+		    continue;
+#endif
+
+		  name = d->d_name;
+
+		  if (fnmatch (pattern, name, fnm_flags) == 0)
+		    {
+		      struct globlink *new = (struct globlink *)
+			__alloca (sizeof (struct globlink));
+		      len = NAMLEN (d);
+		      new->name = (char *) malloc (len + 1);
+		      if (new->name == NULL)
+			goto memory_error;
+#ifdef HAVE_MEMPCPY
+		      *((char *) mempcpy ((__ptr_t) new->name, name, len))
+			= '\0';
+#else
+		      memcpy ((__ptr_t) new->name, name, len);
+		      new->name[len] = '\0';
+#endif
+		      new->next = names;
+		      names = new;
+		      ++nfound;
+		    }
+		}
+	    }
+	}
     }
 
-  if (nfound == 0 && (flags & GLOB_NOMAGIC) &&
-      ! glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE)))
-    flags |= GLOB_NOCHECK;
-
   if (nfound == 0 && (flags & GLOB_NOCHECK))
     {
       size_t len = strlen (pattern);
@@ -850,38 +1365,46 @@
       names->name = (char *) malloc (len + 1);
       if (names->name == NULL)
 	goto memory_error;
+#ifdef HAVE_MEMPCPY
+      *((char *) mempcpy (names->name, pattern, len)) = '\0';
+#else
       memcpy (names->name, pattern, len);
       names->name[len] = '\0';
+#endif
     }
 
-  pglob->gl_pathv
-    = (char **) realloc (pglob->gl_pathv,
-			 (pglob->gl_pathc +
-			  ((flags & GLOB_DOOFFS) ? pglob->gl_offs : 0) +
-			  nfound + 1) *
-			 sizeof (char *));
-  if (pglob->gl_pathv == NULL)
-    goto memory_error;
+  if (nfound != 0)
+    {
+      pglob->gl_pathv
+	= (char **) realloc (pglob->gl_pathv,
+			     (pglob->gl_pathc +
+			      ((flags & GLOB_DOOFFS) ? pglob->gl_offs : 0) +
+			      nfound + 1) *
+			     sizeof (char *));
+      if (pglob->gl_pathv == NULL)
+	goto memory_error;
 
-  if (flags & GLOB_DOOFFS)
-    while (pglob->gl_pathc < pglob->gl_offs)
-      pglob->gl_pathv[pglob->gl_pathc++] = NULL;
+      if (flags & GLOB_DOOFFS)
+	while (pglob->gl_pathc < pglob->gl_offs)
+	  pglob->gl_pathv[pglob->gl_pathc++] = NULL;
 
-  for (; names != NULL; names = names->next)
-    pglob->gl_pathv[pglob->gl_pathc++] = names->name;
-  pglob->gl_pathv[pglob->gl_pathc] = NULL;
+      for (; names != NULL; names = names->next)
+	pglob->gl_pathv[pglob->gl_pathc++] = names->name;
+      pglob->gl_pathv[pglob->gl_pathc] = NULL;
 
-  pglob->gl_flags = flags;
+      pglob->gl_flags = flags;
+    }
 
+  save = errno;
   if (stream != NULL)
     {
-      int save = errno;
       if (flags & GLOB_ALTDIRFUNC)
 	(*pglob->gl_closedir) (stream);
       else
 	closedir ((DIR *) stream);
-      errno = save;
     }
+  __set_errno (save);
+
   return nfound == 0 ? GLOB_NOMATCH : 0;
 
  memory_error:
@@ -891,7 +1414,7 @@
       (*pglob->gl_closedir) (stream);
     else
       closedir ((DIR *) stream);
-    errno = save;
+    __set_errno (save);
   }
   while (names != NULL)
     {
@@ -903,4 +1426,3 @@
 }
 
 #endif	/* Not ELIDE_CODE.  */
-