#include "rc.h" #include Word *globmatch = nil; static char *globbuf = nil; int match(char *s, char *p, int stop) { rune rs, rp, lo, hi; int np, ns, neg, hit; for(np=utf8·decode(p, &rp), ns=utf8·decode(s, &rs); *p && *p != stop; s+=ns, p+=np, ns=utf8·decode(s,&rs), np=utf8·decode(p,&rp)){ /* fast path: normal character */ if(*p != GLOB){ if(rp != rs) return 0; continue; } /* unglob */ switch(*++p){ case GLOB: if(*s != GLOB) return 0; break; case '*': p += utf8·decode(p, &rp); for(;;){ if(match(s, p, stop)) return 1; if(!*s) break; s += utf8·decode(s, &rs); } return 0; case '?': if(*s == 0) return 0; break; case '[': if(*s==0) return 0; if((neg=*++p=='~')) p++; hit = 0; while(*p != ']'){ if(*p==0) return 0; /* syntax error */ p += utf8·decode(p, &lo); if(*p != '-') hi = lo; else{ if(*++p == 0) return 0; /* syntax error */ p += utf8·decode(p, &hi); if(hi < lo) rp=lo, lo=hi, hi=rp; } if(lo <= rs && rs <= hi) hit = 1; } if(neg) hit=!hit; if(!hit) return 0; break; } } return *s == 0; } static int matchpath(char *s, char *p) { if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0) && p[0] != '.') return 0; return match(s, p, '/'); } /* removes glob marks in place */ void deglob(char *s) { char *t = s; do{ if(*t==GLOB) t++; *s++=*t; }while(*t++); } #define DIRLEN 256 static int globsize(char *p) { ulong is=0, len=DIRLEN+1; for(;*p;p++){ if(*p!=GLOB) len++; else{ p++; if(*p != GLOB) is++; len += (*p == '*') ? DIRLEN : 1; } } return is ? len : 0; } #undef DIRLEN static void globpath(char *p, char *buf) { int fd; char *s, *base; DIR *dir; struct dirent *sub; if(*p == 0){ globmatch = makeword(globbuf, globmatch); return; } s=buf, base=p; while(*base){ if(*base==GLOB) break; *s = *base++; if(*s++ == '/'){ buf=s, p=base; } } /* we ran out of pattern, no glob */ if(*base == 0){ *s = 0; if(fs·exists(globbuf, 0)) globmatch = makeword(globbuf, globmatch); return; } *buf = 0; /* read the directory and recurse for any entry that matches */ /* TODO: replace ugly C interface with something else */ fd = openat(AT_FDCWD, globbuf[0] ? globbuf : ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); if(fd < 0) return; if(!(dir = fdopendir(fd))){ close(fd); return; } while(*base != '/' && *base != 0) base++; while((sub=readdir(dir))){ strcpy(buf, sub->d_name); if(matchpath(buf, p)){ for(s=buf; *s; s++) ; globpath(base, s); } } closedir(dir); } static void sort(Word *left, Word *right) { int n,f; Word *arg; char **arr, *buf[256]; for(arg=left,n=0; arg != right; arg=arg->link) n++; if(nlink,n++) arr[n] = arg->str; sort·string(n,arr); for(arg=left,n=0; arg!=right; arg=arg->link, n++) arg->str = arr[n]; if(f) efree(arr); } static void glob(char *p) { Word *old = globmatch; int nglob = globsize(p); if(!nglob) goto nomatch; globbuf = emalloc(nglob); globbuf[0] = 0; globpath(p, globbuf); efree(globbuf); if(globmatch == old) goto nomatch; /* fallthrough */ match: sort(globmatch, old); return; nomatch: deglob(p); globmatch = makeword(p, globmatch); } void globlist(Word *g) { if(g){ globlist(g->link); glob(g->str); } }