The arguments to collated_compare are actually pointers to pointers to char, not pointers to char. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
457 lines
12 KiB
C
457 lines
12 KiB
C
/* 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>
|
|
#include <qsort.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(const void *a, const void *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(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 oldcount1;
|
|
|
|
#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. */
|
|
|
|
oldcount1 = 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[oldcount1],
|
|
pglob->gl_pathc - oldcount1,
|
|
flags & GLOB_MARK);
|
|
}
|
|
|
|
globfree(&dirs);
|
|
flags |= GLOB_MAGCHAR;
|
|
|
|
if (pglob->gl_pathc == oldcount) {
|
|
/* No matches. */
|
|
|
|
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 {
|
|
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 *), 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(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(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;
|
|
}
|
|
}
|
|
|
|
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';
|
|
}
|
|
|
|
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(glob_t *pglob)
|
|
{
|
|
if (pglob->gl_pathv != NULL) {
|
|
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);
|
|
}
|
|
}
|