* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* This code is derived from software contributed to Berkeley by
* %sccs.include.redist.c%
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid
[] = "@(#)glob.c 8.2 (Berkeley) %G%";
#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.
* Same as GLOB_NOCHECK, but it will only append pattern if it did
* not contain any magic characters. [Used in csh style globbing]
* Use alternately specified directory access functions.
* expand ~user/foo to the /home/dir/of/user/foo
* expand {1,2}{a,b} to 1a 1b 2a 2b
* Number of matches in the current invocation of glob.
#define CHAR(c) ((Char)((c)&M_ASCII))
#define META(c) ((Char)((c)|M_QUOTE))
#define ismeta(c) (((c)&M_QUOTE) != 0)
static int compare
__P((const void *, const void *));
static void g_Ctoc
__P((const Char
*, char *));
static int g_lstat
__P((Char
*, struct stat
*, glob_t
*));
static DIR *g_opendir
__P((Char
*, glob_t
*));
static Char
*g_strchr
__P((Char
*, int));
static Char
*g_strcat
__P((Char
*, const Char
*));
static int g_stat
__P((Char
*, struct stat
*, glob_t
*));
static int glob0
__P((const Char
*, glob_t
*));
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((const Char
*, glob_t
*));
static const Char
* globtilde
__P((const Char
*, Char
*, glob_t
*));
static int globexp1
__P((const Char
*, glob_t
*));
static int globexp2
__P((const Char
*, const Char
*, glob_t
*, int *));
static int match
__P((Char
*, Char
*, Char
*));
static void qprintf
__P((const char *, Char
*));
glob(pattern
, flags
, errfunc
, pglob
)
int flags
, (*errfunc
) __P((const char *, int));
Char
*bufnext
, *bufend
, 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
;
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
)
return globexp1(patbuf
, pglob
);
return glob0(patbuf
, pglob
);
* Expand recursively a glob {} pattern. When there is no more expansion
* invoke the standard globbing routine to glob the rest of the magic
static int globexp1(pattern
, pglob
)
const Char
* ptr
= pattern
;
/* Protect a single {}, for find(1), like csh */
if (pattern
[0] == LBRACE
&& pattern
[1] == RBRACE
&& pattern
[2] == EOS
)
return glob0(pattern
, pglob
);
while ((ptr
= (const Char
*) g_strchr((Char
*) ptr
, LBRACE
)) != NULL
)
if (!globexp2(ptr
, pattern
, pglob
, &rv
))
return glob0(pattern
, pglob
);
* Recursive brace globbing helper. Tries to expand a single brace.
* If it succeeds then it invokes globexp1 with the new pattern.
* If it fails then it tries to glob the rest of the pattern and returns.
static int globexp2(ptr
, pattern
, pglob
, rv
)
const Char
*ptr
, *pattern
;
const Char
*pe
, *pm
, *pl
;
Char patbuf
[MAXPATHLEN
+ 1];
/* copy part up to the brace */
for (lm
= patbuf
, pm
= pattern
; pm
!= ptr
; *lm
++ = *pm
++)
/* Find the balanced brace */
for (i
= 0, pe
= ++ptr
; *pe
; pe
++)
/* Ignore everything between [] */
for (pm
= pe
++; *pe
!= RBRACKET
&& *pe
!= EOS
; pe
++)
* We could not find a matching RBRACKET.
* Ignore and just look for RBRACE
else if (*pe
== RBRACE
) {
/* Non matching braces; just glob the pattern */
if (i
!= 0 || *pe
== EOS
) {
*rv
= glob0(patbuf
, pglob
);
for (i
= 0, pl
= pm
= ptr
; pm
<= pe
; pm
++)
/* Ignore everything between [] */
for (pl
= pm
++; *pm
!= RBRACKET
&& *pm
!= EOS
; pm
++)
* We could not find a matching RBRACKET.
* Ignore and just look for RBRACE
/* Append the current string */
for (lm
= ls
; (pl
< pm
); *lm
++ = *pl
++)
* Append the rest of the pattern after the
for (pl
= pe
+ 1; (*lm
++ = *pl
++) != EOS
;)
/* Expand the current pattern */
qprintf("globexp2:", patbuf
);
*rv
= globexp1(patbuf
, pglob
);
/* move after the comma, to the next string */
* expand tilde from the passwd file.
globtilde(pattern
, patbuf
, pglob
)
if (*pattern
!= TILDE
|| !(pglob
->gl_flags
& GLOB_TILDE
))
/* Copy up to the end of the string or / */
for (p
= pattern
+ 1, h
= (char *) patbuf
; *p
&& *p
!= SLASH
;
if (((char *) patbuf
)[0] == EOS
) {
* handle a plain ~ or ~/ by expanding $HOME
* first and then trying the password file
if ((h
= getenv("HOME")) == NULL
) {
if ((pwd
= getpwuid(getuid())) == NULL
)
if ((pwd
= getpwnam((char*) patbuf
)) == NULL
)
/* Copy the home directory */
for (b
= patbuf
; *h
; *b
++ = *h
++)
/* Append the rest of the pattern */
while ((*b
++ = *p
++) != EOS
)
* 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
Char
*bufnext
, patbuf
[MAXPATHLEN
+1];
qpatnext
= globtilde(pattern
, patbuf
, pglob
);
oldpathc
= pglob
->gl_pathc
;
/* We don't need to check for buffer overflow any more. */
while ((c
= *qpatnext
++) != EOS
) {
g_strchr((Char
*) 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
;
pglob
->gl_flags
|= GLOB_MAGCHAR
;
/* collapse adjacent stars to one,
* to avoid exponential behavior
if (bufnext
== patbuf
|| bufnext
[-1] != M_ALL
)
qprintf("glob0:", patbuf
);
if ((err
= glob1(patbuf
, pglob
)) != 0)
* If there was no match we are going to append the pattern
* if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
* and the pattern did not contain any magic characters
* GLOB_NOMAGIC is there just for compatibility with csh.
if (pglob
->gl_pathc
== oldpathc
&&
((pglob
->gl_flags
& GLOB_NOCHECK
) ||
((pglob
->gl_flags
& (GLOB_NOMAGIC
|GLOB_BRACE
|GLOB_TILDE
)) ||
!(pglob
->gl_flags
& GLOB_MAGCHAR
))))
return(globextend(pattern
, pglob
));
else if (!(pglob
->gl_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_lstat(pathbuf
, &sb
, pglob
))
if (((pglob
->gl_flags
& GLOB_MARK
) &&
pathend
[-1] != SEP
) && (S_ISDIR(sb
.st_mode
)
|| (S_ISLNK(sb
.st_mode
) &&
(g_stat(pathbuf
, &sb
, pglob
) == 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
;
* The readdirfunc declaration can't be prototyped, because it is
* assigned, below, to two functions which are prototyped in glob.h
* and dirent.h as taking pointers to differently typed opaque
struct dirent
*(*readdirfunc
)();
if ((dirp
= g_opendir(pathbuf
, pglob
)) == NULL
) {
/* TODO: don't call for ENOENT or ENOTDIR? */
if (pglob
->gl_errfunc(buf
, errno
) ||
pglob
->gl_flags
& GLOB_ERR
)
/* Search directory for matching names. */
if (pglob
->gl_flags
& GLOB_ALTDIRFUNC
)
readdirfunc
= pglob
->gl_readdir
;
while ((dp
= (*readdirfunc
)(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
);
if (pglob
->gl_flags
& GLOB_ALTDIRFUNC
)
(*pglob
->gl_closedir
)(dirp
);
* 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
= pglob
->gl_pathv
?
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
;
if (match(name
, pat
, patend
))
if ((k
= *name
++) == EOS
)
if ((negate_range
= ((*pat
& M_MASK
) == M_NOT
)) != EOS
)
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
)
if (pglob
->gl_flags
& GLOB_ALTDIRFUNC
)
return((*pglob
->gl_opendir
)(buf
));
if (pglob
->gl_flags
& GLOB_ALTDIRFUNC
)
return((*pglob
->gl_lstat
)(buf
, sb
));
if (pglob
->gl_flags
& GLOB_ALTDIRFUNC
)
return((*pglob
->gl_stat
)(buf
, sb
));
while((*dst
++ = *src
++) != EOS
)
register const Char
*str
;
for (dc
= buf
; (*dc
++ = *str
++) != EOS
;)
(void)printf("%s:\n", str
);
(void)printf("%c", CHAR(*p
));
(void)printf("%c", *p
& M_PROTECT
? '"' : ' ');
(void)printf("%c", ismeta(*p
) ? '_' : ' ');