220 lines
3.2 KiB
C
220 lines
3.2 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <regexp.h>
|
|
#include <String.h>
|
|
#include "glob.h"
|
|
|
|
/*
|
|
* I wrote this glob so that there would be no limit
|
|
* on element or path size. The one in rc is probably
|
|
* better, certainly faster. - presotto
|
|
*/
|
|
|
|
static Glob*
|
|
globnew(void)
|
|
{
|
|
Glob *g;
|
|
|
|
g = mallocz(sizeof(*g), 1);
|
|
if(g == nil)
|
|
sysfatal("globnew: %r");
|
|
return g;
|
|
}
|
|
|
|
static void
|
|
globfree1(Glob *g)
|
|
{
|
|
s_free(g->glob);
|
|
free(g);
|
|
}
|
|
|
|
static void
|
|
globfree(Glob *g)
|
|
{
|
|
Glob *next;
|
|
|
|
for(; g != nil; g = next){
|
|
next = g->next;
|
|
globfree1(g);
|
|
}
|
|
}
|
|
|
|
static Globlist*
|
|
globlistnew(char *x)
|
|
{
|
|
Globlist *gl;
|
|
|
|
gl = mallocz(sizeof *gl, 1);
|
|
if(gl == nil)
|
|
sysfatal("globlistnew: %r");
|
|
gl->first = globnew();
|
|
gl->first->glob = s_copy(x);
|
|
gl->l = &gl->first->next;
|
|
return gl;
|
|
}
|
|
|
|
void
|
|
globlistfree(Globlist *gl)
|
|
{
|
|
if(gl == nil)
|
|
return;
|
|
globfree(gl->first);
|
|
free(gl);
|
|
}
|
|
|
|
void
|
|
globadd(Globlist *gl, char *dir, char *file)
|
|
{
|
|
Glob *g;
|
|
|
|
g = globnew();
|
|
g->glob = s_copy(dir);
|
|
if(strcmp(dir, "/") != 0 && *dir != 0)
|
|
s_append(g->glob, "/");
|
|
s_append(g->glob, file);
|
|
*(gl->l) = g;
|
|
gl->l = &(g->next);
|
|
}
|
|
|
|
static void
|
|
globdir(Globlist *gl, char *dir, Reprog *re)
|
|
{
|
|
Dir *d;
|
|
int i, n, fd;
|
|
|
|
if(*dir == 0)
|
|
fd = open(".", OREAD);
|
|
else
|
|
fd = open(dir, OREAD);
|
|
if(fd < 0)
|
|
return;
|
|
n = dirreadall(fd, &d);
|
|
if(n == 0)
|
|
return;
|
|
close(fd);
|
|
for(i = 0; i < n; i++)
|
|
if(regexec(re, d[i].name, nil, 0))
|
|
globadd(gl, dir, d[i].name);
|
|
free(d);
|
|
}
|
|
|
|
static void
|
|
globdot(Globlist *gl, char *dir)
|
|
{
|
|
Dir *d;
|
|
|
|
if(*dir == 0){
|
|
globadd(gl, "", ".");
|
|
return;
|
|
}
|
|
d = dirstat(dir);
|
|
if(d == nil)
|
|
return;
|
|
if(d->qid.type & QTDIR)
|
|
globadd(gl, dir, ".");
|
|
free(d);
|
|
}
|
|
|
|
static void
|
|
globnext(Globlist *gl, char *pattern)
|
|
{
|
|
String *np;
|
|
Glob *g, *inlist;
|
|
Reprog *re;
|
|
int c;
|
|
|
|
/* nothing left */
|
|
if(*pattern == 0)
|
|
return;
|
|
|
|
inlist = gl->first;
|
|
gl->first = nil;
|
|
gl->l = &gl->first;
|
|
|
|
/* pick off next pattern and turn into a reg exp */
|
|
np = s_new();
|
|
s_putc(np, '^');
|
|
for(; c = *pattern; pattern++){
|
|
if(c == '/'){
|
|
pattern++;
|
|
break;
|
|
}
|
|
switch(c){
|
|
case '|':
|
|
case '+':
|
|
case '.':
|
|
case '^':
|
|
case '$':
|
|
case '(':
|
|
case ')':
|
|
s_putc(np, '\\');
|
|
s_putc(np, c);
|
|
break;
|
|
case '?':
|
|
s_putc(np, '.');
|
|
break;
|
|
case '*':
|
|
s_putc(np, '.');
|
|
s_putc(np, '*');
|
|
break;
|
|
default:
|
|
s_putc(np, c);
|
|
break;
|
|
}
|
|
}
|
|
s_putc(np, '$');
|
|
s_terminate(np);
|
|
if(strcmp(s_to_c(np), "^\\.$") == 0){
|
|
/* anything that's a directory works */
|
|
for(g = inlist; g != nil; g = g->next)
|
|
globdot(gl, s_to_c(g->glob));
|
|
} else {
|
|
re = regcomp(s_to_c(np));
|
|
|
|
/* run input list as directories */
|
|
for(g = inlist; g != nil; g = g->next)
|
|
globdir(gl, s_to_c(g->glob), re);
|
|
free(re);
|
|
}
|
|
s_free(np);
|
|
globfree(inlist);
|
|
|
|
if(gl->first != nil)
|
|
globnext(gl, pattern);
|
|
}
|
|
|
|
char *
|
|
globiter(Globlist *gl)
|
|
{
|
|
Glob *g;
|
|
char *s;
|
|
|
|
if(gl->first == nil)
|
|
return nil;
|
|
g = gl->first;
|
|
gl->first = g->next;
|
|
if(gl->first == nil)
|
|
gl->l = &gl->first;
|
|
s = strdup(s_to_c(g->glob));
|
|
if(s == nil)
|
|
sysfatal("globiter: %r");
|
|
globfree1(g);
|
|
return s;
|
|
}
|
|
|
|
Globlist*
|
|
glob(char *pattern)
|
|
{
|
|
Globlist *gl;
|
|
|
|
if(pattern == nil || *pattern == 0)
|
|
return nil;
|
|
if(*pattern == '/'){
|
|
pattern++;
|
|
gl = globlistnew("/");
|
|
} else
|
|
gl = globlistnew("");
|
|
globnext(gl, pattern);
|
|
return gl;
|
|
}
|