plan9port

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

builtin.c (29415B)


      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	xkill(Node*,Node*);
     30 void	waitstop(Node*, Node*);
     31 void waitsyscall(Node*, Node*);
     32 void	stop(Node*, Node*);
     33 void	start(Node*, Node*);
     34 void	filepc(Node*, Node*);
     35 void	doerror(Node*, Node*);
     36 void	rc(Node*, Node*);
     37 void	doaccess(Node*, Node*);
     38 void	map(Node*, Node*);
     39 void	readfile(Node*, Node*);
     40 void	interpret(Node*, Node*);
     41 void	include(Node*, Node*);
     42 void	includepipe(Node*, Node*);
     43 void	regexp(Node*, Node*);
     44 void textfile(Node*, Node*);
     45 void deltextfile(Node*, Node*);
     46 void stringn(Node*, Node*);
     47 void xregister(Node*, Node*);
     48 void refconst(Node*, Node*);
     49 void dolook(Node*, Node*);
     50 void step1(Node*, Node*);
     51 
     52 typedef struct Btab Btab;
     53 struct Btab
     54 {
     55 	char	*name;
     56 	void	(*fn)(Node*, Node*);
     57 } tab[] =
     58 {
     59 	"access",	doaccess,
     60 	"atof",		cvtatof,
     61 	"atoi",		cvtatoi,
     62 	"deltextfile",	deltextfile,
     63 	"error",	doerror,
     64 	"file",		getfile,
     65 	"filepc",	filepc,
     66 	"fnbound",	funcbound,
     67 	"fmt",		fmt,
     68 	"follow",	follow,
     69 	"include",	include,
     70 	"includepipe",	includepipe,
     71 	"interpret",	interpret,
     72 	"itoa",		cvtitoa,
     73 	"kill",		xkill,
     74 	"map",		map,
     75 	"match",	match,
     76 	"newproc",	newproc,
     77 	"pcfile",	pcfile,
     78 	"pcline",	pcline,
     79 	"print",	bprint,
     80 	"printto",	printto,
     81 	"rc",		rc,
     82 	"readfile",	readfile,
     83 	"reason",	reason,
     84 	"refconst",	refconst,
     85 	"regexp",	regexp,
     86 	"register",	xregister,
     87 	"setproc",	setproc,
     88 	"start",	start,
     89 	"startstop",	startstop,
     90 	"status",	status,
     91 	"step1",	step1,
     92 	"stop",		stop,
     93 	"strace",	strace,
     94 	"stringn",	stringn,
     95 	"textfile",	textfile,
     96 	"var",	dolook,
     97 	"waitstop",	waitstop,
     98 	"waitsyscall",	waitsyscall,
     99 	0
    100 };
    101 
    102 void
    103 mkprint(Lsym *s)
    104 {
    105 	prnt = malloc(sizeof(Node));
    106 	memset(prnt, 0, sizeof(Node));
    107 	prnt->op = OCALL;
    108 	prnt->left = malloc(sizeof(Node));
    109 	memset(prnt->left, 0, sizeof(Node));
    110 	prnt->left->sym = s;
    111 }
    112 
    113 void
    114 installbuiltin(void)
    115 {
    116 	Btab *b;
    117 	Lsym *s;
    118 
    119 	b = tab;
    120 	while(b->name) {
    121 		s = look(b->name);
    122 		if(s == 0)
    123 			s = enter(b->name, Tid);
    124 
    125 		s->builtin = b->fn;
    126 		if(b->fn == bprint)
    127 			mkprint(s);
    128 		b++;
    129 	}
    130 }
    131 
    132 void
    133 match(Node *r, Node *args)
    134 {
    135 	int i;
    136 	List *f;
    137 	Node *av[Maxarg];
    138 	Node resi, resl;
    139 
    140 	na = 0;
    141 	flatten(av, args);
    142 	if(na != 2)
    143 		error("match(obj, list): arg count");
    144 
    145 	expr(av[1], &resl);
    146 	if(resl.type != TLIST)
    147 		error("match(obj, list): need list");
    148 	expr(av[0], &resi);
    149 
    150 	r->op = OCONST;
    151 	r->type = TINT;
    152 	r->store.fmt = 'D';
    153 	r->store.u.ival = -1;
    154 
    155 	i = 0;
    156 	for(f = resl.store.u.l; f; f = f->next) {
    157 		if(resi.type == f->type) {
    158 			switch(resi.type) {
    159 			case TINT:
    160 				if(resi.store.u.ival == f->store.u.ival) {
    161 					r->store.u.ival = i;
    162 					return;
    163 				}
    164 				break;
    165 			case TFLOAT:
    166 				if(resi.store.u.fval == f->store.u.fval) {
    167 					r->store.u.ival = i;
    168 					return;
    169 				}
    170 				break;
    171 			case TSTRING:
    172 				if(scmp(resi.store.u.string, f->store.u.string)) {
    173 					r->store.u.ival = i;
    174 					return;
    175 				}
    176 				break;
    177 			case TLIST:
    178 				error("match(obj, list): not defined for list");
    179 			}
    180 		}
    181 		i++;
    182 	}
    183 }
    184 
    185 void
    186 newproc(Node *r, Node *args)
    187 {
    188 	int i;
    189 	Node res;
    190 	char *p, *e;
    191 	char *argv[Maxarg], buf[Strsize];
    192 
    193 	i = 1;
    194 	argv[0] = symfil;
    195 
    196 	if(args) {
    197 		expr(args, &res);
    198 		if(res.type != TSTRING)
    199 			error("newproc(): arg not string");
    200 		if(res.store.u.string->len >= sizeof(buf))
    201 			error("newproc(): too many arguments");
    202 		memmove(buf, res.store.u.string->string, res.store.u.string->len);
    203 		buf[res.store.u.string->len] = '\0';
    204 		p = buf;
    205 		e = buf+res.store.u.string->len;
    206 		for(;;) {
    207 			while(p < e && (*p == '\t' || *p == ' '))
    208 				*p++ = '\0';
    209 			if(p >= e)
    210 				break;
    211 			argv[i++] = p;
    212 			if(i >= Maxarg)
    213 				error("newproc: too many arguments");
    214 			while(p < e && *p != '\t' && *p != ' ')
    215 				p++;
    216 		}
    217 	}
    218 	argv[i] = 0;
    219 	r->op = OCONST;
    220 	r->type = TINT;
    221 	r->store.fmt = 'D';
    222 	r->store.u.ival = nproc(argv);
    223 }
    224 
    225 void
    226 step1(Node *r, Node *args)
    227 {
    228 	Node res;
    229 
    230 	USED(r);
    231 	if(args == 0)
    232 		error("step1(pid): no pid");
    233 	expr(args, &res);
    234 	if(res.type != TINT)
    235 		error("step1(pid): arg type");
    236 
    237 	msg(res.store.u.ival, "step");
    238 	notes(res.store.u.ival);
    239 	dostop(res.store.u.ival);
    240 }
    241 
    242 void
    243 startstop(Node *r, Node *args)
    244 {
    245 	Node res;
    246 
    247 	USED(r);
    248 	if(args == 0)
    249 		error("startstop(pid): no pid");
    250 	expr(args, &res);
    251 	if(res.type != TINT)
    252 		error("startstop(pid): arg type");
    253 
    254 	msg(res.store.u.ival, "startstop");
    255 	notes(res.store.u.ival);
    256 	dostop(res.store.u.ival);
    257 }
    258 
    259 void
    260 waitstop(Node *r, Node *args)
    261 {
    262 	Node res;
    263 
    264 	USED(r);
    265 	if(args == 0)
    266 		error("waitstop(pid): no pid");
    267 	expr(args, &res);
    268 	if(res.type != TINT)
    269 		error("waitstop(pid): arg type");
    270 
    271 	Bflush(bout);
    272 	msg(res.store.u.ival, "waitstop");
    273 	notes(res.store.u.ival);
    274 	dostop(res.store.u.ival);
    275 }
    276 
    277 void
    278 waitsyscall(Node *r, Node *args)
    279 {
    280 	Node res;
    281 
    282 	USED(r);
    283 	if(args == 0)
    284 		error("waitsyscall(pid): no pid");
    285 	expr(args, &res);
    286 	if(res.type != TINT)
    287 		error("waitsycall(pid): arg type");
    288 
    289 	Bflush(bout);
    290 	msg(res.store.u.ival, "sysstop");
    291 	notes(res.store.u.ival);
    292 	dostop(res.store.u.ival);
    293 }
    294 
    295 void
    296 start(Node *r, Node *args)
    297 {
    298 	Node res;
    299 
    300 	USED(r);
    301 	if(args == 0)
    302 		error("start(pid): no pid");
    303 	expr(args, &res);
    304 	if(res.type != TINT)
    305 		error("start(pid): arg type");
    306 
    307 	msg(res.store.u.ival, "start");
    308 }
    309 
    310 void
    311 stop(Node *r, Node *args)
    312 {
    313 	Node res;
    314 
    315 	USED(r);
    316 	if(args == 0)
    317 		error("stop(pid): no pid");
    318 	expr(args, &res);
    319 	if(res.type != TINT)
    320 		error("stop(pid): arg type");
    321 
    322 	Bflush(bout);
    323 	msg(res.store.u.ival, "stop");
    324 	notes(res.store.u.ival);
    325 	dostop(res.store.u.ival);
    326 }
    327 
    328 void
    329 xkill(Node *r, Node *args)
    330 {
    331 	Node res;
    332 
    333 	USED(r);
    334 	if(args == 0)
    335 		error("kill(pid): no pid");
    336 	expr(args, &res);
    337 	if(res.type != TINT)
    338 		error("kill(pid): arg type");
    339 
    340 	msg(res.store.u.ival, "kill");
    341 	deinstall(res.store.u.ival);
    342 }
    343 
    344 void
    345 xregister(Node *r, Node *args)
    346 {
    347 	int tid;
    348 	Regdesc *rp;
    349 	Node res, resid;
    350 	Node *av[Maxarg];
    351 
    352 	na = 0;
    353 	flatten(av, args);
    354 	if(na != 1/* && na != 2 */)
    355 		error("register(name): arg count");
    356 
    357 	expr(av[0], &res);
    358 	if(res.type != TSTRING)
    359 		error("register(name): arg type: name should be string");
    360 	tid = 0;
    361 	if(na == 2){
    362 		expr(av[1], &resid);
    363 		if(resid.type != TINT)
    364 			error("register(name[, threadid]): arg type: threadid should be int");
    365 		tid = resid.store.u.ival;
    366 	}
    367 	if((rp = regdesc(res.store.u.string->string)) == nil)
    368 		error("no such register");
    369 	r->op = OCONST;
    370 	r->type = TREG;
    371 	r->store.fmt = rp->format;
    372 	r->store.u.reg.name = rp->name;
    373 	r->store.u.reg.thread = tid;
    374 }
    375 
    376 void
    377 refconst(Node *r, Node *args)
    378 {
    379 	Node *n;
    380 
    381 	if(args == 0)
    382 		error("refconst(expr): arg count");
    383 
    384 	n = an(OCONST, ZN, ZN);
    385 	expr(args, n);
    386 
    387 	r->op = OCONST;
    388 	r->type = TCON;
    389 	r->store.u.con = n;
    390 }
    391 
    392 void
    393 dolook(Node *r, Node *args)
    394 {
    395 	Node res;
    396 	Lsym *l;
    397 
    398 	if(args == 0)
    399 		error("var(string): arg count");
    400 	expr(args, &res);
    401 	if(res.type != TSTRING)
    402 		error("var(string): arg type");
    403 
    404 	r->op = OCONST;
    405 	if((l = look(res.store.u.string->string)) == nil || l->v->set == 0){
    406 		r->type = TLIST;
    407 		r->store.u.l = nil;
    408 	}else{
    409 		r->type = l->v->type;
    410 		r->store = l->v->store;
    411 	}
    412 }
    413 
    414 void
    415 status(Node *r, Node *args)
    416 {
    417 	Node res;
    418 	char *p;
    419 
    420 	USED(r);
    421 	if(args == 0)
    422 		error("status(pid): no pid");
    423 	expr(args, &res);
    424 	if(res.type != TINT)
    425 		error("status(pid): arg type");
    426 
    427 	p = getstatus(res.store.u.ival);
    428 	r->store.u.string = strnode(p);
    429 	r->op = OCONST;
    430 	r->store.fmt = 's';
    431 	r->type = TSTRING;
    432 }
    433 
    434 void
    435 reason(Node *r, Node *args)
    436 {
    437 	Node res;
    438 
    439 	if(args == 0)
    440 		error("reason(cause): no cause");
    441 	expr(args, &res);
    442 	if(res.type != TINT)
    443 		error("reason(cause): arg type");
    444 
    445 	r->op = OCONST;
    446 	r->type = TSTRING;
    447 	r->store.fmt = 's';
    448 	r->store.u.string = strnode((*mach->exc)(cormap, acidregs));
    449 }
    450 
    451 void
    452 follow(Node *r, Node *args)
    453 {
    454 	int n, i;
    455 	Node res;
    456 	u64int f[10];
    457 	List **tail, *l;
    458 
    459 	if(args == 0)
    460 		error("follow(addr): no addr");
    461 	expr(args, &res);
    462 	if(res.type != TINT)
    463 		error("follow(addr): arg type");
    464 
    465 	n = (*mach->foll)(cormap, acidregs, res.store.u.ival, f);
    466 	if (n < 0)
    467 		error("follow(addr): %r");
    468 	tail = &r->store.u.l;
    469 	for(i = 0; i < n; i++) {
    470 		l = al(TINT);
    471 		l->store.u.ival = f[i];
    472 		l->store.fmt = 'X';
    473 		*tail = l;
    474 		tail = &l->next;
    475 	}
    476 }
    477 
    478 void
    479 funcbound(Node *r, Node *args)
    480 {
    481 	int n;
    482 	Node res;
    483 	u64int bounds[2];
    484 	List *l;
    485 
    486 	if(args == 0)
    487 		error("fnbound(addr): no addr");
    488 	expr(args, &res);
    489 	if(res.type != TINT)
    490 		error("fnbound(addr): arg type");
    491 
    492 	n = fnbound(res.store.u.ival, bounds);
    493 	if (n >= 0) {
    494 		r->store.u.l = al(TINT);
    495 		l = r->store.u.l;
    496 		l->store.u.ival = bounds[0];
    497 		l->store.fmt = 'X';
    498 		l->next = al(TINT);
    499 		l = l->next;
    500 		l->store.u.ival = bounds[1];
    501 		l->store.fmt = 'X';
    502 	}
    503 }
    504 
    505 void
    506 setproc(Node *r, Node *args)
    507 {
    508 	Node res;
    509 
    510 	USED(r);
    511 	if(args == 0)
    512 		error("setproc(pid): no pid");
    513 	expr(args, &res);
    514 	if(res.type != TINT)
    515 		error("setproc(pid): arg type");
    516 
    517 	sproc(res.store.u.ival);
    518 }
    519 
    520 void
    521 filepc(Node *r, Node *args)
    522 {
    523 	int i;
    524 	Node res;
    525 	char *p, c;
    526 	u64int v;
    527 
    528 	if(args == 0)
    529 		error("filepc(filename:line): arg count");
    530 	expr(args, &res);
    531 	if(res.type != TSTRING)
    532 		error("filepc(filename:line): arg type");
    533 
    534 	p = strchr(res.store.u.string->string, ':');
    535 	if(p == 0)
    536 		error("filepc(filename:line): bad arg format");
    537 
    538 	c = *p;
    539 	*p++ = '\0';
    540 	i = file2pc(res.store.u.string->string, atoi(p), &v);
    541 	p[-1] = c;
    542 	if(i < 0)
    543 		error("filepc(filename:line): can't find address");
    544 
    545 	r->op = OCONST;
    546 	r->type = TINT;
    547 	r->store.fmt = 'D';
    548 	r->store.u.ival = v;
    549 }
    550 
    551 void
    552 interpret(Node *r, Node *args)
    553 {
    554 	Node res;
    555 	int isave;
    556 
    557 	if(args == 0)
    558 		error("interpret(string): arg count");
    559 	expr(args, &res);
    560 	if(res.type != TSTRING)
    561 		error("interpret(string): arg type");
    562 
    563 	pushstr(&res);
    564 
    565 	isave = interactive;
    566 	interactive = 0;
    567 	r->store.u.ival = yyparse();
    568 	interactive = isave;
    569 	popio();
    570 	r->op = OCONST;
    571 	r->type = TINT;
    572 	r->store.fmt = 'D';
    573 }
    574 
    575 void
    576 include(Node *r, Node *args)
    577 {
    578 	char *file, *libfile;
    579 	static char buf[1024];
    580 	Node res;
    581 	int isave;
    582 
    583 	if(args == 0)
    584 		error("include(string): arg count");
    585 	expr(args, &res);
    586 	if(res.type != TSTRING)
    587 		error("include(string): arg type");
    588 
    589 	Bflush(bout);
    590 
    591 	libfile = nil;
    592 	file = res.store.u.string->string;
    593 	if(access(file, AREAD) < 0 && file[0] != '/'){
    594 		snprint(buf, sizeof buf, "#9/acid/%s", file);
    595 		libfile = unsharp(buf);
    596 		if(access(libfile, AREAD) >= 0){
    597 			strecpy(buf, buf+sizeof buf, libfile);
    598 			file = buf;
    599 		}
    600 		free(libfile);
    601 	}
    602 
    603 	pushfile(file);
    604 	isave = interactive;
    605 	interactive = 0;
    606 	r->store.u.ival = yyparse();
    607 	interactive = isave;
    608 	popio();
    609 	r->op = OCONST;
    610 	r->type = TINT;
    611 	r->store.fmt = 'D';
    612 }
    613 
    614 void
    615 includepipe(Node *r, Node *args)
    616 {
    617 	Node res;
    618 	int i, isave, pid, pip[2];
    619 	char *argv[4];
    620 	Waitmsg *w;
    621 
    622 	USED(r);
    623 	if(args == 0)
    624 		error("includepipe(string): arg count");
    625 	expr(args, &res);
    626 	if(res.type != TSTRING)
    627 		error("includepipe(string): arg type");
    628 
    629 	Bflush(bout);
    630 
    631 	argv[0] = "rc";
    632 	argv[1] = "-c";
    633 	argv[2] = res.store.u.string->string;
    634 	argv[3] = 0;
    635 
    636 	if(pipe(pip) < 0)
    637 		error("pipe: %r");
    638 
    639 	pid = fork();
    640 	switch(pid) {
    641 	case -1:
    642 		close(pip[0]);
    643 		close(pip[1]);
    644 		error("fork: %r");
    645 	case 0:
    646 		close(pip[0]);
    647 		close(0);
    648 		open("/dev/null", OREAD);
    649 		dup(pip[1], 1);
    650 		if(pip[1] > 1)
    651 			close(pip[1]);
    652 		for(i=3; i<100; i++)
    653 			close(i);
    654 		exec("rc", argv);
    655 		sysfatal("exec rc: %r");
    656 	}
    657 
    658 	close(pip[1]);
    659 	pushfd(pip[0]);
    660 
    661 	isave = interactive;
    662 	interactive = 0;
    663 	r->store.u.ival = yyparse();
    664 	interactive = isave;
    665 	popio();
    666 
    667 	r->op = OCONST;
    668 	r->type = TINT;
    669 	r->store.fmt = 'D';
    670 
    671 	w = waitfor(pid);
    672 	if(w->msg && w->msg[0])
    673 		error("includepipe(\"%s\"): %s", argv[2], w->msg);	/* leaks w */
    674 	free(w);
    675 }
    676 
    677 void
    678 rc(Node *r, Node *args)
    679 {
    680 	Node res;
    681 	int pid;
    682 	char *p, *q, *argv[4];
    683 	Waitmsg *w;
    684 
    685 	USED(r);
    686 	if(args == 0)
    687 		error("rc(string): arg count");
    688 	expr(args, &res);
    689 	if(res.type != TSTRING)
    690 		error("rc(string): arg type");
    691 
    692 	argv[0] = "rc";
    693 	argv[1] = "-c";
    694 	argv[2] = res.store.u.string->string;
    695 	argv[3] = 0;
    696 
    697 	pid = fork();
    698 	switch(pid) {
    699 	case -1:
    700 		error("fork %r");
    701 	case 0:
    702 		exec("rc", argv);
    703 		exits(0);
    704 	default:
    705 		w = waitfor(pid);
    706 		break;
    707 	}
    708 	p = w->msg;
    709 	q = strrchr(p, ':');
    710 	if (q)
    711 		p = q+1;
    712 
    713 	r->op = OCONST;
    714 	r->type = TSTRING;
    715 	r->store.u.string = strnode(p);
    716 	free(w);
    717 	r->store.fmt = 's';
    718 }
    719 
    720 void
    721 doerror(Node *r, Node *args)
    722 {
    723 	Node res;
    724 
    725 	USED(r);
    726 	if(args == 0)
    727 		error("error(string): arg count");
    728 	expr(args, &res);
    729 	if(res.type != TSTRING)
    730 		error("error(string): arg type");
    731 
    732 	error(res.store.u.string->string);
    733 }
    734 
    735 void
    736 doaccess(Node *r, Node *args)
    737 {
    738 	Node res;
    739 
    740 	if(args == 0)
    741 		error("access(filename): arg count");
    742 	expr(args, &res);
    743 	if(res.type != TSTRING)
    744 		error("access(filename): arg type");
    745 
    746 	r->op = OCONST;
    747 	r->type = TINT;
    748 	r->store.fmt = 'D';
    749 	r->store.u.ival = 0;
    750 	if(access(res.store.u.string->string, 4) == 0)
    751 		r->store.u.ival = 1;
    752 }
    753 
    754 void
    755 readfile(Node *r, Node *args)
    756 {
    757 	Node res;
    758 	int n, fd;
    759 	char *buf;
    760 	Dir *db;
    761 
    762 	if(args == 0)
    763 		error("readfile(filename): arg count");
    764 	expr(args, &res);
    765 	if(res.type != TSTRING)
    766 		error("readfile(filename): arg type");
    767 
    768 	fd = open(res.store.u.string->string, OREAD);
    769 	if(fd < 0)
    770 		return;
    771 
    772 	db = dirfstat(fd);
    773 	if(db == nil || db->length == 0)
    774 		n = 8192;
    775 	else
    776 		n = db->length;
    777 	free(db);
    778 
    779 	buf = malloc(n);
    780 	n = read(fd, buf, n);
    781 
    782 	if(n > 0) {
    783 		r->op = OCONST;
    784 		r->type = TSTRING;
    785 		r->store.u.string = strnodlen(buf, n);
    786 		r->store.fmt = 's';
    787 	}
    788 	free(buf);
    789 	close(fd);
    790 }
    791 
    792 void
    793 getfile(Node *r, Node *args)
    794 {
    795 	int n;
    796 	char *p;
    797 	Node res;
    798 	String *s;
    799 	Biobuf *bp;
    800 	List **l, *new;
    801 
    802 	if(args == 0)
    803 		error("file(filename): arg count");
    804 	expr(args, &res);
    805 	if(res.type != TSTRING)
    806 		error("file(filename): arg type");
    807 
    808 	r->op = OCONST;
    809 	r->type = TLIST;
    810 	r->store.u.l = 0;
    811 
    812 	p = res.store.u.string->string;
    813 	bp = Bopen(p, OREAD);
    814 	if(bp == 0)
    815 		return;
    816 
    817 	l = &r->store.u.l;
    818 	for(;;) {
    819 		p = Brdline(bp, '\n');
    820 		n = Blinelen(bp);
    821 		if(p == 0) {
    822 			if(n == 0)
    823 				break;
    824 			s = strnodlen(0, n);
    825 			Bread(bp, s->string, n);
    826 		}
    827 		else
    828 			s = strnodlen(p, n-1);
    829 
    830 		new = al(TSTRING);
    831 		new->store.u.string = s;
    832 		new->store.fmt = 's';
    833 		*l = new;
    834 		l = &new->next;
    835 	}
    836 	Bterm(bp);
    837 }
    838 
    839 void
    840 cvtatof(Node *r, Node *args)
    841 {
    842 	Node res;
    843 
    844 	if(args == 0)
    845 		error("atof(string): arg count");
    846 	expr(args, &res);
    847 	if(res.type != TSTRING)
    848 		error("atof(string): arg type");
    849 
    850 	r->op = OCONST;
    851 	r->type = TFLOAT;
    852 	r->store.u.fval = atof(res.store.u.string->string);
    853 	r->store.fmt = 'f';
    854 }
    855 
    856 void
    857 cvtatoi(Node *r, Node *args)
    858 {
    859 	Node res;
    860 
    861 	if(args == 0)
    862 		error("atoi(string): arg count");
    863 	expr(args, &res);
    864 	if(res.type != TSTRING)
    865 		error("atoi(string): arg type");
    866 
    867 	r->op = OCONST;
    868 	r->type = TINT;
    869 	r->store.u.ival = strtoull(res.store.u.string->string, 0, 0);
    870 	r->store.fmt = 'D';
    871 }
    872 
    873 void
    874 cvtitoa(Node *r, Node *args)
    875 {
    876 	Node res;
    877 	Node *av[Maxarg];
    878 	s64int ival;
    879 	char buf[128], *fmt;
    880 
    881 	if(args == 0)
    882 err:
    883 		error("itoa(number [, printformat]): arg count");
    884 	na = 0;
    885 	flatten(av, args);
    886 	if(na == 0 || na > 2)
    887 		goto err;
    888 	expr(av[0], &res);
    889 	if(res.type != TINT)
    890 		error("itoa(integer): arg type");
    891 	ival = (int)res.store.u.ival;
    892 	fmt = "%d";
    893 	if(na == 2){
    894 		expr(av[1], &res);
    895 		if(res.type != TSTRING)
    896 			error("itoa(integer, string): arg type");
    897 		fmt = res.store.u.string->string;
    898 	}
    899 
    900 	sprint(buf, fmt, ival);
    901 	r->op = OCONST;
    902 	r->type = TSTRING;
    903 	r->store.u.string = strnode(buf);
    904 	r->store.fmt = 's';
    905 }
    906 
    907 List*
    908 mapent(Map *m)
    909 {
    910 	int i;
    911 	List *l, *n, **t, *h;
    912 
    913 	h = 0;
    914 	t = &h;
    915 	for(i = 0; i < m->nseg; i++) {
    916 		l = al(TSTRING);
    917 		n = al(TLIST);
    918 		n->store.u.l = l;
    919 		*t = n;
    920 		t = &n->next;
    921 		l->store.u.string = strnode(m->seg[i].name);
    922 		l->store.fmt = 's';
    923 		l->next = al(TSTRING);
    924 		l = l->next;
    925 		l->store.u.string = strnode(m->seg[i].file ? m->seg[i].file : "");
    926 		l->store.fmt = 's';
    927 		l->next = al(TINT);
    928 		l = l->next;
    929 		l->store.u.ival = m->seg[i].base;
    930 		l->store.fmt = 'X';
    931 		l->next = al(TINT);
    932 		l = l->next;
    933 		l->store.u.ival = m->seg[i].base + m->seg[i].size;
    934 		l->store.fmt = 'X';
    935 		l->next = al(TINT);
    936 		l = l->next;
    937 		l->store.u.ival = m->seg[i].offset;
    938 		l->store.fmt = 'X';
    939 	}
    940 	return h;
    941 }
    942 
    943 void
    944 map(Node *r, Node *args)
    945 {
    946 	int i;
    947 	Map *m;
    948 	List *l;
    949 	char *nam, *fil;
    950 	Node *av[Maxarg], res;
    951 
    952 	na = 0;
    953 	flatten(av, args);
    954 
    955 	if(na != 0) {
    956 		expr(av[0], &res);
    957 		if(res.type != TLIST)
    958 			error("map(list): map needs a list");
    959 		if(listlen(res.store.u.l) != 5)
    960 			error("map(list): list must have 5 entries");
    961 
    962 		l = res.store.u.l;
    963 		if(l->type != TSTRING)
    964 			error("map name must be a string");
    965 		nam = l->store.u.string->string;
    966 		l = l->next;
    967 		if(l->type != TSTRING)
    968 			error("map file must be a string");
    969 		fil = l->store.u.string->string;
    970 		m = symmap;
    971 		i = findseg(m, nam, fil);
    972 		if(i < 0) {
    973 			m = cormap;
    974 			i = findseg(m, nam, fil);
    975 		}
    976 		if(i < 0)
    977 			error("%s %s is not a map entry", nam, fil);
    978 		l = l->next;
    979 		if(l->type != TINT)
    980 			error("map entry not int");
    981 		m->seg[i].base = l->store.u.ival;
    982 /*
    983 		if (strcmp(ent, "text") == 0)
    984 			textseg(l->store.u.ival, &fhdr);
    985 */
    986 		l = l->next;
    987 		if(l->type != TINT)
    988 			error("map entry not int");
    989 		m->seg[i].size = l->store.u.ival - m->seg[i].base;
    990 		l = l->next;
    991 		if(l->type != TINT)
    992 			error("map entry not int");
    993 		m->seg[i].offset = l->store.u.ival;
    994 	}
    995 
    996 	r->type = TLIST;
    997 	r->store.u.l = 0;
    998 	if(symmap)
    999 		r->store.u.l = mapent(symmap);
   1000 	if(cormap) {
   1001 		if(r->store.u.l == 0)
   1002 			r->store.u.l = mapent(cormap);
   1003 		else {
   1004 			for(l = r->store.u.l; l->next; l = l->next)
   1005 				;
   1006 			l->next = mapent(cormap);
   1007 		}
   1008 	}
   1009 }
   1010 
   1011 void
   1012 flatten(Node **av, Node *n)
   1013 {
   1014 	if(n == 0)
   1015 		return;
   1016 
   1017 	switch(n->op) {
   1018 	case OLIST:
   1019 		flatten(av, n->left);
   1020 		flatten(av, n->right);
   1021 		break;
   1022 	default:
   1023 		av[na++] = n;
   1024 		if(na >= Maxarg)
   1025 			error("too many function arguments");
   1026 		break;
   1027 	}
   1028 }
   1029 
   1030 static struct
   1031 {
   1032 	char *name;
   1033 	u64int val;
   1034 } sregs[Maxarg/2];
   1035 static int nsregs;
   1036 
   1037 static int
   1038 straceregrw(Regs *regs, char *name, u64int *val, int isr)
   1039 {
   1040 	int i;
   1041 
   1042 	if(!isr){
   1043 		werrstr("saved registers cannot be written");
   1044 		return -1;
   1045 	}
   1046 	for(i=0; i<nsregs; i++)
   1047 		if(strcmp(sregs[i].name, name) == 0){
   1048 			*val = sregs[i].val;
   1049 			return 0;
   1050 		}
   1051 	return rget(acidregs, name, val);
   1052 }
   1053 
   1054 void
   1055 strace(Node *r, Node *args)
   1056 {
   1057 	Node *av[Maxarg], res;
   1058 	List *l;
   1059 	Regs regs;
   1060 
   1061 	na = 0;
   1062 	flatten(av, args);
   1063 
   1064 	if(na != 1)
   1065 		error("strace(list): want one arg");
   1066 
   1067 	expr(av[0], &res);
   1068 	if(res.type != TLIST)
   1069 		error("strace(list): strace needs a list");
   1070 	l = res.store.u.l;
   1071 	if(listlen(l)%2)
   1072 		error("strace(list): strace needs an even-length list");
   1073 	for(nsregs=0; l; nsregs++){
   1074 		if(l->type != TSTRING)
   1075 			error("strace({r,v,r,v,...}): non-string name");
   1076 		sregs[nsregs].name = l->store.u.string->string;
   1077 		if(regdesc(sregs[nsregs].name) == nil)
   1078 			error("strace: bad register '%s'", sregs[nsregs].name);
   1079 		l = l->next;
   1080 
   1081 		if(l == nil)
   1082 			error("cannot happen in strace");
   1083 		if(l->type != TINT)
   1084 			error("strace: non-int value for %s", sregs[nsregs].name);
   1085 		sregs[nsregs].val = l->store.u.ival;
   1086 		l = l->next;
   1087 	}
   1088 	regs.rw = straceregrw;
   1089 
   1090 	tracelist = 0;
   1091 	if(stacktrace(cormap, &regs, trlist) <= 0)
   1092 		error("no stack frame");
   1093 	r->type = TLIST;
   1094 	r->store.u.l = tracelist;
   1095 }
   1096 
   1097 void
   1098 regerror(char *msg)
   1099 {
   1100 	error(msg);
   1101 }
   1102 
   1103 void
   1104 regexp(Node *r, Node *args)
   1105 {
   1106 	Node res;
   1107 	Reprog *rp;
   1108 	Node *av[Maxarg];
   1109 
   1110 	na = 0;
   1111 	flatten(av, args);
   1112 	if(na != 2)
   1113 		error("regexp(pattern, string): arg count");
   1114 	expr(av[0], &res);
   1115 	if(res.type != TSTRING)
   1116 		error("regexp(pattern, string): pattern must be string");
   1117 	rp = regcomp(res.store.u.string->string);
   1118 	if(rp == 0)
   1119 		return;
   1120 
   1121 	expr(av[1], &res);
   1122 	if(res.type != TSTRING)
   1123 		error("regexp(pattern, string): bad string");
   1124 
   1125 	r->store.fmt = 'D';
   1126 	r->type = TINT;
   1127 	r->store.u.ival = regexec(rp, res.store.u.string->string, 0, 0);
   1128 	free(rp);
   1129 }
   1130 
   1131 char vfmt[] = "aBbcCdDfFgGiIoOqQrRsSuUVxXYZ";
   1132 
   1133 void
   1134 fmt(Node *r, Node *args)
   1135 {
   1136 	Node res;
   1137 	Node *av[Maxarg];
   1138 
   1139 	na = 0;
   1140 	flatten(av, args);
   1141 	if(na != 2)
   1142 		error("fmt(obj, fmt): arg count");
   1143 	expr(av[1], &res);
   1144 	if(res.type != TINT || strchr(vfmt, res.store.u.ival) == 0)
   1145 		error("fmt(obj, fmt): bad format '%c'", (char)res.store.u.ival);
   1146 	expr(av[0], r);
   1147 	r->store.fmt = res.store.u.ival;
   1148 }
   1149 
   1150 void
   1151 patom(char type, Store *res)
   1152 {
   1153 	int i;
   1154 	char buf[512];
   1155 	extern char *typenames[];
   1156 	Node *n;
   1157 
   1158 	switch(type){
   1159 	case TREG:
   1160 		if(res->u.reg.thread)
   1161 			Bprint(bout, "register(\"%s\", %#ux)", res->u.reg.name, res->u.reg.thread);
   1162 		else
   1163 			Bprint(bout, "register(\"%s\")", res->u.reg.name);
   1164 		return;
   1165 	case TCON:
   1166 		Bprint(bout, "refconst(");
   1167 		n = res->u.con;
   1168 		patom(n->type, &n->store);
   1169 		Bprint(bout, ")");
   1170 		return;
   1171 	}
   1172 
   1173 	switch(res->fmt){
   1174 	case 'c':
   1175 	case 'C':
   1176 	case 'r':
   1177 	case 'B':
   1178 	case 'b':
   1179 	case 'X':
   1180 	case 'x':
   1181 	case 'W':
   1182 	case 'D':
   1183 	case 'd':
   1184 	case 'u':
   1185 	case 'U':
   1186 	case 'Z':
   1187 	case 'V':
   1188 	case 'Y':
   1189 	case 'o':
   1190 	case 'O':
   1191 	case 'q':
   1192 	case 'Q':
   1193 	case 'a':
   1194 	case 'A':
   1195 	case 'I':
   1196 	case 'i':
   1197 		if(type != TINT){
   1198 		badtype:
   1199 			Bprint(bout, "*%s\\%c*", typenames[(uchar)type], res->fmt);
   1200 			return;
   1201 		}
   1202 		break;
   1203 
   1204 	case 'f':
   1205 	case 'F':
   1206 		if(type != TFLOAT)
   1207 			goto badtype;
   1208 		break;
   1209 
   1210 	case 's':
   1211 	case 'g':
   1212 	case 'G':
   1213 	case 'R':
   1214 		if(type != TSTRING)
   1215 			goto badtype;
   1216 		break;
   1217 	}
   1218 
   1219 	switch(res->fmt) {
   1220 	case 'c':
   1221 		Bprint(bout, "%c", (int)res->u.ival);
   1222 		break;
   1223 	case 'C':
   1224 		if(res->u.ival < ' ' || res->u.ival >= 0x7f)
   1225 			Bprint(bout, "%3d", (int)res->u.ival&0xff);
   1226 		else
   1227 			Bprint(bout, "%3c", (int)res->u.ival);
   1228 		break;
   1229 	case 'r':
   1230 		Bprint(bout, "%C", (int)res->u.ival);
   1231 		break;
   1232 	case 'B':
   1233 		memset(buf, '0', 34);
   1234 		buf[1] = 'b';
   1235 		for(i = 0; i < 32; i++) {
   1236 			if(res->u.ival & (1<<i))
   1237 				buf[33-i] = '1';
   1238 		}
   1239 		buf[35] = '\0';
   1240 		Bprint(bout, "%s", buf);
   1241 		break;
   1242 	case 'b':
   1243 		Bprint(bout, "%#.2x", (int)res->u.ival&0xff);
   1244 		break;
   1245 	case 'X':
   1246 		Bprint(bout, "%#.8lux", (ulong)res->u.ival);
   1247 		break;
   1248 	case 'x':
   1249 		Bprint(bout, "%#.4lux", (ulong)res->u.ival&0xffff);
   1250 		break;
   1251 	case 'W':
   1252 		Bprint(bout, "%#.16llux", res->u.ival);
   1253 		break;
   1254 	case 'D':
   1255 		Bprint(bout, "%d", (int)res->u.ival);
   1256 		break;
   1257 	case 'd':
   1258 		Bprint(bout, "%d", (ushort)res->u.ival);
   1259 		break;
   1260 	case 'u':
   1261 		Bprint(bout, "%d", (int)res->u.ival&0xffff);
   1262 		break;
   1263 	case 'U':
   1264 		Bprint(bout, "%lud", (ulong)res->u.ival);
   1265 		break;
   1266 	case 'Z':
   1267 		Bprint(bout, "%llud", res->u.ival);
   1268 		break;
   1269 	case 'V':
   1270 		Bprint(bout, "%lld", res->u.ival);
   1271 		break;
   1272 	case 'Y':
   1273 		Bprint(bout, "%#.16llux", res->u.ival);
   1274 		break;
   1275 	case 'o':
   1276 		Bprint(bout, "%#.11uo", (int)res->u.ival&0xffff);
   1277 		break;
   1278 	case 'O':
   1279 		Bprint(bout, "%#.6uo", (int)res->u.ival);
   1280 		break;
   1281 	case 'q':
   1282 		Bprint(bout, "%#.11o", (short)(res->u.ival&0xffff));
   1283 		break;
   1284 	case 'Q':
   1285 		Bprint(bout, "%#.6o", (int)res->u.ival);
   1286 		break;
   1287 	case 'f':
   1288 	case 'F':
   1289 		Bprint(bout, "%g", res->u.fval);
   1290 		break;
   1291 	case 's':
   1292 	case 'g':
   1293 	case 'G':
   1294 		Bwrite(bout, res->u.string->string, res->u.string->len);
   1295 		break;
   1296 	case 'R':
   1297 		Bprint(bout, "%S", (Rune*)res->u.string->string);
   1298 		break;
   1299 	case 'a':
   1300 	case 'A':
   1301 		symoff(buf, sizeof(buf), res->u.ival, CANY);
   1302 		Bprint(bout, "%s", buf);
   1303 		break;
   1304 	case 'I':
   1305 	case 'i':
   1306 		if (symmap == nil || (*mach->das)(symmap, res->u.ival, res->fmt, buf, sizeof(buf)) < 0)
   1307 			Bprint(bout, "no instruction");
   1308 		else
   1309 			Bprint(bout, "%s", buf);
   1310 		break;
   1311 	}
   1312 }
   1313 
   1314 void
   1315 blprint(List *l)
   1316 {
   1317 	Store *res;
   1318 
   1319 	Bprint(bout, "{");
   1320 	while(l) {
   1321 		switch(l->type) {
   1322 		case TINT:
   1323 			res = &l->store;
   1324 			if(res->fmt == 'c'){
   1325 				Bprint(bout, "\'%c\'", (int)res->u.ival);
   1326 				break;
   1327 			}else if(res->fmt == 'r'){
   1328 				Bprint(bout, "\'%C\'", (int)res->u.ival);
   1329 				break;
   1330 			}
   1331 			/* fall through */
   1332 		default:
   1333 			patom(l->type, &l->store);
   1334 			break;
   1335 		case TSTRING:
   1336 			Bputc(bout, '"');
   1337 			patom(l->type, &l->store);
   1338 			Bputc(bout, '"');
   1339 			break;
   1340 		case TLIST:
   1341 			blprint(l->store.u.l);
   1342 			break;
   1343 		case TCODE:
   1344 			pcode(l->store.u.cc, 0);
   1345 			break;
   1346 		}
   1347 		l = l->next;
   1348 		if(l)
   1349 			Bprint(bout, ", ");
   1350 	}
   1351 	Bprint(bout, "}");
   1352 }
   1353 
   1354 int
   1355 comx(Node res)
   1356 {
   1357 	Lsym *sl;
   1358 	Node *n, xx;
   1359 
   1360 	if(res.store.fmt != 'a' && res.store.fmt != 'A')
   1361 		return 0;
   1362 
   1363 	if(res.store.comt == 0 || res.store.comt->base == 0)
   1364 		return 0;
   1365 
   1366 	sl = res.store.comt->base;
   1367 	if(sl->proc) {
   1368 		res.left = ZN;
   1369 		res.right = ZN;
   1370 		n = an(ONAME, ZN, ZN);
   1371 		n->sym = sl;
   1372 		n = an(OCALL, n, &res);
   1373 			n->left->sym = sl;
   1374 		expr(n, &xx);
   1375 		return 1;
   1376 	}
   1377 	print("(%s)", sl->name);
   1378 	return 0;
   1379 }
   1380 
   1381 void
   1382 bprint(Node *r, Node *args)
   1383 {
   1384 	int i, nas;
   1385 	Node res, *av[Maxarg];
   1386 
   1387 	USED(r);
   1388 	na = 0;
   1389 	flatten(av, args);
   1390 	nas = na;
   1391 	for(i = 0; i < nas; i++) {
   1392 		expr(av[i], &res);
   1393 		switch(res.type) {
   1394 		default:
   1395 			if(comx(res))
   1396 				break;
   1397 			patom(res.type, &res.store);
   1398 			break;
   1399 		case TCODE:
   1400 			pcode(res.store.u.cc, 0);
   1401 			break;
   1402 		case TLIST:
   1403 			blprint(res.store.u.l);
   1404 			break;
   1405 		}
   1406 	}
   1407 	if(ret == 0)
   1408 		Bputc(bout, '\n');
   1409 }
   1410 
   1411 void
   1412 printto(Node *r, Node *args)
   1413 {
   1414 	int fd;
   1415 	Biobuf *b;
   1416 	int i, nas;
   1417 	Node res, *av[Maxarg];
   1418 
   1419 	USED(r);
   1420 	na = 0;
   1421 	flatten(av, args);
   1422 	nas = na;
   1423 
   1424 	expr(av[0], &res);
   1425 	if(res.type != TSTRING)
   1426 		error("printto(string, ...): need string");
   1427 
   1428 	fd = create(res.store.u.string->string, OWRITE, 0666);
   1429 	if(fd < 0)
   1430 		fd = open(res.store.u.string->string, OWRITE);
   1431 	if(fd < 0)
   1432 		error("printto: open %s: %r", res.store.u.string->string);
   1433 
   1434 	b = gmalloc(sizeof(Biobuf));
   1435 	Binit(b, fd, OWRITE);
   1436 
   1437 	Bflush(bout);
   1438 	io[iop++] = bout;
   1439 	bout = b;
   1440 
   1441 	for(i = 1; i < nas; i++) {
   1442 		expr(av[i], &res);
   1443 		switch(res.type) {
   1444 		default:
   1445 			if(comx(res))
   1446 				break;
   1447 			patom(res.type, &res.store);
   1448 			break;
   1449 		case TLIST:
   1450 			blprint(res.store.u.l);
   1451 			break;
   1452 		}
   1453 	}
   1454 	if(ret == 0)
   1455 		Bputc(bout, '\n');
   1456 
   1457 	Bterm(b);
   1458 	close(fd);
   1459 	free(b);
   1460 	bout = io[--iop];
   1461 }
   1462 
   1463 void
   1464 pcfile(Node *r, Node *args)
   1465 {
   1466 	Node res;
   1467 	char *p, buf[128];
   1468 
   1469 	if(args == 0)
   1470 		error("pcfile(addr): arg count");
   1471 	expr(args, &res);
   1472 	if(res.type != TINT)
   1473 		error("pcfile(addr): arg type");
   1474 
   1475 	r->type = TSTRING;
   1476 	r->store.fmt = 's';
   1477 	if(fileline(res.store.u.ival, buf, sizeof(buf)) < 0) {
   1478 		r->store.u.string = strnode("?file?");
   1479 		return;
   1480 	}
   1481 	p = strrchr(buf, ':');
   1482 	if(p == 0)
   1483 		error("pcfile(addr): funny file %s", buf);
   1484 	*p = '\0';
   1485 	r->store.u.string = strnode(buf);
   1486 }
   1487 
   1488 void
   1489 pcline(Node *r, Node *args)
   1490 {
   1491 	Node res;
   1492 	char *p, buf[128];
   1493 
   1494 	if(args == 0)
   1495 		error("pcline(addr): arg count");
   1496 	expr(args, &res);
   1497 	if(res.type != TINT)
   1498 		error("pcline(addr): arg type");
   1499 
   1500 	r->type = TINT;
   1501 	r->store.fmt = 'D';
   1502 	if(fileline(res.store.u.ival, buf, sizeof(buf)) < 0) {
   1503 		r->store.u.ival = 0;
   1504 		return;
   1505 	}
   1506 
   1507 	p = strrchr(buf, ':');
   1508 	if(p == 0)
   1509 		error("pcline(addr): funny file %s", buf);
   1510 	r->store.u.ival = atoi(p+1);
   1511 }
   1512 
   1513 void
   1514 textfile(Node *r, Node *args)
   1515 {
   1516 	char *file;
   1517 	long base;
   1518 	Fhdr *fp;
   1519 	Node res, *av[Maxarg];
   1520 	List *l, *l2, **tail, *list, *tl;
   1521 
   1522 	na = 0;
   1523 	flatten(av, args);
   1524 
   1525 	if(na != 0) {
   1526 		expr(av[0], &res);
   1527 		if(res.type != TLIST)
   1528 			error("textfile(list): textfile needs a list");
   1529 		if(listlen(res.store.u.l) != 2)
   1530 			error("textfile(list): list must have 2 entries");
   1531 
   1532 		l = res.store.u.l;
   1533 		if(l->type != TSTRING)
   1534 			error("textfile name must be a string");
   1535 		file = l->store.u.string->string;
   1536 
   1537 		l = l->next;
   1538 		if(l->type != TINT)
   1539 			error("textfile base must be an int");
   1540 		base = l->store.u.ival;
   1541 
   1542 		if((fp = crackhdr(file, OREAD)) == nil)
   1543 			error("crackhdr %s: %r", file);
   1544 		Bflush(bout);
   1545 		fp->base = base;
   1546 		fprint(2, "%s: %s %s %s\n", file, fp->aname, fp->mname, fp->fname);
   1547 		if(mapfile(fp, base, symmap, nil) < 0)
   1548 			fprint(2, "mapping %s: %r\n", file);
   1549 		if(corhdr){
   1550 			unmapfile(corhdr, cormap);
   1551 			mapfile(fp, base, cormap, nil);
   1552 			free(correg);
   1553 			correg = nil;
   1554 			mapfile(corhdr, 0, cormap, &correg);
   1555 		}
   1556 		if(symopen(fp) < 0)
   1557 			fprint(2, "symopen %s: %r\n", file);
   1558 		else
   1559 			addvarsym(fp);
   1560 		return;
   1561 	}
   1562 
   1563 	l2 = nil;
   1564 	tail = &l2;
   1565 	for(fp=fhdrlist; fp; fp=fp->next){
   1566 		if(fp->ftype == FCORE)
   1567 			continue;
   1568 		tl = al(TLIST);
   1569 		*tail = tl;
   1570 		tail = &tl->next;
   1571 
   1572 		list = al(TSTRING);
   1573 		tl->store.u.l = list;
   1574 		list->store.u.string = strnode(fp->filename);
   1575 		list->store.fmt = 's';
   1576 		list->next = al(TINT);
   1577 		list = list->next;
   1578 		list->store.fmt = 'X';
   1579 		list->store.u.ival = fp->base;
   1580 	}
   1581 
   1582 	r->type = TLIST;
   1583 	r->store.u.l = l2;
   1584 }
   1585 
   1586 void
   1587 deltextfile(Node *r, Node *args)
   1588 {
   1589 	int did;
   1590 	char *file;
   1591 	Fhdr *fp, *fpnext;
   1592 	Node res, *av[Maxarg];
   1593 
   1594 	na = 0;
   1595 	flatten(av, args);
   1596 
   1597 	if(na != 1)
   1598 		error("deltextfile(string): arg count");
   1599 
   1600 	expr(av[0], &res);
   1601 	if(res.type != TSTRING)
   1602 		error("deltextfile(string): arg type");
   1603 	file = res.store.u.string->string;
   1604 
   1605 	did = 0;
   1606 	for(fp=fhdrlist; fp; fp=fpnext){
   1607 		fpnext = fp->next;
   1608 		if(fp->ftype == FCORE)
   1609 			continue;
   1610 		if(strcmp(file, fp->filename) == 0){
   1611 			did = 1;
   1612 			if(fp == symhdr)
   1613 				error("cannot remove symbols from main text file");
   1614 			unmapfile(fp, symmap);
   1615 			uncrackhdr(fp);
   1616 		}
   1617 	}
   1618 
   1619 	delvarsym(file);
   1620 	if(!did)
   1621 		error("symbol file %s not open", file);
   1622 }
   1623 
   1624 void
   1625 stringn(Node *r, Node *args)
   1626 {
   1627 	uint addr;
   1628 	int i, n, ret;
   1629 	Node res, *av[Maxarg];
   1630 	char *buf;
   1631 
   1632 	na = 0;
   1633 	flatten(av, args);
   1634 	if(na != 2)
   1635 		error("stringn(addr, n): arg count");
   1636 
   1637 	expr(av[0], &res);
   1638 	if(res.type != TINT)
   1639 		error("stringn(addr, n): arg type");
   1640 	addr = res.store.u.ival;
   1641 
   1642 	expr(av[1], &res);
   1643 	if(res.type != TINT)
   1644 		error("stringn(addr,n): arg type");
   1645 	n = res.store.u.ival;
   1646 
   1647 	buf = malloc(n+1);
   1648 	if(buf == nil)
   1649 		error("out of memory");
   1650 
   1651 	r->type = TSTRING;
   1652 	for(i=0; i<n; i++){
   1653 		ret = get1(cormap, addr, (uchar*)&buf[i], 1);
   1654 		if(ret < 0){
   1655 			free(buf);
   1656 			error("indir: %r");
   1657 		}
   1658 		addr++;
   1659 	}
   1660 	buf[n] = 0;
   1661 	r->store.u.string = strnode(buf);
   1662 	free(buf);
   1663 }