/* Copyright (C) 1991-2002,2003,2004,2005,2006 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 modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #undef ENABLE_GLOB_BRACE_EXPANSION #undef ENABLE_GLOB_TILDE_EXPANSION #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_GLOB_TILDE_EXPANSION #include #endif #ifdef COMPILE_GLOB64 #undef stat #define stat stat64 #define struct_stat64 struct stat64 #define __stat64(fname, buf) stat64 (fname, buf) #define dirent dirent64 #define __readdir readdir64 #define __readdir64 readdir64 #define glob_t glob64_t #define glob(pattern, flags, errfunc, pglob) glob64 (pattern, flags, errfunc, pglob) #define globfree(pglob) globfree64 (pglob) #else #define __readdir readdir #ifdef __UCLIBC_HAS_LFS__ #define __readdir64 readdir64 #else #define __readdir64 readdir #endif #define struct_stat64 struct stat #define __stat64(fname, buf) stat (fname, buf) #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. HAVE_STRUCT_DIRENT_D_TYPE plays the same role in GNULIB. */ #if defined _DIRENT_HAVE_D_TYPE /* True if the directory entry D must be of type T. */ # define DIRENT_MUST_BE(d, t) ((d)->d_type == (t)) /* True if the directory entry D might be a symbolic link. */ # define DIRENT_MIGHT_BE_SYMLINK(d) \ ((d)->d_type == DT_UNKNOWN || (d)->d_type == DT_LNK) /* True if the directory entry D might be a directory. */ # define DIRENT_MIGHT_BE_DIR(d) \ ((d)->d_type == DT_DIR || DIRENT_MIGHT_BE_SYMLINK (d)) #else /* !HAVE_D_TYPE */ # define DIRENT_MUST_BE(d, t) false # define DIRENT_MIGHT_BE_SYMLINK(d) true # define DIRENT_MIGHT_BE_DIR(d) true #endif /* HAVE_D_TYPE */ # define NAMLEN(dirent) strlen((dirent)->d_name) #ifdef _D_NAMLEN # undef NAMLEN # define NAMLEN(d) _D_NAMLEN(d) #endif # if defined _DIRENT_HAVE_D_NAMLEN # define CONVERT_D_NAMLEN(d64, d32) (d64)->d_namlen = (d32)->d_namlen; # else # define CONVERT_D_NAMLEN(d64, d32) # endif # define CONVERT_D_INO(d64, d32) (d64)->d_ino = (d32)->d_ino; # ifdef _DIRENT_HAVE_D_TYPE # define CONVERT_D_TYPE(d64, d32) (d64)->d_type = (d32)->d_type; # else # define CONVERT_D_TYPE(d64, d32) # endif # define CONVERT_DIRENT_DIRENT64(d64, d32) \ memcpy ((d64)->d_name, (d32)->d_name, NAMLEN (d32) + 1); \ CONVERT_D_NAMLEN (d64, d32) \ CONVERT_D_INO (d64, d32) \ CONVERT_D_TYPE (d64, d32) extern int __collated_compare (const void *a, const void *b) attribute_hidden; extern int __prefix_array (const char *dirname, char **array, size_t n) attribute_hidden; #if defined ENABLE_GLOB_BRACE_EXPANSION extern const char *__next_brace_sub (const char *cp, int flags) attribute_hidden; #endif #ifndef COMPILE_GLOB64 /* 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) { register const char *p; int open = 0; for (p = pattern; *p != '\0'; ++p) switch (*p) { case '?': case '*': return 1; case '\\': if (quote && p[1] != '\0') ++p; break; case '[': open = 1; break; case ']': if (open) return 1; break; } return 0; } libc_hidden_def(glob_pattern_p) /* Do a collated comparison of A and B. */ 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; if (s1 == s2) return 0; if (s1 == NULL) return 1; if (s2 == NULL) return -1; return strcoll (s1, s2); } /* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's elements in place. Return nonzero if out of memory, zero if successful. A slash is inserted between DIRNAME and each elt of ARRAY, unless DIRNAME is just "/". Each old element of ARRAY is freed. If ADD_SLASH is non-zero, allocate one character more than necessary, so that a slash can be appended later. */ int __prefix_array (const char *dirname, char **array, size_t n) { register size_t i; size_t dirlen = strlen (dirname); # define DIRSEP_CHAR '/' 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; for (i = 0; i < n; ++i) { size_t eltlen = strlen (array[i]) + 1; char *new = (char *) malloc (dirlen + 1 + eltlen); if (new == NULL) { while (i > 0) free (array[--i]); return 1; } { char *endp = mempcpy (new, dirname, dirlen); *endp++ = DIRSEP_CHAR; mempcpy (endp, array[i], eltlen); } free (array[i]); array[i] = new; } return 0; } #if defined ENABLE_GLOB_BRACE_EXPANSION /* Find the end of the sub-pattern in a brace expression. */ const char * __next_brace_sub (const char *cp, int flags) { unsigned int depth = 0; while (*cp != '\0') if ((flags & GLOB_NOESCAPE) == 0 && *cp == '\\') { if (*++cp == '\0') break; ++cp; } else { if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) break; if (*cp++ == '{') depth++; } return *cp != '\0' ? cp : NULL; } #endif #endif static int link_exists_p (const char *dir, size_t dirlen, const char *fname, glob_t *pglob, int flags) { size_t fnamelen = strlen (fname); char *fullname = (char *) 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) : __stat64 (fullname, &st64)) == 0); } /* Like `glob', but PATTERN is a final pathname component, and matches are searched for in DIRECTORY. The GLOB_NOSORT bit in FLAGS is ignored. No sorting is ever done. The GLOB_APPEND flag is assumed to be set (always appends). */ static int glob_in_dir (const char *pattern, const char *directory, int flags, int (*errfunc) (const char *, int), glob_t *pglob) { size_t dirlen = strlen (directory); void *stream = NULL; struct globlink { struct globlink *next; char *name; }; struct globlink *names = NULL; size_t nfound; int meta; int save; meta = glob_pattern_p (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)) { /* Since we use the normal file functions we can also use stat() to verify the file is there. */ struct stat st; struct_stat64 st64; size_t patlen = strlen (pattern); char *fullname = (char *) alloca (dirlen + 1 + patlen + 1); mempcpy (mempcpy (mempcpy (fullname, directory, dirlen), "/", 1), pattern, patlen + 1); if (((flags & GLOB_ALTDIRFUNC) ? (*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') { /* 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) : 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) ); nfound = 0; flags |= GLOB_MAGCHAR; while (1) { const char *name; size_t len; #if defined __UCLIBC_HAS_LFS__ && !defined COMPILE_GLOB64 struct dirent64 *d; union { struct dirent64 d64; char room [offsetof (struct dirent64, d_name[0]) + NAME_MAX + 1]; } d64buf; if (flags & GLOB_ALTDIRFUNC) { struct dirent *d32 = (*pglob->gl_readdir) (stream); if (d32 != NULL) { CONVERT_DIRENT_DIRENT64 (&d64buf.d64, d32); d = &d64buf.d64; } else d = NULL; } else d = __readdir64 (stream); #else struct dirent *d = ((flags & GLOB_ALTDIRFUNC) ? ((struct dirent *) (*pglob->gl_readdir) (stream)) : __readdir (stream)); #endif if (d == NULL) break; # define REAL_DIR_ENTRY(dp) (dp->d_ino != 0) 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; name = d->d_name; 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 (directory, dirlen, name, pglob, flags)) { 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; *((char *) mempcpy (new->name, name, len)) = '\0'; new->next = names; names = new; ++nfound; } } } } } } if (nfound == 0 && (flags & GLOB_NOCHECK)) { size_t len = strlen (pattern); nfound = 1; names = (struct globlink *) alloca (sizeof (struct globlink)); names->next = NULL; names->name = (char *) malloc (len + 1); if (names->name == NULL) goto memory_error; *((char *) mempcpy (names->name, pattern, len)) = '\0'; } if (nfound != 0) { char **new_gl_pathv; new_gl_pathv = (char **) realloc (pglob->gl_pathv, (pglob->gl_pathc + pglob->gl_offs + nfound + 1) * sizeof (char *)); if (new_gl_pathv == NULL) goto memory_error; pglob->gl_pathv = new_gl_pathv; 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_flags = flags; } save = errno; if (stream != NULL) { if (flags & GLOB_ALTDIRFUNC) (*pglob->gl_closedir) (stream); else closedir (stream); } __set_errno (save); return nfound == 0 ? GLOB_NOMATCH : 0; memory_error: { int save2 = errno; if (flags & GLOB_ALTDIRFUNC) (*pglob->gl_closedir) (stream); else closedir (stream); __set_errno (save2); } while (names != NULL) { free (names->name); names = names->next; } return GLOB_NOSPACE; } /* 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. If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned. Otherwise, `glob' returns zero. */ int glob ( const char *pattern, int flags, int (*errfunc) (const char *, int), glob_t *pglob) { const char *filename; const char *dirname; size_t dirlen; int status; size_t oldcount; if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) { __set_errno (EINVAL); return -1; } if (!(flags & GLOB_DOOFFS)) /* Have to do this so `globfree' knows where to start freeing. It also makes all the code that uses gl_offs simpler. */ pglob->gl_offs = 0; #if defined ENABLE_GLOB_BRACE_EXPANSION if (flags & GLOB_BRACE) { const char *begin; if (flags & GLOB_NOESCAPE) begin = strchr (pattern, '{'); else { begin = pattern; while (1) { if (*begin == '\0') { begin = NULL; break; } if (*begin == '\\' && begin[1] != '\0') ++begin; else if (*begin == '{') break; ++begin; } } if (begin != NULL) { /* Allocate working buffer large enough for our work. Note that we have at least an opening and closing brace. */ size_t firstc; char *alt_start; const char *p; const char *next; const char *rest; size_t rest_len; char onealt[strlen (pattern) - 1]; /* We know the prefix for all sub-patterns. */ alt_start = mempcpy (onealt, pattern, begin - pattern); /* Find the first sub-pattern and at the same time find the rest after the closing brace. */ next = __next_brace_sub (begin + 1, flags); if (next == NULL) { /* It is an illegal expression. */ 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, flags); if (rest == NULL) { /* It is an illegal expression. */ return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob); } } /* 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 points past the final }. We will accumulate result names from recursive runs for each brace alternative in the buffer using GLOB_APPEND. */ if (!(flags & GLOB_APPEND)) { /* This call is to set a new vector, so clear out the vector so we can append to it. */ pglob->gl_pathc = 0; pglob->gl_pathv = NULL; } firstc = pglob->gl_pathc; p = begin + 1; while (1) { int result; /* Construct the new glob expression. */ mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len); result = glob (onealt, ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC)) | GLOB_APPEND), errfunc, pglob); /* If we got an error, return it. */ if (result && result != GLOB_NOMATCH) { if (!(flags & GLOB_APPEND)) { globfree (pglob); pglob->gl_pathc = 0; } return result; } if (*next == '}') /* We saw the last entry. */ break; p = next + 1; next = __next_brace_sub (p, flags); /* assert (next != NULL); */ } if (pglob->gl_pathc != firstc) /* We found some entries. */ return 0; else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) return GLOB_NOMATCH; } } #endif /* Find the filename. */ filename = strrchr (pattern, '/'); if (filename == NULL) { /* This can mean two things: a simple name or "~name". The latter case is nothing but a notation for a directory. */ if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~') { dirname = pattern; 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; dirname = "."; dirlen = 0; } } else if (filename == pattern) { /* "/pattern". */ dirname = "/"; dirlen = 1; ++filename; } else { char *newp; dirlen = filename - pattern; newp = (char *) alloca (dirlen + 1); *((char *) mempcpy (newp, pattern, dirlen)) = '\0'; 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 (!(flags & GLOB_APPEND)) { pglob->gl_pathc = 0; if (!(flags & GLOB_DOOFFS)) pglob->gl_pathv = NULL; else { size_t i; pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1) * sizeof (char *)); if (pglob->gl_pathv == NULL) return GLOB_NOSPACE; for (i = 0; i <= pglob->gl_offs; ++i) pglob->gl_pathv[i] = NULL; } } oldcount = pglob->gl_pathc + pglob->gl_offs; #if defined ENABLE_GLOB_TILDE_EXPANSION if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~') { if (dirname[1] == '\0' || dirname[1] == '/') { /* Look up home directory. */ const char *home_dir = getenv ("HOME"); if (home_dir == NULL || home_dir[0] == '\0') { int success; char *name; # define GET_LOGIN_NAME_MAX() sysconf (_SC_LOGIN_NAME_MAX) size_t buflen = GET_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; if (success) { struct passwd *p; # define GETPW_R_SIZE_MAX() sysconf (_SC_GETPW_R_SIZE_MAX) long int pwbuflen = GETPW_R_SIZE_MAX (); char *pwtmpbuf; struct passwd pwbuf; int save = errno; pwtmpbuf = (char *) alloca (pwbuflen); while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p) != 0) { if (errno != ERANGE) { p = NULL; break; } pwtmpbuf = extend_alloca (pwtmpbuf, pwbuflen, 2 * pwbuflen); __set_errno (save); } if (p != NULL) home_dir = p->pw_dir; } } if (home_dir == NULL || home_dir[0] == '\0') { if (flags & GLOB_TILDE_CHECK) return GLOB_NOMATCH; else home_dir = "~"; /* No luck. */ } /* 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); mempcpy (mempcpy (newp, home_dir, home_len), &dirname[1], dirlen); dirname = newp; } } 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); *((char *) mempcpy (newp, dirname + 1, end_name - dirname)) = '\0'; user_name = newp; } /* Look up specific user's home directory. */ { struct passwd *p; long int buflen = GETPW_R_SIZE_MAX (); char *pwtmpbuf; struct passwd pwbuf; int save = errno; pwtmpbuf = (char *) alloca (buflen); while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0) { if (errno != ERANGE) { p = NULL; break; } pwtmpbuf = extend_alloca (pwtmpbuf, buflen, 2 * buflen); __set_errno (save); } 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); *((char *) mempcpy (mempcpy (newp, home_dir, home_len), end_name, rest_len)) = '\0'; dirname = newp; } else if (flags & GLOB_TILDE_CHECK) /* We have to regard it as an error if we cannot find the home directory. */ return GLOB_NOMATCH; } } /* Now test whether we looked for "~" or "~NAME". In this case we can give the answer now. */ if (filename == NULL) { struct stat st; struct_stat64 st64; /* 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) == 0 && S_ISDIR (st.st_mode)) : (__stat64 (dirname, &st64) == 0 && S_ISDIR (st64.st_mode))))) { int newcount = pglob->gl_pathc + pglob->gl_offs; char **new_gl_pathv; new_gl_pathv = (char **) realloc (pglob->gl_pathv, (newcount + 1 + 1) * sizeof (char *)); if (new_gl_pathv == NULL) { nospace: free (pglob->gl_pathv); pglob->gl_pathv = NULL; pglob->gl_pathc = 0; return GLOB_NOSPACE; } pglob->gl_pathv = new_gl_pathv; pglob->gl_pathv[newcount] = strdup (dirname); if (pglob->gl_pathv[newcount] == NULL) goto nospace; pglob->gl_pathv[++newcount] = NULL; ++pglob->gl_pathc; pglob->gl_flags = flags; return 0; } /* Not found. */ return GLOB_NOMATCH; } #endif 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 the pattern in each directory found. */ glob_t dirs; size_t i; if ((flags & GLOB_ALTDIRFUNC) != 0) { /* Use the alternative access functions also in the recursive call. */ dirs.gl_opendir = pglob->gl_opendir; dirs.gl_readdir = pglob->gl_readdir; dirs.gl_closedir = pglob->gl_closedir; dirs.gl_stat = pglob->gl_stat; dirs.gl_lstat = pglob->gl_lstat; } status = glob (dirname, ((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ALTDIRFUNC)) | GLOB_NOSORT | GLOB_ONLYDIR), errfunc, &dirs); if (status != 0) return status; /* We have successfully globbed the preceding directory name. For each name we found, call glob_in_dir on it and FILENAME, appending the results to PGLOB. */ for (i = 0; i < dirs.gl_pathc; ++i) { int old_pathc; old_pathc = pglob->gl_pathc; status = glob_in_dir (filename, dirs.gl_pathv[i], ((flags | GLOB_APPEND) & ~(GLOB_NOCHECK | GLOB_NOMAGIC)), errfunc, pglob); if (status == GLOB_NOMATCH) /* No matches in this directory. Try the next. */ continue; if (status != 0) { globfree (&dirs); globfree (pglob); pglob->gl_pathc = 0; return status; } /* Stick the directory on the front of each name. */ if (__prefix_array (dirs.gl_pathv[i], &pglob->gl_pathv[old_pathc + pglob->gl_offs], pglob->gl_pathc - old_pathc)) { globfree (&dirs); globfree (pglob); pglob->gl_pathc = 0; return GLOB_NOSPACE; } } 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 the GLOB_NOCHECK flag was set we must return the input pattern itself. */ if (pglob->gl_pathc + pglob->gl_offs == oldcount) { /* No matches. */ if (flags & GLOB_NOCHECK) { int newcount = pglob->gl_pathc + pglob->gl_offs; char **new_gl_pathv; new_gl_pathv = (char **) realloc (pglob->gl_pathv, (newcount + 2) * sizeof (char *)); if (new_gl_pathv == NULL) { globfree (&dirs); return GLOB_NOSPACE; } pglob->gl_pathv = new_gl_pathv; pglob->gl_pathv[newcount] = strdup (pattern); if (pglob->gl_pathv[newcount] == NULL) { globfree (&dirs); globfree (pglob); pglob->gl_pathc = 0; return GLOB_NOSPACE; } ++pglob->gl_pathc; ++newcount; pglob->gl_pathv[newcount] = NULL; pglob->gl_flags = flags; } else { globfree (&dirs); return GLOB_NOMATCH; } } globfree (&dirs); } else { int old_pathc = pglob->gl_pathc; status = glob_in_dir (filename, dirname, flags, errfunc, pglob); if (status != 0) return status; if (dirlen > 0) { /* Stick the directory on the front of each name. */ if (__prefix_array (dirname, &pglob->gl_pathv[old_pathc + pglob->gl_offs], pglob->gl_pathc - old_pathc)) { globfree (pglob); pglob->gl_pathc = 0; return GLOB_NOSPACE; } } } 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 (((flags & GLOB_ALTDIRFUNC) ? ((*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. */ qsort (&pglob->gl_pathv[oldcount], pglob->gl_pathc + pglob->gl_offs - oldcount, sizeof (char *), __collated_compare); } return 0; } #ifdef COMPILE_GLOB64 libc_hidden_def(glob64) #else libc_hidden_def(glob) #endif /* Free storage allocated in PGLOB by a previous `glob' call. */ void globfree (register glob_t *pglob) { if (pglob->gl_pathv != NULL) { size_t i; for (i = 0; i < pglob->gl_pathc; ++i) if (pglob->gl_pathv[pglob->gl_offs + i] != NULL) free (pglob->gl_pathv[pglob->gl_offs + i]); free (pglob->gl_pathv); pglob->gl_pathv = NULL; } } #ifdef COMPILE_GLOB64 libc_hidden_def(globfree64) #else libc_hidden_def(globfree) #endif