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