xref: /plan9/sys/src/cmd/acid/builtin.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ctype.h>
5 #include <mach.h>
6 #include <regexp.h>
7 #define Extern extern
8 #include "acid.h"
9 #include "y.tab.h"
10 
11 void	cvtatof(Node*, Node*);
12 void	cvtatoi(Node*, Node*);
13 void	cvtitoa(Node*, Node*);
14 void	bprint(Node*, Node*);
15 void	funcbound(Node*, Node*);
16 void	printto(Node*, Node*);
17 void	getfile(Node*, Node*);
18 void	fmt(Node*, Node*);
19 void	pcfile(Node*, Node*);
20 void	pcline(Node*, Node*);
21 void	setproc(Node*, Node*);
22 void	strace(Node*, Node*);
23 void	follow(Node*, Node*);
24 void	reason(Node*, Node*);
25 void	newproc(Node*, Node*);
26 void	startstop(Node*, Node*);
27 void	match(Node*, Node*);
28 void	status(Node*, Node*);
29 void	kill(Node*,Node*);
30 void	waitstop(Node*, Node*);
31 void	stop(Node*, Node*);
32 void	start(Node*, Node*);
33 void	filepc(Node*, Node*);
34 void	doerror(Node*, Node*);
35 void	rc(Node*, Node*);
36 void	doaccess(Node*, Node*);
37 void	map(Node*, Node*);
38 void	readfile(Node*, Node*);
39 void	interpret(Node*, Node*);
40 void	include(Node*, Node*);
41 void	regexp(Node*, Node*);
42 void	dosysr1(Node*, Node*);
43 
44 typedef struct Btab Btab;
45 struct Btab
46 {
47 	char	*name;
48 	void	(*fn)(Node*, Node*);
49 } tab[] =
50 {
51 	"atof",		cvtatof,
52 	"atoi",		cvtatoi,
53 	"error",	doerror,
54 	"file",		getfile,
55 	"readfile",	readfile,
56 	"access",	doaccess,
57 	"filepc",	filepc,
58 	"fnbound",	funcbound,
59 	"fmt",		fmt,
60 	"follow",	follow,
61 	"itoa",		cvtitoa,
62 	"kill",		kill,
63 	"match",	match,
64 	"newproc",	newproc,
65 	"pcfile",	pcfile,
66 	"pcline",	pcline,
67 	"print",	bprint,
68 	"printto",	printto,
69 	"rc",		rc,
70 	"reason",	reason,
71 	"setproc",	setproc,
72 	"start",	start,
73 	"startstop",	startstop,
74 	"status",	status,
75 	"stop",		stop,
76 	"strace",	strace,
77 	"sysr1",	dosysr1,
78 	"waitstop",	waitstop,
79 	"map",		map,
80 	"interpret",	interpret,
81 	"include",	include,
82 	"regexp",	regexp,
83 	0
84 };
85 
86 char vfmt[] = "aBbcCdDfFgGiIoOqQrRsSuUVWxXYZ";
87 
88 void
89 mkprint(Lsym *s)
90 {
91 	prnt = malloc(sizeof(Node));
92 	memset(prnt, 0, sizeof(Node));
93 	prnt->op = OCALL;
94 	prnt->left = malloc(sizeof(Node));
95 	memset(prnt->left, 0, sizeof(Node));
96 	prnt->left->sym = s;
97 }
98 
99 void
100 installbuiltin(void)
101 {
102 	Btab *b;
103 	Lsym *s;
104 
105 	b = tab;
106 	while(b->name) {
107 		s = look(b->name);
108 		if(s == 0)
109 			s = enter(b->name, Tid);
110 
111 		s->builtin = b->fn;
112 		if(b->fn == bprint)
113 			mkprint(s);
114 		b++;
115 	}
116 }
117 
118 void
119 dosysr1(Node *r, Node*)
120 {
121 	extern int sysr1(void);
122 
123 	r->op = OCONST;
124 	r->type = TINT;
125 	r->fmt = 'D';
126 	r->ival = sysr1();
127 }
128 
129 void
130 match(Node *r, Node *args)
131 {
132 	int i;
133 	List *f;
134 	Node *av[Maxarg];
135 	Node resi, resl;
136 
137 	na = 0;
138 	flatten(av, args);
139 	if(na != 2)
140 		error("match(obj, list): arg count");
141 
142 	expr(av[1], &resl);
143 	if(resl.type != TLIST)
144 		error("match(obj, list): need list");
145 	expr(av[0], &resi);
146 
147 	r->op = OCONST;
148 	r->type = TINT;
149 	r->fmt = 'D';
150 	r->ival = -1;
151 
152 	i = 0;
153 	for(f = resl.l; f; f = f->next) {
154 		if(resi.type == f->type) {
155 			switch(resi.type) {
156 			case TINT:
157 				if(resi.ival == f->ival) {
158 					r->ival = i;
159 					return;
160 				}
161 				break;
162 			case TFLOAT:
163 				if(resi.fval == f->fval) {
164 					r->ival = i;
165 					return;
166 				}
167 				break;
168 			case TSTRING:
169 				if(scmp(resi.string, f->string)) {
170 					r->ival = i;
171 					return;
172 				}
173 				break;
174 			case TLIST:
175 				error("match(obj, list): not defined for list");
176 			}
177 		}
178 		i++;
179 	}
180 }
181 
182 void
183 newproc(Node *r, Node *args)
184 {
185 	int i;
186 	Node res;
187 	char *p, *e;
188 	char *argv[Maxarg], buf[Strsize];
189 
190 	i = 1;
191 	argv[0] = aout;
192 
193 	if(args) {
194 		expr(args, &res);
195 		if(res.type != TSTRING)
196 			error("newproc(): arg not string");
197 		if(res.string->len >= sizeof(buf))
198 			error("newproc(): too many arguments");
199 		memmove(buf, res.string->string, res.string->len);
200 		buf[res.string->len] = '\0';
201 		p = buf;
202 		e = buf+res.string->len;
203 		for(;;) {
204 			while(p < e && (*p == '\t' || *p == ' '))
205 				*p++ = '\0';
206 			if(p >= e)
207 				break;
208 			argv[i++] = p;
209 			if(i >= Maxarg)
210 				error("newproc: too many arguments");
211 			while(p < e && *p != '\t' && *p != ' ')
212 				p++;
213 		}
214 	}
215 	argv[i] = 0;
216 	r->op = OCONST;
217 	r->type = TINT;
218 	r->fmt = 'D';
219 	r->ival = nproc(argv);
220 }
221 
222 void
223 startstop(Node *r, Node *args)
224 {
225 	Node res;
226 
227 	USED(r);
228 	if(args == 0)
229 		error("startstop(pid): no pid");
230 	expr(args, &res);
231 	if(res.type != TINT)
232 		error("startstop(pid): arg type");
233 
234 	msg(res.ival, "startstop");
235 	notes(res.ival);
236 	dostop(res.ival);
237 }
238 
239 void
240 waitstop(Node *r, Node *args)
241 {
242 	Node res;
243 
244 	USED(r);
245 	if(args == 0)
246 		error("waitstop(pid): no pid");
247 	expr(args, &res);
248 	if(res.type != TINT)
249 		error("waitstop(pid): arg type");
250 
251 	Bflush(bout);
252 	msg(res.ival, "waitstop");
253 	notes(res.ival);
254 	dostop(res.ival);
255 }
256 
257 void
258 start(Node *r, Node *args)
259 {
260 	Node res;
261 
262 	USED(r);
263 	if(args == 0)
264 		error("start(pid): no pid");
265 	expr(args, &res);
266 	if(res.type != TINT)
267 		error("start(pid): arg type");
268 
269 	msg(res.ival, "start");
270 }
271 
272 void
273 stop(Node *r, Node *args)
274 {
275 	Node res;
276 
277 	USED(r);
278 	if(args == 0)
279 		error("stop(pid): no pid");
280 	expr(args, &res);
281 	if(res.type != TINT)
282 		error("stop(pid): arg type");
283 
284 	Bflush(bout);
285 	msg(res.ival, "stop");
286 	notes(res.ival);
287 	dostop(res.ival);
288 }
289 
290 void
291 kill(Node *r, Node *args)
292 {
293 	Node res;
294 
295 	USED(r);
296 	if(args == 0)
297 		error("kill(pid): no pid");
298 	expr(args, &res);
299 	if(res.type != TINT)
300 		error("kill(pid): arg type");
301 
302 	msg(res.ival, "kill");
303 	deinstall(res.ival);
304 }
305 
306 void
307 status(Node *r, Node *args)
308 {
309 	Node res;
310 	char *p;
311 
312 	USED(r);
313 	if(args == 0)
314 		error("status(pid): no pid");
315 	expr(args, &res);
316 	if(res.type != TINT)
317 		error("status(pid): arg type");
318 
319 	p = getstatus(res.ival);
320 	r->string = strnode(p);
321 	r->op = OCONST;
322 	r->fmt = 's';
323 	r->type = TSTRING;
324 }
325 
326 void
327 reason(Node *r, Node *args)
328 {
329 	Node res;
330 
331 	if(args == 0)
332 		error("reason(cause): no cause");
333 	expr(args, &res);
334 	if(res.type != TINT)
335 		error("reason(cause): arg type");
336 
337 	r->op = OCONST;
338 	r->type = TSTRING;
339 	r->fmt = 's';
340 	r->string = strnode((*machdata->excep)(cormap, rget));
341 }
342 
343 void
344 follow(Node *r, Node *args)
345 {
346 	int n, i;
347 	Node res;
348 	uvlong f[10];
349 	List **tail, *l;
350 
351 	if(args == 0)
352 		error("follow(addr): no addr");
353 	expr(args, &res);
354 	if(res.type != TINT)
355 		error("follow(addr): arg type");
356 
357 	n = (*machdata->foll)(cormap, res.ival, rget, f);
358 	if (n < 0)
359 		error("follow(addr): %r");
360 	tail = &r->l;
361 	for(i = 0; i < n; i++) {
362 		l = al(TINT);
363 		l->ival = f[i];
364 		l->fmt = 'X';
365 		*tail = l;
366 		tail = &l->next;
367 	}
368 }
369 
370 void
371 funcbound(Node *r, Node *args)
372 {
373 	int n;
374 	Node res;
375 	uvlong bounds[2];
376 	List *l;
377 
378 	if(args == 0)
379 		error("fnbound(addr): no addr");
380 	expr(args, &res);
381 	if(res.type != TINT)
382 		error("fnbound(addr): arg type");
383 
384 	n = fnbound(res.ival, bounds);
385 	if (n != 0) {
386 		r->l = al(TINT);
387 		l = r->l;
388 		l->ival = bounds[0];
389 		l->fmt = 'X';
390 		l->next = al(TINT);
391 		l = l->next;
392 		l->ival = bounds[1];
393 		l->fmt = 'X';
394 	}
395 }
396 
397 void
398 setproc(Node *r, Node *args)
399 {
400 	Node res;
401 
402 	USED(r);
403 	if(args == 0)
404 		error("setproc(pid): no pid");
405 	expr(args, &res);
406 	if(res.type != TINT)
407 		error("setproc(pid): arg type");
408 
409 	sproc(res.ival);
410 }
411 
412 void
413 filepc(Node *r, Node *args)
414 {
415 	Node res;
416 	char *p, c;
417 
418 	if(args == 0)
419 		error("filepc(filename:line): arg count");
420 	expr(args, &res);
421 	if(res.type != TSTRING)
422 		error("filepc(filename:line): arg type");
423 
424 	p = strchr(res.string->string, ':');
425 	if(p == 0)
426 		error("filepc(filename:line): bad arg format");
427 
428 	c = *p;
429 	*p++ = '\0';
430 	r->ival = file2pc(res.string->string, strtol(p, 0, 0));
431 	p[-1] = c;
432 	if(r->ival == ~0)
433 		error("filepc(filename:line): can't find address");
434 
435 	r->op = OCONST;
436 	r->type = TINT;
437 	r->fmt = 'V';
438 }
439 
440 void
441 interpret(Node *r, Node *args)
442 {
443 	Node res;
444 	int isave;
445 
446 	if(args == 0)
447 		error("interpret(string): arg count");
448 	expr(args, &res);
449 	if(res.type != TSTRING)
450 		error("interpret(string): arg type");
451 
452 	pushstr(&res);
453 
454 	isave = interactive;
455 	interactive = 0;
456 	r->ival = yyparse();
457 	interactive = isave;
458 	popio();
459 	r->op = OCONST;
460 	r->type = TINT;
461 	r->fmt = 'D';
462 }
463 
464 void
465 include(Node *r, Node *args)
466 {
467 	Node res;
468 	int isave;
469 
470 	if(args == 0)
471 		error("include(string): arg count");
472 	expr(args, &res);
473 	if(res.type != TSTRING)
474 		error("include(string): arg type");
475 
476 	pushfile(res.string->string);
477 
478 	isave = interactive;
479 	interactive = 0;
480 	r->ival = yyparse();
481 	interactive = isave;
482 	popio();
483 	r->op = OCONST;
484 	r->type = TINT;
485 	r->fmt = 'D';
486 }
487 
488 void
489 rc(Node *r, Node *args)
490 {
491 	Node res;
492 	int pid;
493 	char *p, *q, *argv[4];
494 	Waitmsg *w;
495 
496 	USED(r);
497 	if(args == 0)
498 		error("error(string): arg count");
499 	expr(args, &res);
500 	if(res.type != TSTRING)
501 		error("error(string): arg type");
502 
503 	argv[0] = "/bin/rc";
504 	argv[1] = "-c";
505 	argv[2] = res.string->string;
506 	argv[3] = 0;
507 
508 	pid = fork();
509 	switch(pid) {
510 	case -1:
511 		error("fork %r");
512 	case 0:
513 		exec("/bin/rc", argv);
514 		exits(0);
515 	default:
516 		w = waitfor(pid);
517 		break;
518 	}
519 	p = w->msg;
520 	q = strrchr(p, ':');
521 	if (q)
522 		p = q+1;
523 
524 	r->op = OCONST;
525 	r->type = TSTRING;
526 	r->string = strnode(p);
527 	free(w);
528 	r->fmt = 's';
529 }
530 
531 void
532 doerror(Node *r, Node *args)
533 {
534 	Node res;
535 
536 	USED(r);
537 	if(args == 0)
538 		error("error(string): arg count");
539 	expr(args, &res);
540 	if(res.type != TSTRING)
541 		error("error(string): arg type");
542 
543 	error(res.string->string);
544 }
545 
546 void
547 doaccess(Node *r, Node *args)
548 {
549 	Node res;
550 
551 	if(args == 0)
552 		error("access(filename): arg count");
553 	expr(args, &res);
554 	if(res.type != TSTRING)
555 		error("access(filename): arg type");
556 
557 	r->op = OCONST;
558 	r->type = TINT;
559 	r->ival = 0;
560 	if(access(res.string->string, 4) == 0)
561 		r->ival = 1;
562 }
563 
564 void
565 readfile(Node *r, Node *args)
566 {
567 	Node res;
568 	int n, fd;
569 	char *buf;
570 	Dir *db;
571 
572 	if(args == 0)
573 		error("readfile(filename): arg count");
574 	expr(args, &res);
575 	if(res.type != TSTRING)
576 		error("readfile(filename): arg type");
577 
578 	fd = open(res.string->string, OREAD);
579 	if(fd < 0)
580 		return;
581 
582 	db = dirfstat(fd);
583 	if(db == nil || db->length == 0)
584 		n = 8192;
585 	else
586 		n = db->length;
587 	free(db);
588 
589 	buf = malloc(n);
590 	n = read(fd, buf, n);
591 
592 	if(n > 0) {
593 		r->op = OCONST;
594 		r->type = TSTRING;
595 		r->string = strnodlen(buf, n);
596 		r->fmt = 's';
597 	}
598 	free(buf);
599 	close(fd);
600 }
601 
602 void
603 getfile(Node *r, Node *args)
604 {
605 	int n;
606 	char *p;
607 	Node res;
608 	String *s;
609 	Biobuf *bp;
610 	List **l, *new;
611 
612 	if(args == 0)
613 		error("file(filename): arg count");
614 	expr(args, &res);
615 	if(res.type != TSTRING)
616 		error("file(filename): arg type");
617 
618 	r->op = OCONST;
619 	r->type = TLIST;
620 	r->l = 0;
621 
622 	p = res.string->string;
623 	bp = Bopen(p, OREAD);
624 	if(bp == 0)
625 		return;
626 
627 	l = &r->l;
628 	for(;;) {
629 		p = Brdline(bp, '\n');
630 		n = Blinelen(bp);
631 		if(p == 0) {
632 			if(n == 0)
633 				break;
634 			s = strnodlen(0, n);
635 			Bread(bp, s->string, n);
636 		}
637 		else
638 			s = strnodlen(p, n-1);
639 
640 		new = al(TSTRING);
641 		new->string = s;
642 		new->fmt = 's';
643 		*l = new;
644 		l = &new->next;
645 	}
646 	Bterm(bp);
647 }
648 
649 void
650 cvtatof(Node *r, Node *args)
651 {
652 	Node res;
653 
654 	if(args == 0)
655 		error("atof(string): arg count");
656 	expr(args, &res);
657 	if(res.type != TSTRING)
658 		error("atof(string): arg type");
659 
660 	r->op = OCONST;
661 	r->type = TFLOAT;
662 	r->fval = atof(res.string->string);
663 	r->fmt = 'f';
664 }
665 
666 void
667 cvtatoi(Node *r, Node *args)
668 {
669 	Node res;
670 
671 	if(args == 0)
672 		error("atoi(string): arg count");
673 	expr(args, &res);
674 	if(res.type != TSTRING)
675 		error("atoi(string): arg type");
676 
677 	r->op = OCONST;
678 	r->type = TINT;
679 	r->ival = strtoull(res.string->string, 0, 0);
680 	r->fmt = 'V';
681 }
682 
683 static char *fmtflags = "-0123456789. #,u";
684 static char *fmtverbs = "bdox";
685 
686 static int
687 acidfmt(char *fmt, char *buf, int blen)
688 {
689 	char *r, *w, *e;
690 
691 	w = buf;
692 	e = buf+blen;
693 	for(r=fmt; *r; r++){
694 		if(w >= e)
695 			return -1;
696 		if(*r != '%'){
697 			*w++ = *r;
698 			continue;
699 		}
700 		if(*r == '%'){
701 			*w++ = *r++;
702 			if(*r == '%'){
703 				if(w >= e)
704 					return -1;
705 				*w++ = *r;
706 				continue;
707 			}
708 			while(*r && strchr(fmtflags, *r)){
709 				if(w >= e)
710 					return -1;
711 				*w++ = *r++;
712 			}
713 			if(*r == 0 || strchr(fmtverbs, *r) == nil)
714 				return -1;
715 			if(w+3 > e)
716 				return -1;
717 			*w++ = 'l';
718 			*w++ = 'l';
719 			*w++ = *r;
720 		}
721 	}
722 	if(w >= e)
723 		return -1;
724 	*w = 0;
725 
726 	return 0;
727 }
728 
729 void
730 cvtitoa(Node *r, Node *args)
731 {
732 	Node res;
733 	Node *av[Maxarg];
734 	vlong ival;
735 	char buf[128], fmt[32];
736 
737 	if(args == 0)
738 err:
739 		error("itoa(number [, fmt]): arg count");
740 	na = 0;
741 	flatten(av, args);
742 	if(na == 0 || na > 2)
743 		goto err;
744 	expr(av[0], &res);
745 	if(res.type != TINT)
746 		error("itoa(number [, fmt]): arg type");
747 	ival = res.ival;
748 	strncpy(fmt, "%lld", sizeof(fmt));
749 	if(na == 2){
750 		expr(av[1], &res);
751 		if(res.type != TSTRING)
752 			error("itoa(number [, fmt]): fmt type");
753 		if(acidfmt(res.string->string, fmt, sizeof(buf)))
754 			error("itoa(number [, fmt]): malformed fmt");
755 	}
756 
757 	snprint(buf, sizeof(buf), fmt, ival);
758 	r->op = OCONST;
759 	r->type = TSTRING;
760 	r->string = strnode(buf);
761 	r->fmt = 's';
762 }
763 
764 List*
765 mapent(Map *m)
766 {
767 	int i;
768 	List *l, *n, **t, *h;
769 
770 	h = 0;
771 	t = &h;
772 	for(i = 0; i < m->nsegs; i++) {
773 		if(m->seg[i].inuse == 0)
774 			continue;
775 		l = al(TSTRING);
776 		n = al(TLIST);
777 		n->l = l;
778 		*t = n;
779 		t = &n->next;
780 		l->string = strnode(m->seg[i].name);
781 		l->fmt = 's';
782 		l->next = al(TINT);
783 		l = l->next;
784 		l->ival = m->seg[i].b;
785 		l->fmt = 'W';
786 		l->next = al(TINT);
787 		l = l->next;
788 		l->ival = m->seg[i].e;
789 		l->fmt = 'W';
790 		l->next = al(TINT);
791 		l = l->next;
792 		l->ival = m->seg[i].f;
793 		l->fmt = 'W';
794 	}
795 	return h;
796 }
797 
798 void
799 map(Node *r, Node *args)
800 {
801 	int i;
802 	Map *m;
803 	List *l;
804 	char *ent;
805 	Node *av[Maxarg], res;
806 
807 	na = 0;
808 	flatten(av, args);
809 
810 	if(na != 0) {
811 		expr(av[0], &res);
812 		if(res.type != TLIST)
813 			error("map(list): map needs a list");
814 		if(listlen(res.l) != 4)
815 			error("map(list): list must have 4 entries");
816 
817 		l = res.l;
818 		if(l->type != TSTRING)
819 			error("map name must be a string");
820 		ent = l->string->string;
821 		m = symmap;
822 		i = findseg(m, ent);
823 		if(i < 0) {
824 			m = cormap;
825 			i = findseg(m, ent);
826 		}
827 		if(i < 0)
828 			error("%s is not a map entry", ent);
829 		l = l->next;
830 		if(l->type != TINT)
831 			error("map entry not int");
832 		m->seg[i].b = l->ival;
833 		if (strcmp(ent, "text") == 0)
834 			textseg(l->ival, &fhdr);
835 		l = l->next;
836 		if(l->type != TINT)
837 			error("map entry not int");
838 		m->seg[i].e = l->ival;
839 		l = l->next;
840 		if(l->type != TINT)
841 			error("map entry not int");
842 		m->seg[i].f = l->ival;
843 	}
844 
845 	r->type = TLIST;
846 	r->l = 0;
847 	if(symmap)
848 		r->l = mapent(symmap);
849 	if(cormap) {
850 		if(r->l == 0)
851 			r->l = mapent(cormap);
852 		else {
853 			for(l = r->l; l->next; l = l->next)
854 				;
855 			l->next = mapent(cormap);
856 		}
857 	}
858 }
859 
860 void
861 flatten(Node **av, Node *n)
862 {
863 	if(n == 0)
864 		return;
865 
866 	switch(n->op) {
867 	case OLIST:
868 		flatten(av, n->left);
869 		flatten(av, n->right);
870 		break;
871 	default:
872 		av[na++] = n;
873 		if(na >= Maxarg)
874 			error("too many function arguments");
875 		break;
876 	}
877 }
878 
879 void
880 strace(Node *r, Node *args)
881 {
882 	Node *av[Maxarg], *n, res;
883 	uvlong pc, sp;
884 
885 	na = 0;
886 	flatten(av, args);
887 	if(na != 3)
888 		error("strace(pc, sp, link): arg count");
889 
890 	n = av[0];
891 	expr(n, &res);
892 	if(res.type != TINT)
893 		error("strace(pc, sp, link): pc bad type");
894 	pc = res.ival;
895 
896 	n = av[1];
897 	expr(n, &res);
898 	if(res.type != TINT)
899 		error("strace(pc, sp, link): sp bad type");
900 	sp = res.ival;
901 
902 	n = av[2];
903 	expr(n, &res);
904 	if(res.type != TINT)
905 		error("strace(pc, sp, link): link bad type");
906 
907 	tracelist = 0;
908 	if ((*machdata->ctrace)(cormap, pc, sp, res.ival, trlist) <= 0)
909 		error("no stack frame: %r");
910 	r->type = TLIST;
911 	r->l = tracelist;
912 }
913 
914 void
915 regerror(char *msg)
916 {
917 	error(msg);
918 }
919 
920 void
921 regexp(Node *r, Node *args)
922 {
923 	Node res;
924 	Reprog *rp;
925 	Node *av[Maxarg];
926 
927 	na = 0;
928 	flatten(av, args);
929 	if(na != 2)
930 		error("regexp(pattern, string): arg count");
931 	expr(av[0], &res);
932 	if(res.type != TSTRING)
933 		error("regexp(pattern, string): pattern must be string");
934 	rp = regcomp(res.string->string);
935 	if(rp == 0)
936 		return;
937 
938 	expr(av[1], &res);
939 	if(res.type != TSTRING)
940 		error("regexp(pattern, string): bad string");
941 
942 	r->fmt = 'D';
943 	r->type = TINT;
944 	r->ival = regexec(rp, res.string->string, 0, 0);
945 	free(rp);
946 }
947 
948 void
949 fmt(Node *r, Node *args)
950 {
951 	Node res;
952 	Node *av[Maxarg];
953 
954 	na = 0;
955 	flatten(av, args);
956 	if(na != 2)
957 		error("fmt(obj, fmt): arg count");
958 	expr(av[1], &res);
959 	if(res.type != TINT || strchr(vfmt, res.ival) == 0)
960 		error("fmt(obj, fmt): bad format '%c'", (char)res.ival);
961 	expr(av[0], r);
962 	r->fmt = res.ival;
963 }
964 
965 void
966 patom(char type, Store *res)
967 {
968 	int i;
969 	char buf[512];
970 	extern char *typenames[];
971 
972 	switch(res->fmt) {
973 	case 'c':
974 		Bprint(bout, "%c", (int)res->ival);
975 		break;
976 	case 'C':
977 		if(res->ival < ' ' || res->ival >= 0x7f)
978 			Bprint(bout, "%3d", (int)res->ival&0xff);
979 		else
980 			Bprint(bout, "%3c", (int)res->ival);
981 		break;
982 	case 'r':
983 		Bprint(bout, "%C", (int)res->ival);
984 		break;
985 	case 'B':
986 		memset(buf, '0', 34);
987 		buf[1] = 'b';
988 		for(i = 0; i < 32; i++) {
989 			if(res->ival & (1<<i))
990 				buf[33-i] = '1';
991 		}
992 		buf[35] = '\0';
993 		Bprint(bout, "%s", buf);
994 		break;
995 	case 'b':
996 		Bprint(bout, "%.2x", (int)res->ival&0xff);
997 		break;
998 	case 'X':
999 		Bprint(bout, "%.8lux", (ulong)res->ival);
1000 		break;
1001 	case 'x':
1002 		Bprint(bout, "%.4lux", (ulong)res->ival&0xffff);
1003 		break;
1004 	case 'D':
1005 		Bprint(bout, "%d", (int)res->ival);
1006 		break;
1007 	case 'd':
1008 		Bprint(bout, "%d", (ushort)res->ival);
1009 		break;
1010 	case 'u':
1011 		Bprint(bout, "%d", (int)res->ival&0xffff);
1012 		break;
1013 	case 'U':
1014 		Bprint(bout, "%lud", (ulong)res->ival);
1015 		break;
1016 	case 'Z':
1017 		Bprint(bout, "%llud", res->ival);
1018 		break;
1019 	case 'V':
1020 		Bprint(bout, "%lld", res->ival);
1021 		break;
1022 	case 'W':
1023 		Bprint(bout, "%.8llux", res->ival);
1024 		break;
1025 	case 'Y':
1026 		Bprint(bout, "%.16llux", res->ival);
1027 		break;
1028 	case 'o':
1029 		Bprint(bout, "0%.11uo", (int)res->ival&0xffff);
1030 		break;
1031 	case 'O':
1032 		Bprint(bout, "0%.6uo", (int)res->ival);
1033 		break;
1034 	case 'q':
1035 		Bprint(bout, "0%.11o", (short)(res->ival&0xffff));
1036 		break;
1037 	case 'Q':
1038 		Bprint(bout, "0%.6o", (int)res->ival);
1039 		break;
1040 	case 'f':
1041 	case 'F':
1042 		if(type != TFLOAT)
1043 			Bprint(bout, "*%c<%s>*", res->fmt, typenames[type]);
1044 		else
1045 			Bprint(bout, "%g", res->fval);
1046 		break;
1047 	case 's':
1048 	case 'g':
1049 	case 'G':
1050 		if(type != TSTRING)
1051 			Bprint(bout, "*%c<%s>*", res->fmt, typenames[type]);
1052 		else
1053 			Bwrite(bout, res->string->string, res->string->len);
1054 		break;
1055 	case 'R':
1056 		if(type != TSTRING)
1057 			Bprint(bout, "*%c<%s>*", res->fmt, typenames[type]);
1058 		else
1059 			Bprint(bout, "%S", (Rune*)res->string->string);
1060 		break;
1061 	case 'a':
1062 	case 'A':
1063 		symoff(buf, sizeof(buf), res->ival, CANY);
1064 		Bprint(bout, "%s", buf);
1065 		break;
1066 	case 'I':
1067 	case 'i':
1068 		if(type != TINT)
1069 			Bprint(bout, "*%c<%s>*", res->fmt, typenames[type]);
1070 		else {
1071 			if (symmap == nil || (*machdata->das)(symmap, res->ival, res->fmt, buf, sizeof(buf)) < 0)
1072 				Bprint(bout, "no instruction");
1073 			else
1074 				Bprint(bout, "%s", buf);
1075 		}
1076 		break;
1077 	}
1078 }
1079 
1080 void
1081 blprint(List *l)
1082 {
1083 	Bprint(bout, "{");
1084 	while(l) {
1085 		switch(l->type) {
1086 		default:
1087 			patom(l->type, &l->Store);
1088 			break;
1089 		case TSTRING:
1090 			Bputc(bout, '"');
1091 			patom(l->type, &l->Store);
1092 			Bputc(bout, '"');
1093 			break;
1094 		case TLIST:
1095 			blprint(l->l);
1096 			break;
1097 		case TCODE:
1098 			pcode(l->cc, 0);
1099 			break;
1100 		}
1101 		l = l->next;
1102 		if(l)
1103 			Bprint(bout, ", ");
1104 	}
1105 	Bprint(bout, "}");
1106 }
1107 
1108 int
1109 comx(Node res)
1110 {
1111 	Lsym *sl;
1112 	Node *n, xx;
1113 
1114 	if(res.fmt != 'a' && res.fmt != 'A')
1115 		return 0;
1116 
1117 	if(res.comt == 0 || res.comt->base == 0)
1118 		return 0;
1119 
1120 	sl = res.comt->base;
1121 	if(sl->proc) {
1122 		res.left = ZN;
1123 		res.right = ZN;
1124 		n = an(ONAME, ZN, ZN);
1125 		n->sym = sl;
1126 		n = an(OCALL, n, &res);
1127 			n->left->sym = sl;
1128 		expr(n, &xx);
1129 		return 1;
1130 	}
1131 	print("(%s)", sl->name);
1132 	return 0;
1133 }
1134 
1135 void
1136 bprint(Node *r, Node *args)
1137 {
1138 	int i, nas;
1139 	Node res, *av[Maxarg];
1140 
1141 	USED(r);
1142 	na = 0;
1143 	flatten(av, args);
1144 	nas = na;
1145 	for(i = 0; i < nas; i++) {
1146 		expr(av[i], &res);
1147 		switch(res.type) {
1148 		default:
1149 			if(comx(res))
1150 				break;
1151 			patom(res.type, &res.Store);
1152 			break;
1153 		case TCODE:
1154 			pcode(res.cc, 0);
1155 			break;
1156 		case TLIST:
1157 			blprint(res.l);
1158 			break;
1159 		}
1160 	}
1161 	if(ret == 0)
1162 		Bputc(bout, '\n');
1163 }
1164 
1165 void
1166 printto(Node *r, Node *args)
1167 {
1168 	int fd;
1169 	Biobuf *b;
1170 	int i, nas;
1171 	Node res, *av[Maxarg];
1172 
1173 	USED(r);
1174 	na = 0;
1175 	flatten(av, args);
1176 	nas = na;
1177 
1178 	expr(av[0], &res);
1179 	if(res.type != TSTRING)
1180 		error("printto(string, ...): need string");
1181 
1182 	fd = create(res.string->string, OWRITE, 0666);
1183 	if(fd < 0)
1184 		fd = open(res.string->string, OWRITE);
1185 	if(fd < 0)
1186 		error("printto: open %s: %r", res.string->string);
1187 
1188 	b = gmalloc(sizeof(Biobuf));
1189 	Binit(b, fd, OWRITE);
1190 
1191 	Bflush(bout);
1192 	io[iop++] = bout;
1193 	bout = b;
1194 
1195 	for(i = 1; i < nas; i++) {
1196 		expr(av[i], &res);
1197 		switch(res.type) {
1198 		default:
1199 			if(comx(res))
1200 				break;
1201 			patom(res.type, &res.Store);
1202 			break;
1203 		case TLIST:
1204 			blprint(res.l);
1205 			break;
1206 		}
1207 	}
1208 	if(ret == 0)
1209 		Bputc(bout, '\n');
1210 
1211 	Bterm(b);
1212 	close(fd);
1213 	free(b);
1214 	bout = io[--iop];
1215 }
1216 
1217 void
1218 pcfile(Node *r, Node *args)
1219 {
1220 	Node res;
1221 	char *p, buf[128];
1222 
1223 	if(args == 0)
1224 		error("pcfile(addr): arg count");
1225 	expr(args, &res);
1226 	if(res.type != TINT)
1227 		error("pcfile(addr): arg type");
1228 
1229 	r->type = TSTRING;
1230 	r->fmt = 's';
1231 	if(fileline(buf, sizeof(buf), res.ival) == 0) {
1232 		r->string = strnode("?file?");
1233 		return;
1234 	}
1235 	p = strrchr(buf, ':');
1236 	if(p == 0)
1237 		error("pcfile(addr): funny file %s", buf);
1238 	*p = '\0';
1239 	r->string = strnode(buf);
1240 }
1241 
1242 void
1243 pcline(Node *r, Node *args)
1244 {
1245 	Node res;
1246 	char *p, buf[128];
1247 
1248 	if(args == 0)
1249 		error("pcline(addr): arg count");
1250 	expr(args, &res);
1251 	if(res.type != TINT)
1252 		error("pcline(addr): arg type");
1253 
1254 	r->type = TINT;
1255 	r->fmt = 'D';
1256 	if(fileline(buf, sizeof(buf), res.ival) == 0) {
1257 		r->ival = 0;
1258 		return;
1259 	}
1260 
1261 	p = strrchr(buf, ':');
1262 	if(p == 0)
1263 		error("pcline(addr): funny file %s", buf);
1264 	r->ival = strtol(p+1, 0, 0);
1265 }
1266