aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/rc/glob.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/rc/glob.c')
-rw-r--r--src/cmd/rc/glob.c178
1 files changed, 163 insertions, 15 deletions
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 <dirent.h>
-/* 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(n<arrlen(buf))
+ arr=buf, f=0;
+ else
+ arr=emalloc(n*sizeof(*arr)), f=1;
+
+ for(arg=left,n=0; arg!=right; arg=arg->link,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);
+ }
+}
+