plan9fox/sys/src/cmd/ip/glob.c
2011-03-30 19:35:09 +03:00

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;
}