From 187847b2b483f37f3f595af576c362c4e9d4c288 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Sun, 9 Mar 2008 22:35:38 +0100 Subject: add globbing support --- arch/sandbox/Makefile | 3 +- common/Kconfig | 7 + common/hush.c | 130 +++++++++++--- include/fnmatch.h | 85 +++++++++ include/glob.h | 231 +++++++++++++++++++++++++ lib/fnmatch.c | 228 ++++++++++++++++++++++++ lib/glob.c | 471 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1127 insertions(+), 28 deletions(-) create mode 100644 include/fnmatch.h create mode 100644 include/glob.h create mode 100644 lib/fnmatch.c create mode 100644 lib/glob.c diff --git a/arch/sandbox/Makefile b/arch/sandbox/Makefile index 6622fe89ae..25fcceb7e0 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 7fcf7f1b4f..05223d5acf 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 c05b5642b7..4e865c4a3c 100644 --- a/common/hush.c +++ b/common/hush.c @@ -120,6 +120,7 @@ #include #include #include +#include /*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 0000000000..c0fd6a9207 --- /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 . */ +#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 0000000000..698b06ecc2 --- /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 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 +#endif + +/* Enable GNU extensions in fnmatch.h. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#include +#include +#include + + +/* 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 +#include +#include +#include +#include +#include +#include +#define _GNU_SOURCE +#include + +#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); + } +} -- cgit v1.2.3