Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

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