9
0
Fork 0

add globbing support

This commit is contained in:
Sascha Hauer 2008-03-09 22:35:38 +01:00
parent 2209ae02ba
commit 187847b2b4
7 changed files with 1127 additions and 28 deletions

View File

@ -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)

View File

@ -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

View File

@ -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);

85
include/fnmatch.h Normal file
View File

@ -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 */

231
include/glob.h Normal file
View File

@ -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 */

228
lib/fnmatch.c Normal file
View File

@ -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
}

471
lib/glob.c Normal file
View File

@ -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);
}
}