aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas <nbnoll@eml.cc>2021-11-15 18:43:39 -0800
committerNicholas <nbnoll@eml.cc>2021-11-15 18:43:39 -0800
commit5baa63ab502c98f6f62368302d92b6b90f9fcd26 (patch)
treebbfa323a387b94f8eef21c90a27e26f1d7d14728
parentd2c554ee0deeb64a805549d230ff8467ff6601f4 (diff)
Feat: globbing
Introduced shell globbing
-rw-r--r--src/cmd/rc/exec.c47
-rw-r--r--src/cmd/rc/glob.c178
-rw-r--r--src/cmd/rc/rc.h7
3 files changed, 206 insertions, 26 deletions
diff --git a/src/cmd/rc/exec.c b/src/cmd/rc/exec.c
index a551d52..62375df 100644
--- a/src/cmd/rc/exec.c
+++ b/src/cmd/rc/exec.c
@@ -103,8 +103,7 @@ freelist(Word *w)
}
-static
-void
+static void
pushlist(void)
{
List *stack = emalloc(sizeof(*stack));
@@ -115,8 +114,7 @@ pushlist(void)
runner->args = stack;
}
-static
-void
+static void
poplist(void)
{
List *stack = runner->args;
@@ -128,6 +126,24 @@ poplist(void)
efree(stack);
}
+static void
+globargs(void)
+{
+ Word *arg;
+
+ globmatch = nil;
+ globlist(runner->args->word);
+ poplist();
+ pushlist();
+ if(globmatch){
+ for(arg=globmatch; arg->link; arg = arg->link)
+ ;
+ arg->link= runner->args->word;
+ runner->args->word = globmatch;
+ }
+}
+
+
/* system interop */
static
Word*
@@ -792,7 +808,7 @@ Xdollar(void)
return;
}
s = runner->args->word->str;
- // deglob(s);
+ deglob(s);
n = 0;
for(t = s;'0'<=*t && *t<='9';t++)
@@ -846,7 +862,7 @@ getindex(Word *array, int len, Word *index, Word *tail)
tail = getindex(array, len, index->link, tail);
s = index->str;
- //deglob(s)
+ deglob(s);
m = 0, n = 0;
while('0' <= *s && *s <= '9')
@@ -881,7 +897,7 @@ Xindex(void)
return;
}
s = runner->args->word->str;
- //deglob(s)
+ deglob(s);
val = var(s)->val;
poplist();
@@ -935,6 +951,12 @@ Xjoin(void)
}
void
+Xglob(void)
+{
+ globargs();
+}
+
+void
Xassign(void)
{
Var *v;
@@ -943,11 +965,11 @@ Xassign(void)
Xerror("variable name not singleton!\n");
return;
}
- //deglob(runq->argv->words->word);
+ deglob(runner->args->word->str);
v = var(runner->args->word->str);
poplist();
- //globlist();
+ globargs();
freewords(v->val);
v->val = runner->args->word;
v->new = 1;
@@ -991,7 +1013,7 @@ Xlocal(void)
Xerror("variable name must be singleton\n");
return;
}
- //deglob(shell->args->word->str);
+ deglob(runner->args->word->str);
runner->local = makevar(strdup(runner->args->word->str), runner->local);
runner->local->val = copywords(runner->args->link->word, nil);
@@ -1141,6 +1163,7 @@ Xbasic(void)
int pid, status;
struct Builtin *b;
+ globargs();
arg = runner->args->word;
if(!arg){
Xerror("empty argument list\n");
@@ -1189,6 +1212,8 @@ Xcount(void)
}
str = runner->args->word->str;
+ deglob(str);
+
arg = var(str)->val;
poplist();
@@ -1209,6 +1234,8 @@ Xflat(void)
}
str = runner->args->word->str;
+ deglob(str);
+
arg = var(str)->val;
poplist();
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);
+ }
+}
+
diff --git a/src/cmd/rc/rc.h b/src/cmd/rc/rc.h
index f7533a0..b51b78b 100644
--- a/src/cmd/rc/rc.h
+++ b/src/cmd/rc/rc.h
@@ -1,3 +1,4 @@
+#pragma once
#include <u.h>
#include <base.h>
@@ -265,4 +266,8 @@ void freecode(Code *c);
/* glob.c */
#define GLOB ((char)(0x01))
-int match(char *, char *, int);
+extern Word *globmatch;
+
+void deglob(char *);
+void globlist(Word *);
+int match(char *, char *, int);