URI:
       tmatch.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tmatch.c (8090B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <regexp.h>
            5 #include <thread.h>
            6 #include <plumb.h>
            7 #include "plumber.h"
            8 
            9 /*
           10 static char*
           11 nonnil(char *s)
           12 {
           13         if(s == nil)
           14                 return "";
           15         return s;
           16 }
           17 */
           18 
           19 int
           20 verbis(int obj, Plumbmsg *m, Rule *r)
           21 {
           22         switch(obj){
           23         default:
           24                 fprint(2, "unimplemented 'is' object %d\n", obj);
           25                 break;
           26         case OData:
           27                 return strcmp(m->data, r->qarg) == 0;
           28         case ODst:
           29                 return strcmp(m->dst, r->qarg) == 0;
           30         case OType:
           31                 return strcmp(m->type, r->qarg) == 0;
           32         case OWdir:
           33                 return strcmp(m->wdir, r->qarg) == 0;
           34         case OSrc:
           35                 return strcmp(m->src, r->qarg) == 0;
           36         }
           37         return 0;
           38 }
           39 
           40 static void
           41 setvar(Resub rs[10], char *match[10])
           42 {
           43         int i, n;
           44 
           45         for(i=0; i<10; i++){
           46                 free(match[i]);
           47                 match[i] = nil;
           48         }
           49         for(i=0; i<10 && rs[i].s.sp!=nil; i++){
           50                 n = rs[i].e.ep-rs[i].s.sp;
           51                 match[i] = emalloc(n+1);
           52                 memmove(match[i], rs[i].s.sp, n);
           53                 match[i][n] = '\0';
           54         }
           55 }
           56 
           57 int
           58 clickmatch(Reprog *re, char *text, Resub rs[10], int click)
           59 {
           60         char *clickp;
           61         int i, w;
           62         Rune r;
           63 
           64         /* click is in characters, not bytes */
           65         for(i=0; i<click && text[i]!='\0'; i+=w)
           66                 w = chartorune(&r, text+i);
           67         clickp = text+i;
           68         for(i=0; i<=click; i++){
           69                 memset(rs, 0, 10*sizeof(Resub));
           70                 if(regexec(re, text+i, rs, 10))
           71                         if(rs[0].s.sp<=clickp && clickp<=rs[0].e.ep)
           72                                 return 1;
           73         }
           74         return 0;
           75 }
           76 
           77 int
           78 verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e)
           79 {
           80         Resub rs[10];
           81         char *clickval, *alltext;
           82         int p0, p1, ntext;
           83 
           84         memset(rs, 0, sizeof rs);
           85         ntext = -1;
           86         switch(obj){
           87         default:
           88                 fprint(2, "unimplemented 'matches' object %d\n", obj);
           89                 break;
           90         case OData:
           91                 clickval = plumblookup(m->attr, "click");
           92                 if(clickval == nil){
           93                         alltext = m->data;
           94                         ntext = m->ndata;
           95                         goto caseAlltext;
           96                 }
           97                 if(!clickmatch(r->regex, m->data, rs, atoi(clickval)))
           98                         break;
           99                 p0 = rs[0].s.sp - m->data;
          100                 p1 = rs[0].e.ep - m->data;
          101                 if(e->p0 >=0 && !(p0==e->p0 && p1==e->p1))
          102                         break;
          103                 e->clearclick = 1;
          104                 e->setdata = 1;
          105                 e->p0 = p0;
          106                 e->p1 = p1;
          107                 setvar(rs, e->match);
          108                 return 1;
          109         case ODst:
          110                 alltext = m->dst;
          111                 goto caseAlltext;
          112         case OType:
          113                 alltext = m->type;
          114                 goto caseAlltext;
          115         case OWdir:
          116                 alltext = m->wdir;
          117                 goto caseAlltext;
          118         case OSrc:
          119                 alltext = m->src;
          120                 /* fall through */
          121         caseAlltext:
          122                 /* must match full text */
          123                 if(ntext < 0)
          124                         ntext = strlen(alltext);
          125                 if(!regexec(r->regex, alltext, rs, 10) || rs[0].s.sp!=alltext || rs[0].e.ep!=alltext+ntext)
          126                         break;
          127                 setvar(rs, e->match);
          128                 return 1;
          129         }
          130         return 0;
          131 }
          132 
          133 int
          134 isfile(char *file, ulong maskon, ulong maskoff)
          135 {
          136         Dir *d;
          137         int mode;
          138 
          139         d = dirstat(file);
          140         if(d == nil)
          141                 return 0;
          142         mode = d->mode;
          143         free(d);
          144         if((mode & maskon) == 0)
          145                 return 0;
          146         if(mode & maskoff)
          147                 return 0;
          148         return 1;
          149 }
          150 
          151 char*
          152 absolute(char *dir, char *file)
          153 {
          154         char *p;
          155 
          156         if(file[0] == '/')
          157                 return estrdup(file);
          158         p = emalloc(strlen(dir)+1+strlen(file)+1);
          159         sprint(p, "%s/%s", dir, file);
          160         return cleanname(p);
          161 }
          162 
          163 int
          164 verbisfile(int obj, Plumbmsg *m, Rule *r, Exec *e, ulong maskon, ulong maskoff, char **var)
          165 {
          166         char *file;
          167 
          168         switch(obj){
          169         default:
          170                 fprint(2, "unimplemented 'isfile' object %d\n", obj);
          171                 break;
          172         case OArg:
          173                 file = absolute(m->wdir, expand(e, r->arg, nil));
          174                 if(isfile(file, maskon, maskoff)){
          175                         *var = file;
          176                         return 1;
          177                 }
          178                 free(file);
          179                 break;
          180         case OData:
          181         case OWdir:
          182                 file = absolute(m->wdir, obj==OData? m->data : m->wdir);
          183                 if(isfile(file, maskon, maskoff)){
          184                         *var = file;
          185                         return 1;
          186                 }
          187                 free(file);
          188                 break;
          189         }
          190         return 0;
          191 }
          192 
          193 int
          194 verbset(int obj, Plumbmsg *m, Rule *r, Exec *e)
          195 {
          196         char *new;
          197 
          198         switch(obj){
          199         default:
          200                 fprint(2, "unimplemented 'is' object %d\n", obj);
          201                 break;
          202         case OData:
          203                 new = estrdup(expand(e, r->arg, nil));
          204                 m->ndata = strlen(new);
          205                 free(m->data);
          206                 m->data = new;
          207                 e->p0 = -1;
          208                 e->p1 = -1;
          209                 e->setdata = 0;
          210                 return 1;
          211         case ODst:
          212                 new = estrdup(expand(e, r->arg, nil));
          213                 free(m->dst);
          214                 m->dst = new;
          215                 return 1;
          216         case OType:
          217                 new = estrdup(expand(e, r->arg, nil));
          218                 free(m->type);
          219                 m->type = new;
          220                 return 1;
          221         case OWdir:
          222                 new = estrdup(expand(e, r->arg, nil));
          223                 free(m->wdir);
          224                 m->wdir = new;
          225                 return 1;
          226         case OSrc:
          227                 new = estrdup(expand(e, r->arg, nil));
          228                 free(m->src);
          229                 m->src = new;
          230                 return 1;
          231         }
          232         return 0;
          233 }
          234 
          235 int
          236 verbadd(int obj, Plumbmsg *m, Rule *r, Exec *e)
          237 {
          238         switch(obj){
          239         default:
          240                 fprint(2, "unimplemented 'add' object %d\n", obj);
          241                 break;
          242         case OAttr:
          243                 m->attr = plumbaddattr(m->attr, plumbunpackattr(expand(e, r->arg, nil)));
          244                 return 1;
          245         }
          246         return 0;
          247 }
          248 
          249 int
          250 verbdelete(int obj, Plumbmsg *m, Rule *r, Exec *e)
          251 {
          252         char *a;
          253 
          254         switch(obj){
          255         default:
          256                 fprint(2, "unimplemented 'delete' object %d\n", obj);
          257                 break;
          258         case OAttr:
          259                 a = expand(e, r->arg, nil);
          260                 if(plumblookup(m->attr, a) == nil)
          261                         break;
          262                 m->attr = plumbdelattr(m->attr, a);
          263                 return 1;
          264         }
          265         return 0;
          266 }
          267 
          268 int
          269 matchpat(Plumbmsg *m, Exec *e, Rule *r)
          270 {
          271         switch(r->verb){
          272         default:
          273                 fprint(2, "unimplemented verb %d\n", r->verb);
          274                 break;
          275         case VAdd:
          276                 return verbadd(r->obj, m, r, e);
          277         case VDelete:
          278                 return verbdelete(r->obj, m, r, e);
          279         case VIs:
          280                 return verbis(r->obj, m, r);
          281         case VIsdir:
          282                 return verbisfile(r->obj, m, r, e, DMDIR, 0, &e->dir);
          283         case VIsfile:
          284                 return verbisfile(r->obj, m, r, e, ~DMDIR, DMDIR, &e->file);
          285         case VMatches:
          286                 return verbmatches(r->obj, m, r, e);
          287         case VSet:
          288                 verbset(r->obj, m, r, e);
          289                 return 1;
          290         }
          291         return 0;
          292 }
          293 
          294 void
          295 freeexec(Exec *exec)
          296 {
          297         int i;
          298 
          299         if(exec == nil)
          300                 return;
          301         free(exec->dir);
          302         free(exec->file);
          303         for(i=0; i<10; i++)
          304                 free(exec->match[i]);
          305         free(exec);
          306 }
          307 
          308 Exec*
          309 newexec(Plumbmsg *m)
          310 {
          311         Exec *exec;
          312 
          313         exec = emalloc(sizeof(Exec));
          314         exec->msg = m;
          315         exec->p0 = -1;
          316         exec->p1 = -1;
          317         return exec;
          318 }
          319 
          320 void
          321 rewrite(Plumbmsg *m, Exec *e)
          322 {
          323         Plumbattr *a, *prev;
          324 
          325         if(e->clearclick){
          326                 prev = nil;
          327                 for(a=m->attr; a!=nil; a=a->next){
          328                         if(strcmp(a->name, "click") == 0){
          329                                 if(prev == nil)
          330                                         m->attr = a->next;
          331                                 else
          332                                         prev->next = a->next;
          333                                 free(a->name);
          334                                 free(a->value);
          335                                 free(a);
          336                                 break;
          337                         }
          338                         prev = a;
          339                 }
          340                 if(e->setdata){
          341                         free(m->data);
          342                         m->data = estrdup(expand(e, "$0", nil));
          343                         m->ndata = strlen(m->data);
          344                 }
          345         }
          346 }
          347 
          348 char**
          349 buildargv(char *s, Exec *e)
          350 {
          351         char **av;
          352         int ac;
          353 
          354         ac = 0;
          355         av = nil;
          356         for(;;){
          357                 av = erealloc(av, (ac+1) * sizeof(char*));
          358                 av[ac] = nil;
          359                 while(*s==' ' || *s=='\t')
          360                         s++;
          361                 if(*s == '\0')
          362                         break;
          363                 av[ac++] = estrdup(expand(e, s, &s));
          364         }
          365         return av;
          366 }
          367 
          368 Exec*
          369 matchruleset(Plumbmsg *m, Ruleset *rs)
          370 {
          371         int i;
          372         Exec *exec;
          373 
          374         if(m->dst!=nil && m->dst[0]!='\0' && rs->port!=nil && strcmp(m->dst, rs->port)!=0)
          375                 return nil;
          376         exec = newexec(m);
          377         for(i=0; i<rs->npat; i++)
          378                 if(!matchpat(m, exec, rs->pat[i])){
          379                         freeexec(exec);
          380                         return nil;
          381                 }
          382         if(rs->port!=nil && (m->dst==nil || m->dst[0]=='\0')){
          383                 free(m->dst);
          384                 m->dst = estrdup(rs->port);
          385         }
          386         rewrite(m, exec);
          387         return exec;
          388 }
          389 
          390 enum
          391 {
          392         NARGS                = 100,
          393         NARGCHAR        = 8*1024,
          394         EXECSTACK         = 32*1024+(NARGS+1)*sizeof(char*)+NARGCHAR
          395 };
          396 
          397 /* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
          398 void
          399 stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
          400 {
          401         int i, n;
          402         char *s, *a;
          403 
          404         s = args;
          405         for(i=0; i<NARGS; i++){
          406                 a = inargv[i];
          407                 if(a == nil)
          408                         break;
          409                 n = strlen(a)+1;
          410                 if((s-args)+n >= NARGCHAR)        /* too many characters */
          411                         break;
          412                 argv[i] = s;
          413                 memmove(s, a, n);
          414                 s += n;
          415                 free(a);
          416         }
          417         argv[i] = nil;
          418 }
          419 
          420 
          421 void
          422 execproc(void *v)
          423 {
          424         int fd[3];
          425         char **av;
          426         char *args[NARGS+1], argc[NARGCHAR];
          427 
          428         fd[0] = open("/dev/null", OREAD);
          429         fd[1] = dup(1, -1);
          430         fd[2] = dup(2, -1);
          431         av = v;
          432         stackargv(av, args, argc);
          433         free(av);
          434         threadexec(nil, fd, args[0], args);
          435         threadexits("can't exec");
          436 }
          437 
          438 char*
          439 startup(Ruleset *rs, Exec *e)
          440 {
          441         char **argv;
          442         int i;
          443 
          444         if(rs != nil)
          445                 for(i=0; i<rs->nact; i++){
          446                         if(rs->act[i]->verb == VStart)
          447                                 goto Found;
          448                         if(rs->act[i]->verb == VClient){
          449                                 if(e->msg->dst==nil || e->msg->dst[0]=='\0')
          450                                         return "no port for \"client\" rule";
          451                                 e->holdforclient = 1;
          452                                 goto Found;
          453                         }
          454                 }
          455         return "no start action for plumb message";
          456 
          457 Found:
          458         argv = buildargv(rs->act[i]->arg, e);
          459         if(argv[0] == nil)
          460                 return "empty argument list";
          461         threadcreate(execproc, argv, EXECSTACK);
          462         return nil;
          463 }