ref: 9ced712a2ad70557a8ceb89ff3b969b92c85f68c
dir: /sys/src/cmd/ip/glob.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; }