summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2008-03-09 22:35:38 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2008-03-09 22:35:38 +0100
commit187847b2b483f37f3f595af576c362c4e9d4c288 (patch)
treea1040fe6e189196e1247b032205112af71490f7e
parent2209ae02ba97e7c349e80af8fa6c5946d4510f84 (diff)
downloadbarebox-187847b2b483f37f3f595af576c362c4e9d4c288.tar.gz
add globbing support
-rw-r--r--arch/sandbox/Makefile3
-rw-r--r--common/Kconfig7
-rw-r--r--common/hush.c130
-rw-r--r--include/fnmatch.h85
-rw-r--r--include/glob.h231
-rw-r--r--lib/fnmatch.c228
-rw-r--r--lib/glob.c471
7 files changed, 1127 insertions, 28 deletions
diff --git a/arch/sandbox/Makefile b/arch/sandbox/Makefile
index 6622fe8..25fcceb 100644
--- a/arch/sandbox/Makefile
+++ b/arch/sandbox/Makefile
@@ -20,7 +20,8 @@ CFLAGS := -fno-common -Os -Dmalloc=u_boot_malloc \
-Dputc=u_boot_putc -Dfgetc=u_boot_fgetc \
-Dfputc=u_boot_fputc -Dfgets=u_boot_fgets \
-Dfputs=u_boot_fputs -Dsetenv=u_boot_setenv \
- -Dgetenv=u_boot_getenv -Dprintf=u_boot_printf
+ -Dgetenv=u_boot_getenv -Dprintf=u_boot_printf \
+ -Dglob=u_boot_glob -Dglobfree=u_boot_globfree
ifeq ($(incdir-y),)
incdir-y := $(machine-y)
diff --git a/common/Kconfig b/common/Kconfig
index 7fcf7f1..05223d5 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -114,6 +114,13 @@ choice
endchoice
+config GLOB
+ bool
+ prompt "hush globbing support"
+ depends on SHELL_HUSH
+ help
+ If you want to use wildcards like * or ? say y here.
+
config PROMPT_HUSH_PS2
string
depends on SHELL_HUSH
diff --git a/common/hush.c b/common/hush.c
index c05b564..4e865c4 100644
--- a/common/hush.c
+++ b/common/hush.c
@@ -120,6 +120,7 @@
#include <errno.h>
#include <fs.h>
#include <libbb.h>
+#include <glob.h>
/*cmd_boot.c*/
extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); /* do_bootd */
@@ -192,6 +193,7 @@ struct child_prog {
struct pipe *group; /* if non-NULL, first in group or subshell */
int sp; /* number of SPECIAL_VAR_SYMBOL */
int type;
+ glob_t glob_result; /* result of parameter globbing */
};
struct pipe {
@@ -585,6 +587,7 @@ static int run_pipe_real(struct pipe *pi)
free(str);
return last_return_code;
}
+
if (strchr(child->argv[i], '/')) {
return execute_script(child->argv[i], child->argc-i, &child->argv[i]);
}
@@ -754,11 +757,7 @@ static int free_pipe(struct pipe *pi, int indent)
for (a=0,p=child->argv; *p; a++,p++) {
final_printf("%s argv[%d] = %s\n",ind,a,*p);
}
- for (a = child->argc;a >= 0;a--) {
- free(child->argv[a]);
- }
- free(child->argv);
- child->argc = 0;
+ globfree(&child->glob_result);
child->argv=NULL;
} else if (child->group) {
ret_code = free_pipe_list(child->group,indent+3);
@@ -788,6 +787,86 @@ static int free_pipe_list(struct pipe *head, int indent)
return rcode;
}
+/* The API for glob is arguably broken. This routine pushes a non-matching
+ * string into the output structure, removing non-backslashed backslashes.
+ * If someone can prove me wrong, by performing this function within the
+ * original glob(3) api, feel free to rewrite this routine into oblivion.
+ * Return code (0 vs. GLOB_NOSPACE) matches glob(3).
+ * XXX broken if the last character is '\\', check that before calling.
+ */
+static int globhack(const char *src, int flags, glob_t *pglob)
+{
+ int cnt=0, pathc;
+ const char *s;
+ char *dest;
+ for (cnt=1, s=src; s && *s; s++) {
+ if (*s == '\\') s++;
+ cnt++;
+ }
+ dest = xmalloc(cnt);
+ if (!(flags & GLOB_APPEND)) {
+ pglob->gl_pathv = NULL;
+ pglob->gl_pathc = 0;
+ pglob->gl_offs = 0;
+ }
+ pathc = ++pglob->gl_pathc;
+ pglob->gl_pathv = xrealloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv));
+ pglob->gl_pathv[pathc-1] = dest;
+ pglob->gl_pathv[pathc] = NULL;
+ for (s=src; s && *s; s++, dest++) {
+ if (*s == '\\') s++;
+ *dest = *s;
+ }
+ *dest='\0';
+ return 0;
+}
+
+/* XXX broken if the last character is '\\', check that before calling */
+static int glob_needed(const char *s)
+{
+#ifdef CONFIG_GLOB
+ for (; *s; s++) {
+ if (*s == '\\') s++;
+ if (strchr("*[?",*s))
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+static int xglob(o_string *dest, int flags, glob_t *pglob)
+{
+ int gr;
+
+ /* short-circuit for null word */
+ /* we can code this better when the debug_printf's are gone */
+ if (dest->length == 0) {
+ if (dest->nonnull) {
+ /* bash man page calls this an "explicit" null */
+ gr = globhack(dest->data, flags, pglob);
+ debug_printf("globhack returned %d\n",gr);
+ } else {
+ return 0;
+ }
+ } else if (glob_needed(dest->data)) {
+ gr = glob(dest->data, flags, NULL, pglob);
+ debug_printf("glob returned %d\n",gr);
+ if (gr == GLOB_NOMATCH) {
+ /* quote removal, or more accurately, backslash removal */
+ gr = globhack(dest->data, flags, pglob);
+ debug_printf("globhack returned %d\n",gr);
+ }
+ } else {
+ gr = globhack(dest->data, flags, pglob);
+ debug_printf("globhack returned %d\n",gr);
+ }
+ if (gr != 0) { /* GLOB_ABORTED ? */
+ error_msg("glob(3) error %d",gr);
+ }
+ /* globprint(glob_target); */
+ return gr;
+}
+
/* Select which version we will use */
static int run_list(struct pipe *pi)
{
@@ -946,8 +1025,8 @@ static int reserved_word(o_string *dest, struct p_context *ctx)
static int done_word(o_string *dest, struct p_context *ctx)
{
struct child_prog *child=ctx->child;
- char *str, *s;
- int argc, cnt;
+ glob_t *glob_target;
+ int gr, flags = 0;
debug_printf("done_word: %s %p\n", dest->data, child);
if (dest->length == 0 && !dest->nonnull) {
@@ -963,27 +1042,20 @@ static int done_word(o_string *dest, struct p_context *ctx)
if (reserved_word(dest,ctx))
return ctx->w==RES_SNTX;
}
- for (cnt = 1, s = dest->data; s && *s; s++) {
- if (*s == '\\')
- s++;
- cnt++;
- }
- str = xmalloc(cnt);
- if ( child->argv == NULL) {
- child->argc=0;
- }
- argc = ++child->argc;
- child->argv = xrealloc(child->argv, (argc+1)*sizeof(*child->argv));
- child->argv[argc-1]=str;
- child->argv[argc]=NULL;
- for (s = dest->data; s && *s; s++,str++) {
- if (*s == '\\')
- s++;
- *str = *s;
- }
- *str = '\0';
+
+ glob_target = &child->glob_result;
+ if (child->argv)
+ flags |= GLOB_APPEND;
+
+ gr = xglob(dest, flags, glob_target);
+ if (gr)
+ return 1;
b_reset(dest);
+
+ child->argv = glob_target->gl_pathv;
+ child->argc = glob_target->gl_pathc;
+
if (ctx->w == RES_FOR) {
done_word(dest,ctx);
done_pipe(ctx,PIPE_SEQ);
@@ -1017,6 +1089,8 @@ static int done_command(struct p_context *ctx)
pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
prog = pi->progs + pi->num_progs;
+ prog->glob_result.gl_pathv = NULL;
+
prog->argv = NULL;
prog->group = NULL;
prog->sp = 0;
@@ -1496,8 +1570,10 @@ static int source_script(const char *path, int argc, char *argv[])
ctx.global_argv = argv;
script = read_file(path, NULL);
- if (!script)
+ if (!script) {
+ perror("sh");
return 1;
+ }
ret = parse_string_outer(&ctx, script, FLAG_PARSE_SEMICOLON);
diff --git a/include/fnmatch.h b/include/fnmatch.h
new file mode 100644
index 0000000..c0fd6a9
--- /dev/null
+++ b/include/fnmatch.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 1991,92,93,96,97,98,99,2001 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, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _FNMATCH_H
+#define _FNMATCH_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
+# if !defined __GLIBC__ || !defined __P
+# undef __P
+# define __P(protos) protos
+# endif
+#else /* Not C++ or ANSI C. */
+# undef __P
+# define __P(protos) ()
+/* We can get away without defining `const' here only because in this file
+ it is used only inside the prototype for `fnmatch', which is elided in
+ non-ANSI C where `const' is problematical. */
+#endif /* C++ or ANSI C. */
+
+#ifndef const
+# if (defined __STDC__ && __STDC__) || defined __cplusplus
+# define __const const
+# else
+# define __const
+# endif
+#endif
+
+/* We #undef these before defining them because some losing systems
+ (HP-UX A.08.07 for example) define these in <unistd.h>. */
+#undef FNM_PATHNAME
+#undef FNM_NOESCAPE
+#undef FNM_PERIOD
+
+/* Bits set in the FLAGS argument to `fnmatch'. */
+#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
+#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
+#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
+
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
+# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
+# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
+# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
+# define FNM_EXTMATCH (1 << 5) /* Use ksh-like extended matching. */
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN. */
+#define FNM_NOMATCH 1
+
+/* This value is returned if the implementation does not support
+ `fnmatch'. Since this is not the case here it will never be
+ returned but the conformance test suites still require the symbol
+ to be defined. */
+#ifdef _XOPEN_SOURCE
+# define FNM_NOSYS (-1)
+#endif
+
+/* Match NAME against the filename pattern PATTERN,
+ returning zero if it matches, FNM_NOMATCH if not. */
+extern int fnmatch __P ((__const char *__pattern, __const char *__name,
+ int __flags));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/include/glob.h b/include/glob.h
new file mode 100644
index 0000000..698b06e
--- /dev/null
+++ b/include/glob.h
@@ -0,0 +1,231 @@
+/* Copyright (C) 1991,92,95,96,97,98,2000,2001 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, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _GLOB_H
+#define _GLOB_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#undef __ptr_t
+#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
+# if !defined __GLIBC__ || !defined __P
+# undef __P
+# undef __PMT
+# define __P(protos) protos
+# define __PMT(protos) protos
+# if !defined __GNUC__ || __GNUC__ < 2
+# undef __const
+# define __const const
+# endif
+# endif
+# define __ptr_t void *
+#else /* Not C++ or ANSI C. */
+# undef __P
+# undef __PMT
+# define __P(protos) ()
+# define __PMT(protos) ()
+# undef __const
+# define __const
+# define __ptr_t char *
+#endif /* C++ or ANSI C. */
+
+/* We need `size_t' for the following definitions. */
+#ifndef __size_t
+# if defined __GNUC__ && __GNUC__ >= 2
+typedef __SIZE_TYPE__ __size_t;
+# ifdef _XOPEN_SOURCE
+typedef __SIZE_TYPE__ size_t;
+# endif
+# else
+/* This is a guess. */
+typedef unsigned long int __size_t;
+# endif
+#else
+/* The GNU CC stddef.h version defines __size_t as empty. We need a real
+ definition. */
+# undef __size_t
+# define __size_t size_t
+#endif
+
+/* Bits set in the FLAGS argument to `glob'. */
+#define GLOB_ERR (1 << 0)/* Return on read errors. */
+#define GLOB_MARK (1 << 1)/* Append a slash to each name. */
+#define GLOB_NOSORT (1 << 2)/* Don't sort the names. */
+#define GLOB_DOOFFS (1 << 3)/* Insert PGLOB->gl_offs NULLs. */
+#define GLOB_NOCHECK (1 << 4)/* If nothing matches, return the pattern. */
+#define GLOB_APPEND (1 << 5)/* Append to results of a previous call. */
+#define GLOB_NOESCAPE (1 << 6)/* Backslashes don't quote metacharacters. */
+#define GLOB_PERIOD (1 << 7)/* Leading `.' can be matched by metachars. */
+
+#if (!defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _BSD_SOURCE \
+ || defined _GNU_SOURCE)
+# define GLOB_MAGCHAR (1 << 8)/* Set in gl_flags if any metachars seen. */
+# define GLOB_ALTDIRFUNC (1 << 9)/* Use gl_opendir et al functions. */
+# define GLOB_BRACE (1 << 10)/* Expand "{a,b}" to "a" "b". */
+# define GLOB_NOMAGIC (1 << 11)/* If no magic chars, return the pattern. */
+# define GLOB_TILDE (1 << 12)/* Expand ~user and ~ to home directories. */
+# define GLOB_ONLYDIR (1 << 13)/* Match only directories. */
+# define GLOB_TILDE_CHECK (1 << 14)/* Like GLOB_TILDE but return an error
+ if the user name is not available. */
+# define __GLOB_FLAGS (GLOB_ERR|GLOB_MARK|GLOB_NOSORT|GLOB_DOOFFS| \
+ GLOB_NOESCAPE|GLOB_NOCHECK|GLOB_APPEND| \
+ GLOB_PERIOD|GLOB_ALTDIRFUNC|GLOB_BRACE| \
+ GLOB_NOMAGIC|GLOB_TILDE|GLOB_ONLYDIR|GLOB_TILDE_CHECK)
+#else
+# define __GLOB_FLAGS (GLOB_ERR|GLOB_MARK|GLOB_NOSORT|GLOB_DOOFFS| \
+ GLOB_NOESCAPE|GLOB_NOCHECK|GLOB_APPEND| \
+ GLOB_PERIOD)
+#endif
+
+/* Error returns from `glob'. */
+#define GLOB_NOSPACE 1 /* Ran out of memory. */
+#define GLOB_ABORTED 2 /* Read error. */
+#define GLOB_NOMATCH 3 /* No matches found. */
+#define GLOB_NOSYS 4 /* Not implemented. */
+#ifdef _GNU_SOURCE
+/* Previous versions of this file defined GLOB_ABEND instead of
+ GLOB_ABORTED. Provide a compatibility definition here. */
+# define GLOB_ABEND GLOB_ABORTED
+#endif
+
+/* Structure describing a globbing run. */
+#if !defined _AMIGA && !defined VMS /* Buggy compiler. */
+# ifdef _GNU_SOURCE
+struct stat;
+# endif
+#endif
+typedef struct
+ {
+ __size_t gl_pathc; /* Count of paths matched by the pattern. */
+ char **gl_pathv; /* List of matched pathnames. */
+ __size_t gl_offs; /* Slots to reserve in `gl_pathv'. */
+ int gl_flags; /* Set to FLAGS, maybe | GLOB_MAGCHAR. */
+
+ /* If the GLOB_ALTDIRFUNC flag is set, the following functions
+ are used instead of the normal file access functions. */
+ void (*gl_closedir) __PMT ((void *));
+#ifdef _GNU_SOURCE
+ struct dirent *(*gl_readdir) __PMT ((void *));
+#else
+ void *(*gl_readdir) __PMT ((void *));
+#endif
+ __ptr_t (*gl_opendir) __PMT ((__const char *));
+#ifdef _GNU_SOURCE
+ int (*gl_lstat) __PMT ((__const char *__restrict,
+ struct stat *__restrict));
+ int (*gl_stat) __PMT ((__const char *__restrict, struct stat *__restrict));
+#else
+ int (*gl_lstat) __PMT ((__const char *__restrict, void *__restrict));
+ int (*gl_stat) __PMT ((__const char *__restrict, void *__restrict));
+#endif
+ } glob_t;
+
+#ifdef _LARGEFILE64_SOURCE
+# ifdef _GNU_SOURCE
+struct stat64;
+# endif
+typedef struct
+ {
+ __size_t gl_pathc;
+ char **gl_pathv;
+ __size_t gl_offs;
+ int gl_flags;
+
+ /* If the GLOB_ALTDIRFUNC flag is set, the following functions
+ are used instead of the normal file access functions. */
+ void (*gl_closedir) __PMT ((void *));
+# ifdef _GNU_SOURCE
+ struct dirent64 *(*gl_readdir) __PMT ((void *));
+# else
+ void *(*gl_readdir) __PMT ((void *));
+# endif
+ __ptr_t (*gl_opendir) __PMT ((__const char *));
+# ifdef _GNU_SOURCE
+ int (*gl_lstat) __PMT ((__const char *__restrict,
+ struct stat64 *__restrict));
+ int (*gl_stat) __PMT ((__const char *__restrict,
+ struct stat64 *__restrict));
+# else
+ int (*gl_lstat) __PMT ((__const char *__restrict, void *__restrict));
+ int (*gl_stat) __PMT ((__const char *__restrict, void *__restrict));
+# endif
+ } glob64_t;
+#endif
+
+#if _FILE_OFFSET_BITS == 64 && __GNUC__ < 2
+# define glob glob64
+# define globfree globfree64
+#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. */
+#if _FILE_OFFSET_BITS != 64 || __GNUC__ < 2
+#if defined CONFIG_GLOB || defined CONFIG_FAKE_GLOB
+extern int glob __P ((__const char *__restrict __pattern, int __flags,
+ int (*__errfunc) (__const char *, int),
+ glob_t *__restrict __pglob));
+
+#else
+static inline int glob __P ((__const char *__restrict __pattern, int __flags,
+ int (*__errfunc) (__const char *, int),
+ glob_t *__restrict __pglob))
+{
+ return GLOB_ABORTED;
+}
+#endif
+/* Free storage allocated in PGLOB by a previous `glob' call. */
+extern void globfree __P ((glob_t *__pglob));
+#else
+extern int glob __P ((__const char *__restrict __pattern, int __flags,
+ int (*__errfunc) (__const char *, int),
+ glob_t *__restrict __pglob)) __asm__ ("glob64");
+
+extern void globfree __P ((glob_t *__pglob)) __asm__ ("globfree64");
+#endif
+
+#ifdef _LARGEFILE64_SOURCE
+extern int glob64 __P ((__const char *__restrict __pattern, int __flags,
+ int (*__errfunc) (__const char *, int),
+ glob64_t *__restrict __pglob));
+
+extern void globfree64 __P ((glob64_t *__pglob));
+#endif
+
+
+#ifdef _GNU_SOURCE
+/* Return nonzero if PATTERN contains any metacharacters.
+ Metacharacters can be quoted with backslashes if QUOTE is nonzero.
+
+ This function is not part of the interface specified by POSIX.2
+ but several programs want to use it. */
+extern int glob_pattern_p __P ((__const char *__pattern, int __quote));
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* glob.h */
diff --git a/lib/fnmatch.c b/lib/fnmatch.c
new file mode 100644
index 0000000..86968bc
--- /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 0000000..a5e3d1d
--- /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);
+ }
+}