X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fglob.c;h=1d31d77d0ef64618bf56f4993090a5bc659e7c84;hb=b474f4eb2187f6ae6f504ce9ff5eb11b7ec811bc;hp=7499c5bfd1e946fe263f5535a55d9029d0e6cd87;hpb=2d4c3914efc4bbe03b56573a5aaa69b4c9efd525;p=gnulib.git diff --git a/lib/glob.c b/lib/glob.c index 7499c5bfd..1d31d77d0 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -1,4 +1,5 @@ -/* Copyright (C) 1991-2002, 2003, 2004, 2005 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 @@ -16,7 +17,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#ifdef HAVE_CONFIG_H +#ifndef _LIBC # include #endif @@ -34,13 +35,10 @@ #include /* Needed on stupid SunOS for assert. */ #if !defined _LIBC || !defined GLOB_ONLY_P -#if defined HAVE_UNISTD_H || defined _LIBC -# include -# ifndef POSIX -# ifdef _POSIX_VERSION -# define POSIX -# endif -# endif + +#include +#if !defined POSIX && defined _POSIX_VERSION +# define POSIX #endif #include @@ -50,31 +48,12 @@ # define __set_errno(val) errno = (val) #endif -#if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__ -# include -# define NAMLEN(dirent) strlen((dirent)->d_name) -#else -# define dirent direct -# define NAMLEN(dirent) (dirent)->d_namlen -# ifdef HAVE_SYS_NDIR_H -# include -# endif -# ifdef HAVE_SYS_DIR_H -# include -# endif -# ifdef HAVE_NDIR_H -# include -# endif -# ifdef HAVE_VMSDIR_H -# include "vmsdir.h" -# endif /* HAVE_VMSDIR_H */ -#endif +#include /* In GNU systems, defines this macro for us. */ -#ifdef _D_NAMLEN -# undef NAMLEN -# define NAMLEN(d) _D_NAMLEN(d) +#ifndef _D_EXACT_NAMLEN +# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name) #endif /* When used in the GNU libc the symbol _DIRENT_HAVE_D_TYPE is available @@ -100,13 +79,6 @@ /* If the system has the `struct dirent64' type we use it internally. */ #if defined _LIBC && !defined COMPILE_GLOB64 -# if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__ -# define CONVERT_D_NAMLEN(d64, d32) -# else -# define CONVERT_D_NAMLEN(d64, d32) \ - (d64)->d_namlen = (d32)->d_namlen; -# endif - # if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ # define CONVERT_D_INO(d64, d32) # else @@ -122,8 +94,7 @@ # endif # define CONVERT_DIRENT_DIRENT64(d64, d32) \ - memcpy ((d64)->d_name, (d32)->d_name, NAMLEN (d32) + 1); \ - CONVERT_D_NAMLEN (d64, d32) \ + memcpy ((d64)->d_name, (d32)->d_name, _D_EXACT_NAMLEN (d32) + 1); \ CONVERT_D_INO (d64, d32) \ CONVERT_D_TYPE (d64, d32) #endif @@ -162,11 +133,8 @@ # endif # define struct_stat64 struct stat64 #else /* !_LIBC */ -# include "getlogin_r.h" -# include "mempcpy.h" -# include "stat-macros.h" -# include "strdup.h" # define __stat64(fname, buf) stat (fname, buf) +# define __fxstatat64(_, d, f, st, flag) fstatat (d, f, st, flag) # define struct_stat64 struct stat # define __stat(fname, buf) stat (fname, buf) # define __alloca alloca @@ -175,27 +143,67 @@ # define __glob_pattern_p glob_pattern_p #endif /* _LIBC */ -#include #include +#ifndef _LIBC +# include "dirfd.h" +# include "openat.h" +#endif + #ifdef _SC_GETPW_R_SIZE_MAX # define GETPW_R_SIZE_MAX() sysconf (_SC_GETPW_R_SIZE_MAX) #else # define GETPW_R_SIZE_MAX() (-1) #endif #ifdef _SC_LOGIN_NAME_MAX -# define LOGIN_NAME_MAX() sysconf (_SC_LOGIN_NAME_MAX) +# define GET_LOGIN_NAME_MAX() sysconf (_SC_LOGIN_NAME_MAX) #else -# define LOGIN_NAME_MAX() (-1) +# define GET_LOGIN_NAME_MAX() (-1) #endif static const char *next_brace_sub (const char *begin, int flags) __THROW; #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 + +# 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; @@ -240,15 +248,20 @@ int #ifdef GLOB_ATTRIBUTE GLOB_ATTRIBUTE #endif -glob (const char *pattern, int flags, - int (*errfunc) (const char *, int), - glob_t *pglob) +glob (pattern, flags, errfunc, pglob) + const char * restrict pattern; + int flags; + int (*errfunc) (const char *, int); + glob_t * restrict pglob; { const char *filename; const char *dirname; 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) { @@ -320,7 +333,7 @@ glob (const char *pattern, int flags, next = next_brace_sub (begin + 1, flags); if (next == NULL) { - /* It is an illegal expression. */ + /* It is an invalid expression. */ #ifndef __GNUC__ free (onealt); #endif @@ -334,7 +347,7 @@ glob (const char *pattern, int flags, rest = next_brace_sub (rest + 1, flags); if (rest == NULL) { - /* It is an illegal expression. */ + /* It is an invalid expression. */ #ifndef __GNUC__ free (onealt); #endif @@ -417,6 +430,7 @@ glob (const char *pattern, int flags, 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 @@ -442,9 +456,11 @@ glob (const char *pattern, int flags, 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; @@ -485,10 +501,33 @@ glob (const char *pattern, int flags, && 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; } } @@ -512,10 +551,11 @@ glob (const char *pattern, int flags, oldcount = pglob->gl_pathc + pglob->gl_offs; -#ifndef VMS if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~') { - if (dirname[1] == '\0' || dirname[1] == '/') + if (dirname[1] == '\0' || dirname[1] == '/' + || (!(flags & GLOB_NOESCAPE) && dirname[1] == '\\' + && (dirname[2] == '\0' || dirname[2] == '/'))) { /* Look up home directory. */ const char *home_dir = getenv ("HOME"); @@ -531,7 +571,7 @@ glob (const char *pattern, int flags, { int success; char *name; - size_t buflen = LOGIN_NAME_MAX() + 1; + size_t buflen = GET_LOGIN_NAME_MAX () + 1; if (buflen == 0) /* `sysconf' does not support _SC_LOGIN_NAME_MAX. Try @@ -592,7 +632,10 @@ glob (const char *pattern, int flags, # endif /* Now construct the full directory. */ if (dirname[1] == '\0') - dirname = home_dir; + { + dirname = home_dir; + dirlen = strlen (dirname); + } else { char *newp; @@ -601,7 +644,9 @@ glob (const char *pattern, int flags, 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 @@ -609,7 +654,19 @@ glob (const char *pattern, int flags, 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 @@ -618,6 +675,33 @@ glob (const char *pattern, int flags, 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; } @@ -671,6 +755,8 @@ glob (const char *pattern, int flags, *((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) @@ -680,7 +766,6 @@ glob (const char *pattern, int flags, } # 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. */ @@ -691,7 +776,7 @@ glob (const char *pattern, int flags, /* 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))))) @@ -711,9 +796,22 @@ glob (const char *pattern, int flags, } 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; @@ -725,15 +823,31 @@ glob (const char *pattern, int flags, 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. */ @@ -745,12 +859,16 @@ glob (const char *pattern, int flags, } 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, @@ -808,6 +926,7 @@ glob (const char *pattern, int flags, 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) { @@ -850,10 +969,44 @@ glob (const char *pattern, int flags, 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) { @@ -869,6 +1022,33 @@ glob (const char *pattern, int flags, } } + 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. */ @@ -888,7 +1068,8 @@ libc_hidden_def (glob) /* Free storage allocated in PGLOB by a previous `glob' call. */ void -globfree (register glob_t *pglob) +globfree (pglob) + register glob_t *pglob; { if (pglob->gl_pathv != NULL) { @@ -909,8 +1090,8 @@ libc_hidden_def (globfree) static int collated_compare (const void *a, const void *b) { - const char *const s1 = *(const char *const * const) a; - const char *const s2 = *(const char *const * const) b; + char *const *ps1 = a; char *s1 = *ps1; + char *const *ps2 = b; char *s2 = *ps2; if (s1 == s2) return 0; @@ -983,13 +1164,13 @@ prefix_array (const char *dirname, char **array, size_t n) /* We must not compile this function twice. */ #if !defined _LIBC || !defined NO_GLOB_PATTERN_P -/* Return nonzero if PATTERN contains any metacharacters. - Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ int -__glob_pattern_p (const char *pattern, int 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) @@ -999,21 +1180,35 @@ __glob_pattern_p (const char *pattern, int quote) return 1; case '\\': - if (quote && p[1] != '\0') - ++p; + if (quote) + { + if (p[1] != '\0') + ++p; + ret |= 2; + } break; case '[': - open = 1; + ret |= 4; break; case ']': - if (open) + if (ret & 4) return 1; break; } - return 0; + return ret; +} + +/* Return nonzero if PATTERN contains any metacharacters. + Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ +int +__glob_pattern_p (pattern, quote) + const char *pattern; + int quote; +{ + return __glob_pattern_type (pattern, quote) == 1; } # ifdef _LIBC weak_alias (__glob_pattern_p, glob_pattern_p) @@ -1026,21 +1221,33 @@ weak_alias (__glob_pattern_p, glob_pattern_p) /* 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) { size_t fnamelen = strlen (fname); char *fullname = __alloca (dirlen + 1 + fnamelen + 1); struct stat st; - struct_stat64 st64; 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)); + return (*pglob->gl_stat) (fullname, &st) == 0; +} + +/* Return true if DIR/FNAME exists. */ +static int +link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname, + glob_t *pglob, int flags) +{ + if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) + return link_exists2_p (dir, dirlen, fname, pglob); + else + { + struct_stat64 st64; + return __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0) == 0; + } } #endif @@ -1056,27 +1263,35 @@ glob_in_dir (const char *pattern, const char *directory, int flags, { 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. */ @@ -1088,134 +1303,115 @@ glob_in_dir (const char *pattern, const char *directory, int flags, 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) + int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? -1 : dirfd ((DIR *) stream)); + int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) + | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) #if defined _AMIGA || defined VMS - | FNM_CASEFOLD + | FNM_CASEFOLD #endif - ); - nfound = 0; - flags |= GLOB_MAGCHAR; + ); + flags |= GLOB_MAGCHAR; - while (1) - { - const char *name; - size_t len; + while (1) + { + const char *name; + size_t len; #if defined _LIBC && !defined COMPILE_GLOB64 - struct dirent64 *d; - union - { - struct dirent64 d64; - char room [offsetof (struct dirent64, d_name[0]) - + NAME_MAX + 1]; - } - d64buf; + struct dirent64 *d; + union + { + struct dirent64 d64; + char room [offsetof (struct dirent64, d_name[0]) + + NAME_MAX + 1]; + } + d64buf; - if (flags & GLOB_ALTDIRFUNC) + if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) + { + struct dirent *d32 = (*pglob->gl_readdir) (stream); + if (d32 != NULL) { - struct dirent *d32 = (*pglob->gl_readdir) (stream); - if (d32 != NULL) - { - CONVERT_DIRENT_DIRENT64 (&d64buf.d64, d32); - d = &d64buf.d64; - } - else - d = NULL; + CONVERT_DIRENT_DIRENT64 (&d64buf.d64, d32); + d = &d64buf.d64; } else - d = __readdir64 (stream); + d = NULL; + } + else + d = __readdir64 (stream); #else - struct dirent *d = ((flags & GLOB_ALTDIRFUNC) - ? ((*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 = 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; } } } @@ -1226,59 +1422,89 @@ glob_in_dir (const char *pattern, const char *directory, int flags, { 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 *)); + result = 0; + if (new_gl_pathv == NULL) - goto memory_error; - pglob->gl_pathv = new_gl_pathv; + { + 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_pathv = new_gl_pathv; - 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; + pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; - pglob->gl_flags = flags; + 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; }