]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Carry over changes from 2.1 which increase the speed and fix some bugs.
authorUlrich Drepper <drepper@redhat.com>
Wed, 12 Aug 1998 12:30:23 +0000 (12:30 +0000)
committerUlrich Drepper <drepper@redhat.com>
Wed, 12 Aug 1998 12:30:23 +0000 (12:30 +0000)
posix/glob.c

index 36282805efb2f5ee003f3c9ed2bfa67dff07fb5a..5fc094ebe70bdb5b1dd78b06dad53181ca923622 100644 (file)
@@ -155,6 +155,12 @@ extern void abort (), exit ();
 
 #endif /* Standard headers.  */
 
+#ifdef HAVE_GETLOGIN_R
+extern int getlogin_r __P ((char *, size_t));
+#else
+extern char *getlogin __P ((void));
+#endif
+
 #ifndef        ANSI_STRING
 
 # ifndef bzero
@@ -180,7 +186,6 @@ extern void bcopy ();
 # define mempcpy(Dest, Src, Len) __mempcpy (Dest, Src, Len)
 #endif
 
-
 #ifndef        __GNU_LIBRARY__
 # ifdef        __GNUC__
 __inline
@@ -239,6 +244,17 @@ extern char *alloca ();
 # endif
 #endif
 
+#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)
+#endif
+
 #if !(defined STDC_HEADERS || defined __GNU_LIBRARY__)
 # undef        size_t
 # define size_t        unsigned int
@@ -246,21 +262,25 @@ extern char *alloca ();
 
 /* 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>
 \f
 static
@@ -337,7 +357,7 @@ glob (pattern, flags, errfunc, pglob)
      glob_t *pglob;
 {
   const char *filename;
-  char *dirname;
+  const char *dirname;
   size_t dirlen;
   int status;
   int oldcount;
@@ -477,43 +497,98 @@ glob (pattern, flags, errfunc, pglob)
 
   /* 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);
+#if defined __MSDOS__ || defined WINDOWS32
+      if (*filename == ':'
+         || (filename > pattern + 1 && filename[-1] == ':'))
+       {
+         char *drive_spec;
+
+         ++dirlen;
+         drive_spec = (char *) __alloca (dirlen + 1);
 #ifdef HAVE_MEMPCPY
-      *((char *) mempcpy (dirname, pattern, dirlen)) = '\0';
+         *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0';
 #else
-      memcpy (dirname, pattern, dirlen);
-      dirname[dirlen] = '\0';
+         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' && 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 (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))
@@ -525,12 +600,12 @@ glob (pattern, flags, errfunc, pglob)
   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' || dirname[1] == '/')
        {
          /* Look up home directory.  */
-         char *home_dir = getenv ("HOME");
+         const char *home_dir = getenv ("HOME");
 # ifdef _AMIGA
          if (home_dir == NULL || home_dir[0] == '\0')
            home_dir = "SYS:";
@@ -542,37 +617,38 @@ glob (pattern, flags, errfunc, pglob)
          if (home_dir == NULL || home_dir[0] == '\0')
            {
              int success;
+             char *name;
 #   if defined HAVE_GETLOGIN_R || defined _LIBC
-             extern int getlogin_r __P ((char *, size_t));
              size_t buflen = sysconf (_SC_LOGIN_NAME_MAX) + 1;
-             char *name;
 
              if (buflen == 0)
                /* `sysconf' does not support _SC_LOGIN_NAME_MAX.  Try
                   a moderate value.  */
-               buflen = 16;
+               buflen = 20;
              name = (char *) __alloca (buflen);
 
              success = getlogin_r (name, buflen) >= 0;
 #   else
-             extern char *getlogin __P ((void));
-             char *name;
-
              success = (name = getlogin ()) != NULL;
 #   endif
              if (success)
                {
+                 struct passwd *p;
 #   if defined HAVE_GETPWNAM_R || defined _LIBC
                  size_t pwbuflen = sysconf (_SC_GETPW_R_SIZE_MAX);
                  char *pwtmpbuf;
-                 struct passwd pwbuf, *p;
+                 struct passwd pwbuf;
 
+                 if (pwbuflen == -1)
+                   /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.
+                      Try a moderate value.  */
+                   pwbuflen = 1024;
                  pwtmpbuf = (char *) __alloca (pwbuflen);
 
-                 success = (__getpwnam_r (name, &pwbuf, pwtmpbuf,
-                                          pwbuflen, &p) >= 0);
+                 success = (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p)
+                            >= 0);
 #   else
-                 struct passwd *p = getpwnam (name);
+                 p = getpwnam (name);
                  success = p != NULL;
 #   endif
                  if (success)
@@ -580,7 +656,10 @@ glob (pattern, flags, errfunc, pglob)
                }
            }
          if (home_dir == NULL || home_dir[0] == '\0')
-           home_dir = (char *) "~"; /* No luck.  */
+           if (flags & GLOB_TILDE_CHECK)
+             return GLOB_NOMATCH;
+           else
+             home_dir = "~"; /* No luck.  */
 #  endif /* WINDOWS32 */
 # endif
          /* Now construct the full directory.  */
