aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/plumb/match.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2003-11-23 17:58:26 +0000
committerrsc <devnull@localhost>2003-11-23 17:58:26 +0000
commitb8c14089d8f4be73a908f82f62fce80ed2c14a8d (patch)
tree1d3db32a1ff576873d44d4bef60f13f020d5e10d /src/cmd/plumb/match.c
parent7763a61a3582ef330bca54f225e8ec5325fbd35e (diff)
downloadplan9port-b8c14089d8f4be73a908f82f62fce80ed2c14a8d.tar.gz
plan9port-b8c14089d8f4be73a908f82f62fce80ed2c14a8d.tar.bz2
plan9port-b8c14089d8f4be73a908f82f62fce80ed2c14a8d.zip
Plan 9 version, nothing tweaked yet.
Diffstat (limited to 'src/cmd/plumb/match.c')
-rw-r--r--src/cmd/plumb/match.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/src/cmd/plumb/match.c b/src/cmd/plumb/match.c
new file mode 100644
index 00000000..42a9232f
--- /dev/null
+++ b/src/cmd/plumb/match.c
@@ -0,0 +1,463 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <regexp.h>
+#include <thread.h>
+#include <plumb.h>
+#include "plumber.h"
+
+static char*
+nonnil(char *s)
+{
+ if(s == nil)
+ return "";
+ return s;
+}
+
+int
+verbis(int obj, Plumbmsg *m, Rule *r)
+{
+ switch(obj){
+ default:
+ fprint(2, "unimplemented 'is' object %d\n", obj);
+ break;
+ case OData:
+ return strcmp(m->data, r->qarg) == 0;
+ case ODst:
+ return strcmp(m->dst, r->qarg) == 0;
+ case OType:
+ return strcmp(m->type, r->qarg) == 0;
+ case OWdir:
+ return strcmp(m->wdir, r->qarg) == 0;
+ case OSrc:
+ return strcmp(m->src, r->qarg) == 0;
+ }
+ return 0;
+}
+
+static void
+setvar(Resub rs[10], char *match[10])
+{
+ int i, n;
+
+ for(i=0; i<10; i++){
+ free(match[i]);
+ match[i] = nil;
+ }
+ for(i=0; i<10 && rs[i].sp!=nil; i++){
+ n = rs[i].ep-rs[i].sp;
+ match[i] = emalloc(n+1);
+ memmove(match[i], rs[i].sp, n);
+ match[i][n] = '\0';
+ }
+}
+
+int
+clickmatch(Reprog *re, char *text, Resub rs[10], int click)
+{
+ char *clickp;
+ int i, w;
+ Rune r;
+
+ /* click is in characters, not bytes */
+ for(i=0; i<click && text[i]!='\0'; i+=w)
+ w = chartorune(&r, text+i);
+ clickp = text+i;
+ for(i=0; i<=click; i++){
+ memset(rs, 0, 10*sizeof(Resub));
+ if(regexec(re, text+i, rs, 10))
+ if(rs[0].sp<=clickp && clickp<=rs[0].ep)
+ return 1;
+ }
+ return 0;
+}
+
+int
+verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e)
+{
+ Resub rs[10];
+ char *clickval, *alltext;
+ int p0, p1, ntext;
+
+ memset(rs, 0, sizeof rs);
+ ntext = -1;
+ switch(obj){
+ default:
+ fprint(2, "unimplemented 'matches' object %d\n", obj);
+ break;
+ case OData:
+ clickval = plumblookup(m->attr, "click");
+ if(clickval == nil){
+ alltext = m->data;
+ ntext = m->ndata;
+ goto caseAlltext;
+ }
+ if(!clickmatch(r->regex, m->data, rs, atoi(clickval)))
+ break;
+ p0 = rs[0].sp - m->data;
+ p1 = rs[0].ep - m->data;
+ if(e->p0 >=0 && !(p0==e->p0 && p1==e->p1))
+ break;
+ e->clearclick = 1;
+ e->setdata = 1;
+ e->p0 = p0;
+ e->p1 = p1;
+ setvar(rs, e->match);
+ return 1;
+ case ODst:
+ alltext = m->dst;
+ goto caseAlltext;
+ case OType:
+ alltext = m->type;
+ goto caseAlltext;
+ case OWdir:
+ alltext = m->wdir;
+ goto caseAlltext;
+ case OSrc:
+ alltext = m->src;
+ /* fall through */
+ caseAlltext:
+ /* must match full text */
+ if(ntext < 0)
+ ntext = strlen(alltext);
+ if(!regexec(r->regex, alltext, rs, 10) || rs[0].sp!=alltext || rs[0].ep!=alltext+ntext)
+ break;
+ setvar(rs, e->match);
+ return 1;
+ }
+ return 0;
+}
+
+int
+isfile(char *file, ulong maskon, ulong maskoff)
+{
+ Dir *d;
+ int mode;
+
+ d = dirstat(file);
+ if(d == nil)
+ return 0;
+ mode = d->mode;
+ free(d);
+ if((mode & maskon) == 0)
+ return 0;
+ if(mode & maskoff)
+ return 0;
+ return 1;
+}
+
+char*
+absolute(char *dir, char *file)
+{
+ char *p;
+
+ if(file[0] == '/')
+ return estrdup(file);
+ p = emalloc(strlen(dir)+1+strlen(file)+1);
+ sprint(p, "%s/%s", dir, file);
+ return cleanname(p);
+}
+
+int
+verbisfile(int obj, Plumbmsg *m, Rule *r, Exec *e, ulong maskon, ulong maskoff, char **var)
+{
+ char *file;
+
+ switch(obj){
+ default:
+ fprint(2, "unimplemented 'isfile' object %d\n", obj);
+ break;
+ case OArg:
+ file = absolute(m->wdir, expand(e, r->arg, nil));
+ if(isfile(file, maskon, maskoff)){
+ *var = file;
+ return 1;
+ }
+ free(file);
+ break;
+ case OData:
+ case OWdir:
+ file = absolute(m->wdir, obj==OData? m->data : m->wdir);
+ if(isfile(file, maskon, maskoff)){
+ *var = file;
+ return 1;
+ }
+ free(file);
+ break;
+ }
+ return 0;
+}
+
+int
+verbset(int obj, Plumbmsg *m, Rule *r, Exec *e)
+{
+ char *new;
+
+ switch(obj){
+ default:
+ fprint(2, "unimplemented 'is' object %d\n", obj);
+ break;
+ case OData:
+ new = estrdup(expand(e, r->arg, nil));
+ m->ndata = strlen(new);
+ free(m->data);
+ m->data = new;
+ e->p0 = -1;
+ e->p1 = -1;
+ e->setdata = 0;
+ return 1;
+ case ODst:
+ new = estrdup(expand(e, r->arg, nil));
+ free(m->dst);
+ m->dst = new;
+ return 1;
+ case OType:
+ new = estrdup(expand(e, r->arg, nil));
+ free(m->type);
+ m->type = new;
+ return 1;
+ case OWdir:
+ new = estrdup(expand(e, r->arg, nil));
+ free(m->wdir);
+ m->wdir = new;
+ return 1;
+ case OSrc:
+ new = estrdup(expand(e, r->arg, nil));
+ free(m->src);
+ m->src = new;
+ return 1;
+ }
+ return 0;
+}
+
+int
+verbadd(int obj, Plumbmsg *m, Rule *r, Exec *e)
+{
+ switch(obj){
+ default:
+ fprint(2, "unimplemented 'add' object %d\n", obj);
+ break;
+ case OAttr:
+ m->attr = plumbaddattr(m->attr, plumbunpackattr(expand(e, r->arg, nil)));
+ return 1;
+ }
+ return 0;
+}
+
+int
+verbdelete(int obj, Plumbmsg *m, Rule *r, Exec *e)
+{
+ char *a;
+
+ switch(obj){
+ default:
+ fprint(2, "unimplemented 'delete' object %d\n", obj);
+ break;
+ case OAttr:
+ a = expand(e, r->arg, nil);
+ if(plumblookup(m->attr, a) == nil)
+ break;
+ m->attr = plumbdelattr(m->attr, a);
+ return 1;
+ }
+ return 0;
+}
+
+int
+matchpat(Plumbmsg *m, Exec *e, Rule *r)
+{
+ switch(r->verb){
+ default:
+ fprint(2, "unimplemented verb %d\n", r->verb);
+ break;
+ case VAdd:
+ return verbadd(r->obj, m, r, e);
+ case VDelete:
+ return verbdelete(r->obj, m, r, e);
+ case VIs:
+ return verbis(r->obj, m, r);
+ case VIsdir:
+ return verbisfile(r->obj, m, r, e, DMDIR, 0, &e->dir);
+ case VIsfile:
+ return verbisfile(r->obj, m, r, e, ~DMDIR, DMDIR, &e->file);
+ case VMatches:
+ return verbmatches(r->obj, m, r, e);
+ case VSet:
+ verbset(r->obj, m, r, e);
+ return 1;
+ }
+ return 0;
+}
+
+void
+freeexec(Exec *exec)
+{
+ int i;
+
+ if(exec == nil)
+ return;
+ free(exec->dir);
+ free(exec->file);
+ for(i=0; i<10; i++)
+ free(exec->match[i]);
+ free(exec);
+}
+
+Exec*
+newexec(Plumbmsg *m)
+{
+ Exec *exec;
+
+ exec = emalloc(sizeof(Exec));
+ exec->msg = m;
+ exec->p0 = -1;
+ exec->p1 = -1;
+ return exec;
+}
+
+void
+rewrite(Plumbmsg *m, Exec *e)
+{
+ Plumbattr *a, *prev;
+
+ if(e->clearclick){
+ prev = nil;
+ for(a=m->attr; a!=nil; a=a->next){
+ if(strcmp(a->name, "click") == 0){
+ if(prev == nil)
+ m->attr = a->next;
+ else
+ prev->next = a->next;
+ free(a->name);
+ free(a->value);
+ free(a);
+ break;
+ }
+ prev = a;
+ }
+ if(e->setdata){
+ free(m->data);
+ m->data = estrdup(expand(e, "$0", nil));
+ m->ndata = strlen(m->data);
+ }
+ }
+}
+
+char**
+buildargv(char *s, Exec *e)
+{
+ char **av;
+ int ac;
+
+ ac = 0;
+ av = nil;
+ for(;;){
+ av = erealloc(av, (ac+1) * sizeof(char*));
+ av[ac] = nil;
+ while(*s==' ' || *s=='\t')
+ s++;
+ if(*s == '\0')
+ break;
+ av[ac++] = estrdup(expand(e, s, &s));
+ }
+ return av;
+}
+
+Exec*
+matchruleset(Plumbmsg *m, Ruleset *rs)
+{
+ int i;
+ Exec *exec;
+
+ if(m->dst!=nil && m->dst[0]!='\0' && rs->port!=nil && strcmp(m->dst, rs->port)!=0)
+ return nil;
+ exec = newexec(m);
+ for(i=0; i<rs->npat; i++)
+ if(!matchpat(m, exec, rs->pat[i])){
+ freeexec(exec);
+ return nil;
+ }
+ if(rs->port!=nil && (m->dst==nil || m->dst[0]=='\0')){
+ free(m->dst);
+ m->dst = estrdup(rs->port);
+ }
+ rewrite(m, exec);
+ return exec;
+}
+
+enum
+{
+ NARGS = 100,
+ NARGCHAR = 8*1024,
+ EXECSTACK = 4096+(NARGS+1)*sizeof(char*)+NARGCHAR
+};
+
+/* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
+void
+stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
+{
+ int i, n;
+ char *s, *a;
+
+ s = args;
+ for(i=0; i<NARGS; i++){
+ a = inargv[i];
+ if(a == nil)
+ break;
+ n = strlen(a)+1;
+ if((s-args)+n >= NARGCHAR) /* too many characters */
+ break;
+ argv[i] = s;
+ memmove(s, a, n);
+ s += n;
+ free(a);
+ }
+ argv[i] = nil;
+}
+
+
+void
+execproc(void *v)
+{
+ char **av;
+ char buf[1024], *args[NARGS+1], argc[NARGCHAR];
+
+ rfork(RFFDG);
+ close(0);
+ open("/dev/null", OREAD);
+ av = v;
+ stackargv(av, args, argc);
+ free(av);
+ procexec(nil, args[0], args);
+ if(args[0][0]!='/' && strncmp(args[0], "./", 2)!=0 && strncmp(args[0], "../", 3)!=0)
+ snprint(buf, sizeof buf, "/bin/%s", args[0]);
+ procexec(nil, buf, args);
+ threadexits("can't exec");
+}
+
+char*
+startup(Ruleset *rs, Exec *e)
+{
+ char **argv;
+ int i;
+
+ if(rs != nil)
+ for(i=0; i<rs->nact; i++){
+ if(rs->act[i]->verb == VStart)
+ goto Found;
+ if(rs->act[i]->verb == VClient){
+ if(e->msg->dst==nil || e->msg->dst[0]=='\0')
+ return "no port for \"client\" rule";
+ e->holdforclient = 1;
+ goto Found;
+ }
+ }
+ return "no start action for plumb message";
+
+Found:
+ argv = buildargv(rs->act[i]->arg, e);
+ if(argv[0] == nil)
+ return "empty argument list";
+ proccreate(execproc, argv, EXECSTACK);
+ return nil;
+}