From 5baa63ab502c98f6f62368302d92b6b90f9fcd26 Mon Sep 17 00:00:00 2001 From: Nicholas Date: Mon, 15 Nov 2021 18:43:39 -0800 Subject: Feat: globbing Introduced shell globbing --- src/cmd/rc/glob.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 163 insertions(+), 15 deletions(-) (limited to 'src/cmd/rc/glob.c') diff --git a/src/cmd/rc/glob.c b/src/cmd/rc/glob.c index 9d88b59..eee0318 100644 --- a/src/cmd/rc/glob.c +++ b/src/cmd/rc/glob.c @@ -1,25 +1,16 @@ #include "rc.h" +#include -/* removes glob marks in place */ -void -deglob(char *s) -{ - char *t = s; - do{ - if(*t==GLOB) - t++; - *s++=*t; - }while(*t++); -} +Word *globmatch = nil; +static char *globbuf = nil; int match(char *s, char *p, int stop) { rune rs, rp, lo, hi; - int neg, hit; + int np, ns, neg, hit; - utf8·decode(p, &rp), utf8·decode(s, &rs); - for(; *p && *p != stop; s+=utf8·decode(s,&rs),p+=utf8·decode(p,&rp)){ + 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) @@ -34,10 +25,12 @@ match(char *s, char *p, int stop) return 0; break; case '*': + p += utf8·decode(p, &rp); for(;;){ - p += utf8·decode(p, &rp); if(match(s, p, stop)) return 1; + if(!*s) + break; s += utf8·decode(s, &rs); } return 0; @@ -80,3 +73,158 @@ match(char *s, char *p, int stop) 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(os·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); + } +} + -- cgit v1.2.1