@@ -605,35 +684,45 @@ glob (pattern, flags, errfunc, pglob)
       else
        {
          char *end_name = strchr (dirname, '/');
-         char *user_name;
-         char *home_dir;
+         const char *user_name;
+         const char *home_dir;
 
          if (end_name == NULL)
            user_name = dirname + 1;
          else
            {
-             user_name = (char *) __alloca (end_name - dirname);
+             char *newp;
+             newp = (char *) __alloca (end_name - dirname);
 # ifdef HAVE_MEMPCPY
-             *((char *) mempcpy (user_name, dirname + 1, end_name - dirname))
+             *((char *) mempcpy (newp, dirname + 1, end_name - dirname))
                = '\0';
 # else
-             memcpy (user_name, dirname + 1, end_name - dirname);
-             user_name[end_name - dirname - 1] = '\0';
+             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;
 #  if defined HAVE_GETPWNAM_R || defined _LIBC
            size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
-           char *pwtmpbuf = (char *) __alloca (buflen);
-           struct passwd pwbuf, *p;
-           if (__getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) >= 0)
+           char *pwtmpbuf;
+           struct passwd pwbuf;
+
+           if (buflen == -1)
+             /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.  Try a
+                moderate value.  */
+             buflen = 1024;
+           pwtmpbuf = (char *) __alloca (buflen);
+
+           if (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) >= 0)
              home_dir = p->pw_dir;
            else
              home_dir = NULL;
 #  else
-           struct passwd *p = getpwnam (user_name);
+           p = getpwnam (user_name);
            if (p != NULL)
              home_dir = p->pw_dir;
            else
@@ -657,11 +746,69 @@ glob (pattern, flags, errfunc, pglob)
 #  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.  */
     }
 #endif /* Not VMS.  */
 
+  /* 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
@@ -727,26 +874,29 @@ glob (pattern, flags, errfunc, pglob)
 
       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);
+           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) +
-                                   1 + 1) *
+                                   dirs.gl_pathc + 1) *
                                   sizeof (char *));
            if (pglob->gl_pathv == NULL)
              {
-               free (patcopy);
+               globfree (&dirs);
                return GLOB_NOSPACE;
              }
 
@@ -754,12 +904,55 @@ glob (pattern, flags, errfunc, pglob)
              while (pglob->gl_pathc < pglob->gl_offs)
                pglob->gl_pathv[pglob->gl_pathc++] = NULL;
 
-           pglob->gl_pathv[pglob->gl_pathc++] = patcopy;
+           for (i = 0; i < dirs.gl_pathc; ++i)
+             {
+               const char *dir = dirs.gl_pathv[i];
+               size_t dir_len = strlen (dir);
+
+               /* 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;
+                 }
+
+#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] = 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
     {
@@ -791,10 +984,10 @@ glob (pattern, flags, errfunc, pglob)
       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);
@@ -872,11 +1065,31 @@ prefix_array (dirname, array, n)
 {
   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 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)
     {
@@ -892,12 +1105,12 @@ prefix_array (dirname, array, n)
 #ifdef HAVE_MEMPCPY
       {
        char *endp = (char *) mempcpy (new, dirname, dirlen);
-       *endp++ = '/';
+       *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]);
@@ -959,7 +1172,7 @@ glob_in_dir (pattern, directory, flags, errfunc, pglob)
      int (*errfunc) __P ((const char *, int));
      glob_t *pglob;
 {
-  __ptr_t stream;
+  __ptr_t stream = NULL;
 
   struct globlink
     {
@@ -971,92 +1184,127 @@ glob_in_dir (pattern, directory, flags, errfunc, pglob)
   int meta;
   int save;
 
-  stream = ((flags & GLOB_ALTDIRFUNC) ?
-           (*pglob->gl_opendir) (directory) :
-           (__ptr_t) opendir (directory));
-  if (stream == NULL)
+  meta = __glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));
+  if (meta == 0)
     {
-      if ((errfunc != NULL && (*errfunc) (directory, errno)) ||
-         (flags & GLOB_ERR))
-       return GLOB_ABORTED;
+      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;
-      meta = 0;
-    }
-  else if (pattern[0] == '\0')
-    {
-      /* 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
     {
-      nfound = 0;
-      meta = __glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));
-      if(meta)
-       flags |= GLOB_MAGCHAR;
-
-      while (1)
+      if (pattern[0] == '\0')
        {
-         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;
+         /* 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
+       {
+         stream = ((flags & GLOB_ALTDIRFUNC)
+                   ? (*pglob->gl_opendir) (directory)
+                   : (__ptr_t) opendir (directory));
+         if (stream == NULL)
+           {
+             if ((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
+                                  );
+             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;
+                 /* 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;
+                 name = d->d_name;
 
-         if ((!meta && strcmp (pattern, name) == 0)
-             || fnmatch (pattern, name,
-                         (!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) |
-                         ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
-#ifdef _AMIGA
-                         | 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;
+                 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';
+                     *((char *) mempcpy ((__ptr_t) new->name, name, len))
+                       = '\0';
 #else
-             memcpy ((__ptr_t) new->name, name, len);
-             new->name[len] = '\0';
+                     memcpy ((__ptr_t) new->name, name, len);
+                     new->name[len] = '\0';
 #endif
-             new->next = names;
-             names = new;
-             ++nfound;
-             if (!meta)
-               break;
+                     new->next = names;
+                     names = new;
+                     ++nfound;
+                   }
+               }
            }
        }
     }
 
-  if (nfound == 0 && (flags & GLOB_NOMAGIC) && !meta)
-    flags |= GLOB_NOCHECK;
-
   if (nfound == 0 && (flags & GLOB_NOCHECK))
     {
       size_t len = strlen (pattern);
@@ -1097,10 +1345,11 @@ glob_in_dir (pattern, directory, flags, errfunc, pglob)
     }
 
   save = errno;
-  if (flags & GLOB_ALTDIRFUNC)
-    (*pglob->gl_closedir) (stream);
-  else
-    closedir ((DIR *) stream);
+  if (stream != NULL)
+    if (flags & GLOB_ALTDIRFUNC)
+      (*pglob->gl_closedir) (stream);
+    else
+      closedir ((DIR *) stream);
   __set_errno (save);
 
   return nfound == 0 ? GLOB_NOMATCH : 0;