diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2008-03-09 22:35:38 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2008-03-09 22:35:38 +0100 |
commit | 187847b2b483f37f3f595af576c362c4e9d4c288 (patch) | |
tree | a1040fe6e189196e1247b032205112af71490f7e /lib | |
parent | 2209ae02ba97e7c349e80af8fa6c5946d4510f84 (diff) | |
download | barebox-187847b2b483f37f3f595af576c362c4e9d4c288.tar.gz barebox-187847b2b483f37f3f595af576c362c4e9d4c288.tar.xz |
add globbing support
Diffstat (limited to 'lib')
-rw-r--r-- | lib/fnmatch.c | 228 | ||||
-rw-r--r-- | lib/glob.c | 471 |
2 files changed, 699 insertions, 0 deletions
diff --git a/lib/fnmatch.c b/lib/fnmatch.c new file mode 100644 index 0000000000..86968bc9c4 --- /dev/null +++ b/lib/fnmatch.c @@ -0,0 +1,228 @@ +/* Copyright (C) 1991, 1992, 1993, 1996 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 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., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +/* Enable GNU extensions in fnmatch.h. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#include <errno.h> +#include <fnmatch.h> +#include <linux/ctype.h> + + +/* 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 + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +# if defined (STDC_HEADERS) || !defined (isascii) +# define ISASCII(c) 1 +# else +# define ISASCII(c) isascii(c) +# endif + +# define ISUPPER(c) (ISASCII (c) && isupper (c)) + + +# ifndef errno +extern int errno; +# endif + +/* Match STRING against the filename pattern PATTERN, returning zero if + it matches, nonzero if not. */ +int fnmatch(pattern, string, flags) +const char *pattern; +const char *string; +int flags; +{ + register const char *p = pattern, *n = string; + register char c; + +/* Note that this evaluates C many times. */ +# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c)) + + while ((c = *p++) != '\0') { + c = FOLD(c); + + switch (c) { + case '?': + if (*n == '\0') + return FNM_NOMATCH; + else if ((flags & FNM_FILE_NAME) && *n == '/') + return FNM_NOMATCH; + else if ((flags & FNM_PERIOD) && *n == '.' && + (n == string + || ((flags & FNM_FILE_NAME) + && n[-1] == '/'))) return FNM_NOMATCH; + break; + + case '\\': + if (!(flags & FNM_NOESCAPE)) { + c = *p++; + if (c == '\0') + /* Trailing \ loses. */ + return FNM_NOMATCH; + c = FOLD(c); + } + if (FOLD(*n) != c) + return FNM_NOMATCH; + break; + + case '*': + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + for (c = *p++; c == '?' || c == '*'; c = *p++) { + if ((flags & FNM_FILE_NAME) && *n == '/') + /* A slash does not match a wildcard under FNM_FILE_NAME. */ + return FNM_NOMATCH; + else if (c == '?') { + /* A ? needs to match one character. */ + if (*n == '\0') + /* There isn't another character; no match. */ + return FNM_NOMATCH; + else + /* One character of the string is consumed in matching + this ? wildcard, so *??? won't match if there are + less than three characters. */ + ++n; + } + } + + if (c == '\0') + return 0; + + { + char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c; + + c1 = FOLD(c1); + for (--p; *n != '\0'; ++n) + if ((c == '[' || FOLD(*n) == c1) && + fnmatch(p, n, flags & ~FNM_PERIOD) == 0) + return 0; + return FNM_NOMATCH; + } + + case '[': + { + /* Nonzero if the sense of the character class is inverted. */ + register int not; + + if (*n == '\0') + return FNM_NOMATCH; + + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + not = (*p == '!' || *p == '^'); + if (not) + ++p; + + c = *p++; + for (;;) { + register char cstart = c, cend = c; + + if (!(flags & FNM_NOESCAPE) && c == '\\') { + if (*p == '\0') + return FNM_NOMATCH; + cstart = cend = *p++; + } + + cstart = cend = FOLD(cstart); + + if (c == '\0') + /* [ (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + c = FOLD(c); + + if ((flags & FNM_FILE_NAME) && c == '/') + /* [/] can never match. */ + return FNM_NOMATCH; + + if (c == '-' && *p != ']') { + cend = *p++; + if (!(flags & FNM_NOESCAPE) && cend == '\\') + cend = *p++; + if (cend == '\0') + return FNM_NOMATCH; + cend = FOLD(cend); + + c = *p++; + } + + if (FOLD(*n) >= cstart && FOLD(*n) <= cend) + goto matched; + + if (c == ']') + break; + } + if (!not) + return FNM_NOMATCH; + break; + + matched:; + /* Skip the rest of the [...] that already matched. */ + while (c != ']') { + if (c == '\0') + /* [... (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + if (!(flags & FNM_NOESCAPE) && c == '\\') { + if (*p == '\0') + return FNM_NOMATCH; + /* XXX 1003.2d11 is unclear if this is right. */ + ++p; + } + } + if (not) + return FNM_NOMATCH; + } + break; + + default: + if (c != FOLD(*n)) + return FNM_NOMATCH; + } + + ++n; + } + + if (*n == '\0') + return 0; + + if ((flags & FNM_LEADING_DIR) && *n == '/') + /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ + return 0; + + return FNM_NOMATCH; + +# undef FOLD +} + diff --git a/lib/glob.c b/lib/glob.c new file mode 100644 index 0000000000..a5e3d1d67a --- /dev/null +++ b/lib/glob.c @@ -0,0 +1,471 @@ +/* Copyright (C) 1991, 1992, 1993, 1994, 1995 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 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., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <common.h> +#include <errno.h> +#include <fs.h> +#include <linux/stat.h> +#include <malloc.h> +#include <xfuncs.h> +#include <fnmatch.h> +#define _GNU_SOURCE +#include <glob.h> + +#ifdef CONFIG_GLOB + +extern __ptr_t(*__glob_opendir_hook) __P((const char *directory)); +extern void (*__glob_closedir_hook) __P((__ptr_t stream)); +extern const char *(*__glob_readdir_hook) __P((__ptr_t stream)); + +static int glob_in_dir __P((const char *pattern, const char *directory, + int flags, + int (*errfunc) __P((const char *, int)), + glob_t * pglob)); +static int prefix_array __P((const char *prefix, char **array, size_t n, + int add_slash)); + +#ifdef __GLOB64 +extern int glob_pattern_p(const char *pattern, int quote); +#else +/* 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) +{ + 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; +} +#endif + +#ifdef CONFIG_GLOB_SORT +/* Do a collated comparison of A and B. */ +static int collated_compare(a, b) +const __ptr_t a; +const __ptr_t b; +{ + const char *const s1 = *(const char *const *)a; + const char *const s2 = *(const char *const *)b; + + if (s1 == s2) + return 0; + if (s1 == NULL) + return 1; + if (s2 == NULL) + return -1; + return strcmp(s1, s2); +} +#endif + +/* 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(pattern, flags, errfunc, pglob) +const char *pattern; +int flags; +int (*errfunc) __P((const char *, int)); +glob_t *pglob; +{ + const char *filename; + char *dirname = NULL; + size_t dirlen; + int status; + int oldcount; + + if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) { + errno = EINVAL; + return -1; + } + + /* Find the filename. */ + filename = strrchr(pattern, '/'); + if (filename == NULL) { + filename = pattern; + dirname = strdup("."); + dirlen = 0; + } else if (filename == pattern) { + /* "/pattern". */ + dirname = strdup("/"); + dirlen = 1; + ++filename; + } else { + dirlen = filename - pattern; + dirname = (char *)xmalloc(dirlen + 1); + memcpy(dirname, pattern, dirlen); + dirname[dirlen] = '\0'; + ++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); + status = val; + goto out; + } + + if (!(flags & GLOB_APPEND)) { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + } + + oldcount = pglob->gl_pathc; + + 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; + register int i; + status = glob(dirname, + ((flags & + (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE)) | + GLOB_NOSORT), errfunc, &dirs); + if (status != 0) + goto out; + + /* 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 oldcount; + +#ifdef SHELL + { + /* Make globbing interruptible in the bash shell. */ + extern int interrupt_state; + + if (interrupt_state) { + globfree(&dirs); + globfree(&files); + status = GLOB_ABEND goto out; + } + } +#endif /* SHELL. */ + + oldcount = pglob->gl_pathc; + status = glob_in_dir(filename, dirs.gl_pathv[i], + (flags | GLOB_APPEND) & + ~GLOB_NOCHECK, errfunc, pglob); + if (status == GLOB_NOMATCH) { + /* No matches in this directory. Try the next. */ + continue; + } + + if (status != 0) { + globfree(pglob); + goto out; + } + + /* Stick the directory on the front of each name. */ + prefix_array(dirs.gl_pathv[i], + &pglob->gl_pathv[oldcount], + pglob->gl_pathc - oldcount, + flags & GLOB_MARK); + } + + globfree(&dirs); + flags |= GLOB_MAGCHAR; + + if (pglob->gl_pathc == oldcount) { + /* No matches. */ +#ifdef CONFIG_GLOB_NOCHECK + if (flags & GLOB_NOCHECK) { + size_t len = strlen(pattern) + 1; + char *patcopy = (char *)xmalloc(len); + memcpy(patcopy, pattern, len); + + pglob->gl_pathv + = (char **)xrealloc(pglob->gl_pathv, + (pglob->gl_pathc + + ((flags & GLOB_DOOFFS) ? + pglob->gl_offs : 0) + + 1 + 1) * + sizeof(char *)); + + if (flags & GLOB_DOOFFS) + while (pglob->gl_pathc < pglob->gl_offs) + pglob->gl_pathv[pglob-> + gl_pathc++] = + NULL; + + pglob->gl_pathv[pglob->gl_pathc++] = patcopy; + pglob->gl_pathv[pglob->gl_pathc] = NULL; + pglob->gl_flags = flags; + } else +#endif + { + status = GLOB_NOMATCH; + goto out; + } + } + } else { + status = glob_in_dir(filename, dirname, flags, errfunc, pglob); + if (status != 0) + goto out; + + if (dirlen > 0) { + /* Stick the directory on the front of each name. */ + prefix_array(dirname, + &pglob->gl_pathv[oldcount], + pglob->gl_pathc - oldcount, + flags & GLOB_MARK); + } + } + + if (flags & GLOB_MARK) { + /* Append slashes to directory names. glob_in_dir has already + allocated the extra character for us. */ + int i; + struct stat st; + for (i = oldcount; i < pglob->gl_pathc; ++i) + if (stat(pglob->gl_pathv[i], &st) == 0 && + S_ISDIR(st.st_mode)) + strcat(pglob->gl_pathv[i], "/"); + } +#ifdef CONFIG_GLOB_SORT + if (!(flags & GLOB_NOSORT)) + /* Sort the vector. */ + qsort((__ptr_t) & pglob->gl_pathv[oldcount], + pglob->gl_pathc - oldcount, + sizeof(char *), (__compar_fn_t) collated_compare); +#endif + status = 0; +out: + free(dirname); + + return status; +} + +/* 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. */ +static int prefix_array(dirname, array, n, add_slash) +const char *dirname; +char **array; +size_t n; +int add_slash; +{ + register size_t i; + size_t dirlen = strlen(dirname); + + 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 *)xmalloc(dirlen + 1 + eltlen + (add_slash ? 1 : 0)); + + memcpy(new, dirname, dirlen); + new[dirlen] = '/'; + memcpy(&new[dirlen + 1], array[i], eltlen); + free((__ptr_t) array[i]); + array[i] = new; + } + + return 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(pattern, directory, flags, errfunc, pglob) +const char *pattern; +const char *directory; +int flags; +int (*errfunc) __P((const char *, int)); +glob_t *pglob; +{ + __ptr_t stream; + + struct globlink { + struct globlink *next; + char *name; + }; + struct globlink *names = NULL; + size_t nfound = 0; + int meta; + + stream = opendir(directory); + + if (stream == NULL) { + if ((errfunc != NULL && (*errfunc) (directory, errno)) || + (flags & GLOB_ERR)) + return GLOB_ABORTED; + } + + meta = glob_pattern_p(pattern, !(flags & GLOB_NOESCAPE)); + + if (meta) + flags |= GLOB_MAGCHAR; + + while (1) { + const char *name; + size_t len; + + struct dirent *d = readdir((DIR *) stream); + if (d == NULL) + break; +// if (! (d->d_ino != 0)) +// continue; + 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)) == 0) { + struct globlink *new = + (struct globlink *)xmalloc(sizeof(struct globlink)); + len = strlen(name); + new->name = xmalloc(len + ((flags & GLOB_MARK) ? 1 : 0) + 1); + memcpy((__ptr_t) new->name, name, len); + new->name[len] = '\0'; + new->next = names; + names = new; + ++nfound; + if (!meta) + break; + } + } +#ifdef CONFIG_GLOB_NOCHECK + if (nfound == 0 && (flags & GLOB_NOCHECK)) { + size_t len = strlen(pattern); + nfound = 1; + names = (struct globlink *)xmalloc(sizeof(struct globlink)); + names->next = NULL; + names->name = + (char *)xmalloc(len + (flags & GLOB_MARK ? 1 : 0) + 1); + memcpy(names->name, pattern, len); + names->name[len] = '\0'; + } +#endif + pglob->gl_pathv + = (char **)xrealloc(pglob->gl_pathv, + (pglob->gl_pathc + + ((flags & GLOB_DOOFFS) ? pglob->gl_offs : 0) + + nfound + 1) * sizeof(char *)); + + if (flags & GLOB_DOOFFS) + while (pglob->gl_pathc < pglob->gl_offs) + pglob->gl_pathv[pglob->gl_pathc++] = NULL; + + while (names) { + struct globlink *tmp; + + pglob->gl_pathv[pglob->gl_pathc++] = names->name; + tmp = names; + names = names->next; + free(tmp); + } + pglob->gl_pathv[pglob->gl_pathc] = NULL; + + pglob->gl_flags = flags; + + { + int save = errno; + (void)closedir((DIR *) stream); + errno = save; + } + return nfound == 0 ? GLOB_NOMATCH : 0; +} +#endif /* CONFIG_GLOB */ + +#ifdef CONFIG_FAKE_GLOB +/* Fake version of glob. We simply put the input string into + * the gl_pathv array. Currently we don't need it as hush.c won't + * call us if no glob support is available. + */ +int glob(pattern, flags, errfunc, pglob) +const char *pattern; +int flags; +int (*errfunc) __P((const char *, int)); +glob_t *pglob; +{ + int elems, i; + + if (!(flags & GLOB_APPEND)) { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + } + + elems = pglob->gl_pathc + 2; + if (flags & GLOB_DOOFFS) + elems += pglob->gl_offs; + + pglob->gl_pathv = xrealloc(pglob->gl_pathv, elems * sizeof(char *)); + + if (flags & GLOB_DOOFFS) + for (i = 0; i < pglob->gl_offs; i++) + pglob->gl_pathv[i] = NULL; + + pglob->gl_pathv[pglob->gl_pathc] = strdup(pattern); + pglob->gl_pathc++; + pglob->gl_pathv[pglob->gl_pathc] = NULL; + + return 0; +} +#endif /* CONFIG_FAKE_GLOB */ + +/* Free storage allocated in PGLOB by a previous `glob' call. */ +void globfree(pglob) +register glob_t *pglob; +{ + if (pglob->gl_pathv != NULL) { + register int i = + pglob->gl_flags & GLOB_DOOFFS ? pglob->gl_offs : 0; + for (; i < pglob->gl_pathc; ++i) + if (pglob->gl_pathv[i] != NULL) + free((__ptr_t) pglob->gl_pathv[i]); + free((__ptr_t) pglob->gl_pathv); + } +} |