* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid
[] = "@(#)glob.c 5.12 (Berkeley) 6/24/91";
#endif /* LIBC_SCCS and not lint */
* glob(3) -- a superset of the one defined in POSIX 1003.2.
* The [!...] convention to negate a range is supported (SysV, Posix, ksh).
* Optional extra services, controlled by flags not defined by POSIX:
* Escaping convention: \ inhibits any special meaning the following
* character might have (except \ at end of string is retained).
* Set in gl_flags if pattern contained a globbing character.
* Number of matches in the current invocation of glob.
#define CHAR(c) ((c)&M_ASCII)
#define META(c) ((c)|M_QUOTE)
#define ismeta(c) (((c)&M_QUOTE) != 0)
static int compare
__P((const void *, const void *));
static void g_Ctoc
__P((Char
*, char *));
static int g_lstat
__P((Char
*, struct stat
*));
static DIR *g_opendir
__P((Char
*));
static Char
*g_strchr
__P((Char
*, int));
static int g_stat
__P((Char
*, struct stat
*));
static int glob1
__P((Char
*, glob_t
*));
static int glob2
__P((Char
*, Char
*, Char
*, glob_t
*));
static int glob3
__P((Char
*, Char
*, Char
*, Char
*, glob_t
*));
static int globextend
__P((Char
*, glob_t
*));
static int match
__P((Char
*, Char
*, Char
*));
static void qprintf
__P((Char
*));
* The main glob() routine: compiles the pattern (optionally processing
* quotes), calls glob1() to do the real pattern matching, and finally
* sorts the list (unless unsorted operation is requested). Returns 0
* if things went well, nonzero if errors occurred. It is not an error
glob(pattern
, flags
, errfunc
, pglob
)
int flags
, (*errfunc
) __P((char *, int));
const u_char
*compilepat
, *patnext
;
Char
*bufnext
, *bufend
, *compilebuf
, *qpatnext
, patbuf
[MAXPATHLEN
+1];
patnext
= (u_char
*) pattern
;
if (!(flags
& GLOB_APPEND
)) {
if (!(flags
& GLOB_DOOFFS
))
pglob
->gl_flags
= flags
& ~GLOB_MAGCHAR
;
pglob
->gl_errfunc
= errfunc
;
oldpathc
= pglob
->gl_pathc
;
bufend
= bufnext
+ MAXPATHLEN
;
if (flags
& GLOB_QUOTE
) {
/* Protect the quoted characters. */
while (bufnext
< bufend
&& (c
= *patnext
++) != EOS
)
if ((c
= *patnext
++) == EOS
) {
*bufnext
++ = c
| M_PROTECT
;
while (bufnext
< bufend
&& (c
= *patnext
++) != EOS
)
/* We don't need to check for buffer overflow any more. */
while ((c
= *qpatnext
++) != EOS
) {
pglob
->gl_flags
|= GLOB_MAGCHAR
;
g_strchr(qpatnext
+1, RBRACKET
) == NULL
) {
if (*qpatnext
== RANGE
&&
(c
= qpatnext
[1]) != RBRACKET
) {
} while ((c
= *qpatnext
++) != RBRACKET
);
pglob
->gl_flags
|= GLOB_MAGCHAR
;
pglob
->gl_flags
|= GLOB_MAGCHAR
;
if ((err
= glob1(patbuf
, pglob
)) != 0)
if (pglob
->gl_pathc
== oldpathc
&& flags
& GLOB_NOCHECK
) {
if (!(flags
& GLOB_QUOTE
)) {
const u_char
*sp
= compilepat
;
* Copy pattern, interpreting quotes; this is slightly
* different than the interpretation of quotes above
* -- which should prevail?
while (*compilepat
!= EOS
) {
if (*compilepat
== QUOTE
) {
if (*++compilepat
== EOS
)
*compilebuf
++ = (u_char
)*compilepat
++;
return(globextend(patbuf
, pglob
));
} else if (!(flags
& GLOB_NOSORT
))
qsort(pglob
->gl_pathv
+ pglob
->gl_offs
+ oldpathc
,
pglob
->gl_pathc
- oldpathc
, sizeof(char *), compare
);
return(strcmp(*(char **)p
, *(char **)q
));
Char pathbuf
[MAXPATHLEN
+1];
/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
return(glob2(pathbuf
, pathbuf
, pattern
, pglob
));
* The functions glob2 and glob3 are mutually recursive; there is one level
* of recursion for each segment in the pattern that contains one or more
glob2(pathbuf
, pathend
, pattern
, pglob
)
Char
*pathbuf
, *pathend
, *pattern
;
* Loop over pattern segments until end of pattern or until
* segment with meta character found.
if (*pattern
== EOS
) { /* End of pattern? */
if (g_stat(pathbuf
, &sb
))
if (((pglob
->gl_flags
& GLOB_MARK
) &&
pathend
[-1] != SEP
) && (S_ISDIR(sb
.st_mode
)
|| (S_ISLNK(sb
.st_mode
) &&
(g_stat(pathbuf
, &sb
) == 0) &&
return(globextend(pathbuf
, pglob
));
/* Find end of next segment, copy tentatively to pathend. */
while (*p
!= EOS
&& *p
!= SEP
) {
if (!anymeta
) { /* No expansion, do next segment. */
} else /* Need expansion, recurse. */
return(glob3(pathbuf
, pathend
, pattern
, p
, pglob
));
glob3(pathbuf
, pathend
, pattern
, restpattern
, pglob
)
Char
*pathbuf
, *pathend
, *pattern
, *restpattern
;
register struct dirent
*dp
;
if (!(dirp
= g_opendir(pathbuf
)))
/* TODO: don't call for ENOENT or ENOTDIR? */
(*pglob
->gl_errfunc
)(pathbuf
, errno
) ||
(pglob
->gl_flags
& GLOB_ERR
))
/* Search directory for matching names. */
while ((dp
= readdir(dirp
))) {
/* Initial DOT must be matched literally. */
if (dp
->d_name
[0] == DOT
&& *pattern
!= DOT
)
for (sc
= (u_char
*) dp
->d_name
, dc
= pathend
;
if (!match(pathend
, pattern
, restpattern
)) {
err
= glob2(pathbuf
, --dc
, restpattern
, pglob
);
/* TODO: check error from readdir? */
* Extend the gl_pathv member of a glob_t structure to accomodate a new item,
* add the new item, and update gl_pathc.
* This assumes the BSD realloc, which only copies the block when its size
* crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
* Return 0 if new item added, error code if memory couldn't be allocated.
* Invariant of the glob_t structure:
* Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
* gl_pathv points to (gl_offs + gl_pathc + 1) items.
newsize
= sizeof(*pathv
) * (2 + pglob
->gl_pathc
+ pglob
->gl_offs
);
pathv
= (char **)realloc((char *)pglob
->gl_pathv
, newsize
);
if (pglob
->gl_pathv
== NULL
&& pglob
->gl_offs
> 0) {
/* first time around -- clear initial gl_offs items */
for (i
= pglob
->gl_offs
; --i
>= 0; )
if ((copy
= malloc(p
- path
)) != NULL
) {
pathv
[pglob
->gl_offs
+ pglob
->gl_pathc
++] = copy
;
pathv
[pglob
->gl_offs
+ pglob
->gl_pathc
] = NULL
;
return(copy
== NULL
? GLOB_NOSPACE
: 0);
* pattern matching function for filenames. Each occurrence of the *
* pattern causes a recursion level.
register Char
*name
, *pat
, *patend
;
for (; *name
!= EOS
; ++name
)
if (match(name
, pat
, patend
))
if (negate_range
= ((*pat
& M_MASK
) == M_NOT
))
while (((c
= *pat
++) & M_MASK
) != M_END
)
if ((*pat
& M_MASK
) == M_RNG
) {
if (c
<= k
&& k
<= pat
[1])
/* Free allocated data belonging to a glob_t structure. */
if (pglob
->gl_pathv
!= NULL
) {
pp
= pglob
->gl_pathv
+ pglob
->gl_offs
;
for (i
= pglob
->gl_pathc
; i
--; ++pp
)
for (dc
= buf
; *dc
++ = *str
++;);
(void)printf("%c", *p
& 0xff);
(void)printf("%c", *p
& M_PROTECT
? '"' : ' ');
(void)printf("%c", *p
& M_META
? '_' : ' ');