plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

match.c (8171B)


      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[NMATCHSUBEXP], char *match[NMATCHSUBEXP])
     42 {
     43 	int i, n;
     44 
     45 	for(i=0; i<NMATCHSUBEXP; i++){
     46 		free(match[i]);
     47 		match[i] = nil;
     48 		if(rs[i].s.sp != nil){
     49 			n = rs[i].e.ep-rs[i].s.sp;
     50 			match[i] = emalloc(n+1);
     51 			memmove(match[i], rs[i].s.sp, n);
     52 			match[i][n] = '\0';
     53 		}
     54 	}
     55 }
     56 
     57 int
     58 clickmatch(Reprog *re, char *text, Resub rs[NMATCHSUBEXP], 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, NMATCHSUBEXP*sizeof(Resub));
     70 		if(regexec(re, text+i, rs, NMATCHSUBEXP))
     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[NMATCHSUBEXP];
     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, NMATCHSUBEXP)
    126 		|| rs[0].s.sp!=alltext || rs[0].e.ep!=alltext+ntext)
    127 			break;
    128 		setvar(rs, e->match);
    129 		return 1;
    130 	}
    131 	return 0;
    132 }
    133 
    134 int
    135 isfile(char *file, ulong maskon, ulong maskoff)
    136 {
    137 	Dir *d;
    138 	int mode;
    139 
    140 	d = dirstat(file);
    141 	if(d == nil)
    142 		return 0;
    143 	mode = d->mode;
    144 	free(d);
    145 	if((mode & maskon) == 0)
    146 		return 0;
    147 	if(mode & maskoff)
    148 		return 0;
    149 	return 1;
    150 }
    151 
    152 char*
    153 absolute(char *dir, char *file)
    154 {
    155 	char *p;
    156 
    157 	if(file[0] == '/')
    158 		return estrdup(file);
    159 	p = emalloc(strlen(dir)+1+strlen(file)+1);
    160 	sprint(p, "%s/%s", dir, file);
    161 	return cleanname(p);
    162 }
    163 
    164 int
    165 verbisfile(int obj, Plumbmsg *m, Rule *r, Exec *e, ulong maskon, ulong maskoff, char **var)
    166 {
    167 	char *file;
    168 
    169 	switch(obj){
    170 	default:
    171 		fprint(2, "unimplemented 'isfile' object %d\n", obj);
    172 		break;
    173 	case OArg:
    174 		file = absolute(m->wdir, expand(e, r->arg, nil));
    175 		if(isfile(file, maskon, maskoff)){
    176 			*var = file;
    177 			return 1;
    178 		}
    179 		free(file);
    180 		break;
    181 	case OData:
    182 	case OWdir:
    183 		file = absolute(m->wdir, obj==OData? m->data : m->wdir);
    184 		if(isfile(file, maskon, maskoff)){
    185 			*var = file;
    186 			return 1;
    187 		}
    188 		free(file);
    189 		break;
    190 	}
    191 	return 0;
    192 }
    193 
    194 int
    195 verbset(int obj, Plumbmsg *m, Rule *r, Exec *e)
    196 {
    197 	char *new;
    198 
    199 	switch(obj){
    200 	default:
    201 		fprint(2, "unimplemented 'is' object %d\n", obj);
    202 		break;
    203 	case OData:
    204 		new = estrdup(expand(e, r->arg, nil));
    205 		m->ndata = strlen(new);
    206 		free(m->data);
    207 		m->data = new;
    208 		e->p0 = -1;
    209 		e->p1 = -1;
    210 		e->setdata = 0;
    211 		return 1;
    212 	case ODst:
    213 		new = estrdup(expand(e, r->arg, nil));
    214 		free(m->dst);
    215 		m->dst = new;
    216 		return 1;
    217 	case OType:
    218 		new = estrdup(expand(e, r->arg, nil));
    219 		free(m->type);
    220 		m->type = new;
    221 		return 1;
    222 	case OWdir:
    223 		new = estrdup(expand(e, r->arg, nil));
    224 		free(m->wdir);
    225 		m->wdir = new;
    226 		return 1;
    227 	case OSrc:
    228 		new = estrdup(expand(e, r->arg, nil));
    229 		free(m->src);
    230 		m->src = new;
    231 		return 1;
    232 	}
    233 	return 0;
    234 }
    235 
    236 int
    237 verbadd(int obj, Plumbmsg *m, Rule *r, Exec *e)
    238 {
    239 	switch(obj){
    240 	default:
    241 		fprint(2, "unimplemented 'add' object %d\n", obj);
    242 		break;
    243 	case OAttr:
    244 		m->attr = plumbaddattr(m->attr, plumbunpackattr(expand(e, r->arg, nil)));
    245 		return 1;
    246 	}
    247 	return 0;
    248 }
    249 
    250 int
    251 verbdelete(int obj, Plumbmsg *m, Rule *r, Exec *e)
    252 {
    253 	char *a;
    254 
    255 	switch(obj){
    256 	default:
    257 		fprint(2, "unimplemented 'delete' object %d\n", obj);
    258 		break;
    259 	case OAttr:
    260 		a = expand(e, r->arg, nil);
    261 		if(plumblookup(m->attr, a) == nil)
    262 			break;
    263 		m->attr = plumbdelattr(m->attr, a);
    264 		return 1;
    265 	}
    266 	return 0;
    267 }
    268 
    269 int
    270 matchpat(Plumbmsg *m, Exec *e, Rule *r)
    271 {
    272 	switch(r->verb){
    273 	default:
    274 		fprint(2, "unimplemented verb %d\n", r->verb);
    275 		break;
    276 	case VAdd:
    277 		return verbadd(r->obj, m, r, e);
    278 	case VDelete:
    279 		return verbdelete(r->obj, m, r, e);
    280 	case VIs:
    281 		return verbis(r->obj, m, r);
    282 	case VIsdir:
    283 		return verbisfile(r->obj, m, r, e, DMDIR, 0, &e->dir);
    284 	case VIsfile:
    285 		return verbisfile(r->obj, m, r, e, ~DMDIR, DMDIR, &e->file);
    286 	case VMatches:
    287 		return verbmatches(r->obj, m, r, e);
    288 	case VSet:
    289 		verbset(r->obj, m, r, e);
    290 		return 1;
    291 	}
    292 	return 0;
    293 }
    294 
    295 void
    296 freeexec(Exec *exec)
    297 {
    298 	int i;
    299 
    300 	if(exec == nil)
    301 		return;
    302 	free(exec->dir);
    303 	free(exec->file);
    304 	for(i=0; i<NMATCHSUBEXP; i++)
    305 		free(exec->match[i]);
    306 	free(exec);
    307 }
    308 
    309 Exec*
    310 newexec(Plumbmsg *m)
    311 {
    312 	Exec *exec;
    313 
    314 	exec = emalloc(sizeof(Exec));
    315 	exec->msg = m;
    316 	exec->p0 = -1;
    317 	exec->p1 = -1;
    318 	return exec;
    319 }
    320 
    321 void
    322 rewrite(Plumbmsg *m, Exec *e)
    323 {
    324 	Plumbattr *a, *prev;
    325 
    326 	if(e->clearclick){
    327 		prev = nil;
    328 		for(a=m->attr; a!=nil; a=a->next){
    329 			if(strcmp(a->name, "click") == 0){
    330 				if(prev == nil)
    331 					m->attr = a->next;
    332 				else
    333 					prev->next = a->next;
    334 				free(a->name);
    335 				free(a->value);
    336 				free(a);
    337 				break;
    338 			}
    339 			prev = a;
    340 		}
    341 		if(e->setdata){
    342 			free(m->data);
    343 			m->data = estrdup(expand(e, "$0", nil));
    344 			m->ndata = strlen(m->data);
    345 		}
    346 	}
    347 }
    348 
    349 char**
    350 buildargv(char *s, Exec *e)
    351 {
    352 	char **av;
    353 	int ac;
    354 
    355 	ac = 0;
    356 	av = nil;
    357 	for(;;){
    358 		av = erealloc(av, (ac+1) * sizeof(char*));
    359 		av[ac] = nil;
    360 		while(*s==' ' || *s=='\t')
    361 			s++;
    362 		if(*s == '\0')
    363 			break;
    364 		av[ac++] = estrdup(expand(e, s, &s));
    365 	}
    366 	return av;
    367 }
    368 
    369 Exec*
    370 matchruleset(Plumbmsg *m, Ruleset *rs)
    371 {
    372 	int i;
    373 	Exec *exec;
    374 
    375 	if(m->dst!=nil && m->dst[0]!='\0' && rs->port!=nil && strcmp(m->dst, rs->port)!=0)
    376 		return nil;
    377 	exec = newexec(m);
    378 	for(i=0; i<rs->npat; i++)
    379 		if(!matchpat(m, exec, rs->pat[i])){
    380 			freeexec(exec);
    381 			return nil;
    382 		}
    383 	if(rs->port!=nil && (m->dst==nil || m->dst[0]=='\0')){
    384 		free(m->dst);
    385 		m->dst = estrdup(rs->port);
    386 	}
    387 	rewrite(m, exec);
    388 	return exec;
    389 }
    390 
    391 enum
    392 {
    393 	NARGS		= 100,
    394 	NARGCHAR	= 8*1024,
    395 	EXECSTACK 	= 32*1024+(NARGS+1)*sizeof(char*)+NARGCHAR
    396 };
    397 
    398 /* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
    399 void
    400 stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
    401 {
    402 	int i, n;
    403 	char *s, *a;
    404 
    405 	s = args;
    406 	for(i=0; i<NARGS; i++){
    407 		a = inargv[i];
    408 		if(a == nil)
    409 			break;
    410 		n = strlen(a)+1;
    411 		if((s-args)+n >= NARGCHAR)	/* too many characters */
    412 			break;
    413 		argv[i] = s;
    414 		memmove(s, a, n);
    415 		s += n;
    416 		free(a);
    417 	}
    418 	argv[i] = nil;
    419 }
    420 
    421 
    422 void
    423 execproc(void *v)
    424 {
    425 	int fd[3];
    426 	char **av;
    427 	char *args[NARGS+1], argc[NARGCHAR];
    428 
    429 	fd[0] = open("/dev/null", OREAD);
    430 	fd[1] = dup(1, -1);
    431 	fd[2] = dup(2, -1);
    432 	av = v;
    433 	stackargv(av, args, argc);
    434 	free(av);
    435 	threadexec(nil, fd, args[0], args);
    436 	threadexits("can't exec");
    437 }
    438 
    439 char*
    440 startup(Ruleset *rs, Exec *e)
    441 {
    442 	char **argv;
    443 	int i;
    444 
    445 	if(rs != nil)
    446 		for(i=0; i<rs->nact; i++){
    447 			if(rs->act[i]->verb == VStart)
    448 				goto Found;
    449 			if(rs->act[i]->verb == VClient){
    450 				if(e->msg->dst==nil || e->msg->dst[0]=='\0')
    451 					return "no port for \"client\" rule";
    452 				e->holdforclient = 1;
    453 				goto Found;
    454 			}
    455 		}
    456 	return "no start action for plumb message";
    457 
    458 Found:
    459 	argv = buildargv(rs->act[i]->arg, e);
    460 	if(argv[0] == nil)
    461 		return "empty argument list";
    462 	threadcreate(execproc, argv, EXECSTACK);
    463 	return nil;
    464 }