/* glob.c: rc's (ugly) globber. This code is not elegant, but it works */
static List
*dmatch(char *, char *, char *);
static List
*doglob(char *, char *);
static List
*lglob(List
*, char *, char *, size_t);
static List
*sort(List
*);
Matches a list of words s against a list of patterns p. Returns true iff
a pattern in p matches a word in s. () matches (), but otherwise null
extern bool lmatch(List
*s
, List
*p
) {
if (p
== NULL
) /* null matches null */
for (; p
!= NULL
; p
= p
->n
) { /* one or more stars match null */
if (*p
->w
!= '\0') { /* the null string is a special case; it does *not* match () */
for (i
= 0; p
->w
[i
] != '\0'; i
++)
if (p
->w
[i
] != '*' || p
->m
[i
] != 1) {
for (; s
!= NULL
; s
= s
->n
)
for (q
= p
; q
!= NULL
; q
= q
->n
)
if (match(q
->w
, q
->m
, s
->w
))
Globs a list; checks to see if each element in the list has a metacharacter. If it
does, it is globbed, and the output is sorted.
extern List
*glob(List
*s
) {
for (r
= s
, meta
= FALSE
; r
!= NULL
; r
= r
->n
)
return s
; /* don't copy lists with no metacharacters in them */
for (top
= r
= NULL
; s
!= NULL
; s
= s
->n
) {
if (s
->m
== NULL
) { /* no metacharacters; just tack on to the return list */
top
= r
= sort(doglob(s
->w
, s
->m
));
r
->n
= sort(doglob(s
->w
, s
->m
));
/* Matches a pattern p against the contents of directory d */
static List
*dmatch(char *d
, char *p
, char *m
) {
static struct dirent
*dp
;
/* prototypes for XXXdir functions. comment out if necessary */
extern DIR *opendir(const char *);
extern struct dirent
*readdir(DIR *);
/*extern int closedir(DIR *);*/
/* opendir succeeds on regular files on some systems, so the stat() call is necessary (sigh) */
if (stat(d
, &s
) < 0 || (s
.st_mode
& S_IFMT
) != S_IFDIR
|| (dirp
= opendir(d
)) == NULL
)
while ((dp
= readdir(dirp
)) != NULL
)
if ((*dp
->d_name
!= '.' || *p
== '.') && match(p
, m
, dp
->d_name
)) { /* match ^. explicitly */
lglob() globs a pattern agains a list of directory roots. e.g., (/tmp /usr /bin) "*"
will return a list with all the files in /tmp, /usr, and /bin. NULL on no match.
slashcount indicates the number of slashes to stick between the directory and the
matched name. e.g., for matching ////tmp/////foo*
static List
*lglob(List
*s
, char *p
, char *m
, size_t slashcount
) {
static size_t slashsize
= 0;
if (slashcount
+ 1 > slashsize
) {
slash
.w
= ealloc(slashcount
+ 1);
slash
.w
[slashcount
] = '\0';
slash
.w
[--slashcount
] = '/';
for (top
= r
= NULL
; s
!= NULL
; s
= s
->n
) {
if (!(s
->w
[0] == '/' && s
->w
[1] == '\0')) /* need to separate */
q
= concat(&slash
, q
); /* dir/name with slash */
Doglob globs a pathname in pattern form against a unix path. Returns the original
pattern (cleaned of metacharacters) on failure, or the globbed string(s).
static List
*doglob(char *w
, char *m
) {
static char *dir
= NULL
, *pattern
= NULL
, *metadir
= NULL
, *metapattern
= NULL
;
if ((psize
= strlen(w
) + 1) > dsize
|| dir
== NULL
) {
efree(dir
); efree(pattern
); efree(metadir
); efree(metapattern
);
metapattern
= ealloc(psize
);
*d
++ = *s
++, *md
++ = *m
++;
while (*s
!= '/' && *s
!= '\0')
*d
++ = *s
++, *md
++ = *m
++; /* get first directory component */
Special case: no slashes in the pattern, i.e., open the current directory.
Remember that w cannot consist of slashes alone (the other way *s could be
zero) since doglob gets called iff there's a metacharacter to be matched
matched
= dmatch(".", dir
, metadir
);
we must glob against current directory,
since the first character is not a slash.
matched
= dmatch(".", dir
, metadir
);
for (slashcount
= 0; *s
== '/'; s
++, m
++)
slashcount
++; /* skip slashes */
while (*s
!= '/' && *s
!= '\0')
*p
++ = *s
++, *mp
++ = *m
++; /* get pattern */
matched
= lglob(matched
, pattern
, metapattern
, slashcount
);
p
= pattern
, mp
= metapattern
;
end
: if (matched
== NULL
) {
static List
*sort(List
*s
) {
qsort(a
= list2array(s
, FALSE
), nel
, sizeof(char *), starstrcmp
);
for (t
= s
; t
!= NULL
; t
= t
->n
)