| #include <u.h> | 
 | #include <libc.h> | 
 | #include <bio.h> | 
 | #include <ctype.h> | 
 | #include <mach.h> | 
 | #include <regexp.h> | 
 | #define Extern extern | 
 | #include "acid.h" | 
 | #include "y.tab.h" | 
 |  | 
 | void	cvtatof(Node*, Node*); | 
 | void	cvtatoi(Node*, Node*); | 
 | void	cvtitoa(Node*, Node*); | 
 | void	bprint(Node*, Node*); | 
 | void	funcbound(Node*, Node*); | 
 | void	printto(Node*, Node*); | 
 | void	getfile(Node*, Node*); | 
 | void	fmt(Node*, Node*); | 
 | void	pcfile(Node*, Node*); | 
 | void	pcline(Node*, Node*); | 
 | void	setproc(Node*, Node*); | 
 | void	strace(Node*, Node*); | 
 | void	follow(Node*, Node*); | 
 | void	reason(Node*, Node*); | 
 | void	newproc(Node*, Node*); | 
 | void	startstop(Node*, Node*); | 
 | void	match(Node*, Node*); | 
 | void	status(Node*, Node*); | 
 | void	xkill(Node*,Node*); | 
 | void	waitstop(Node*, Node*); | 
 | void waitsyscall(Node*, Node*); | 
 | void	stop(Node*, Node*); | 
 | void	start(Node*, Node*); | 
 | void	filepc(Node*, Node*); | 
 | void	doerror(Node*, Node*); | 
 | void	rc(Node*, Node*); | 
 | void	doaccess(Node*, Node*); | 
 | void	map(Node*, Node*); | 
 | void	readfile(Node*, Node*); | 
 | void	interpret(Node*, Node*); | 
 | void	include(Node*, Node*); | 
 | void	includepipe(Node*, Node*); | 
 | void	regexp(Node*, Node*); | 
 | void textfile(Node*, Node*); | 
 | void deltextfile(Node*, Node*); | 
 | void stringn(Node*, Node*); | 
 | void xregister(Node*, Node*); | 
 | void refconst(Node*, Node*); | 
 | void dolook(Node*, Node*); | 
 | void step1(Node*, Node*); | 
 |  | 
 | typedef struct Btab Btab; | 
 | struct Btab | 
 | { | 
 | 	char	*name; | 
 | 	void	(*fn)(Node*, Node*); | 
 | } tab[] = | 
 | { | 
 | 	"access",	doaccess, | 
 | 	"atof",		cvtatof, | 
 | 	"atoi",		cvtatoi, | 
 | 	"deltextfile",	deltextfile, | 
 | 	"error",	doerror, | 
 | 	"file",		getfile, | 
 | 	"filepc",	filepc, | 
 | 	"fnbound",	funcbound, | 
 | 	"fmt",		fmt, | 
 | 	"follow",	follow, | 
 | 	"include",	include, | 
 | 	"includepipe",	includepipe, | 
 | 	"interpret",	interpret, | 
 | 	"itoa",		cvtitoa, | 
 | 	"kill",		xkill, | 
 | 	"map",		map, | 
 | 	"match",	match, | 
 | 	"newproc",	newproc, | 
 | 	"pcfile",	pcfile, | 
 | 	"pcline",	pcline, | 
 | 	"print",	bprint, | 
 | 	"printto",	printto, | 
 | 	"rc",		rc, | 
 | 	"readfile",	readfile, | 
 | 	"reason",	reason, | 
 | 	"refconst",	refconst, | 
 | 	"regexp",	regexp, | 
 | 	"register",	xregister, | 
 | 	"setproc",	setproc, | 
 | 	"start",	start, | 
 | 	"startstop",	startstop, | 
 | 	"status",	status, | 
 | 	"step1",	step1, | 
 | 	"stop",		stop, | 
 | 	"strace",	strace, | 
 | 	"stringn",	stringn, | 
 | 	"textfile",	textfile, | 
 | 	"var",	dolook, | 
 | 	"waitstop",	waitstop, | 
 | 	"waitsyscall",	waitsyscall, | 
 | 	0 | 
 | }; | 
 |  | 
 | void | 
 | mkprint(Lsym *s) | 
 | { | 
 | 	prnt = malloc(sizeof(Node)); | 
 | 	memset(prnt, 0, sizeof(Node)); | 
 | 	prnt->op = OCALL; | 
 | 	prnt->left = malloc(sizeof(Node)); | 
 | 	memset(prnt->left, 0, sizeof(Node)); | 
 | 	prnt->left->sym = s; | 
 | } | 
 |  | 
 | void | 
 | installbuiltin(void) | 
 | { | 
 | 	Btab *b; | 
 | 	Lsym *s; | 
 |  | 
 | 	b = tab; | 
 | 	while(b->name) { | 
 | 		s = look(b->name); | 
 | 		if(s == 0) | 
 | 			s = enter(b->name, Tid); | 
 |  | 
 | 		s->builtin = b->fn; | 
 | 		if(b->fn == bprint) | 
 | 			mkprint(s); | 
 | 		b++; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | match(Node *r, Node *args) | 
 | { | 
 | 	int i; | 
 | 	List *f; | 
 | 	Node *av[Maxarg]; | 
 | 	Node resi, resl; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 | 	if(na != 2) | 
 | 		error("match(obj, list): arg count"); | 
 |  | 
 | 	expr(av[1], &resl); | 
 | 	if(resl.type != TLIST) | 
 | 		error("match(obj, list): need list"); | 
 | 	expr(av[0], &resi); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TINT; | 
 | 	r->store.fmt = 'D'; | 
 | 	r->store.u.ival = -1; | 
 |  | 
 | 	i = 0; | 
 | 	for(f = resl.store.u.l; f; f = f->next) { | 
 | 		if(resi.type == f->type) { | 
 | 			switch(resi.type) { | 
 | 			case TINT: | 
 | 				if(resi.store.u.ival == f->store.u.ival) { | 
 | 					r->store.u.ival = i; | 
 | 					return; | 
 | 				} | 
 | 				break; | 
 | 			case TFLOAT: | 
 | 				if(resi.store.u.fval == f->store.u.fval) { | 
 | 					r->store.u.ival = i; | 
 | 					return; | 
 | 				} | 
 | 				break; | 
 | 			case TSTRING: | 
 | 				if(scmp(resi.store.u.string, f->store.u.string)) { | 
 | 					r->store.u.ival = i; | 
 | 					return; | 
 | 				} | 
 | 				break; | 
 | 			case TLIST: | 
 | 				error("match(obj, list): not defined for list"); | 
 | 			} | 
 | 		} | 
 | 		i++; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | newproc(Node *r, Node *args) | 
 | { | 
 | 	int i; | 
 | 	Node res; | 
 | 	char *p, *e; | 
 | 	char *argv[Maxarg], buf[Strsize]; | 
 |  | 
 | 	i = 1; | 
 | 	argv[0] = symfil; | 
 |  | 
 | 	if(args) { | 
 | 		expr(args, &res); | 
 | 		if(res.type != TSTRING) | 
 | 			error("newproc(): arg not string"); | 
 | 		if(res.store.u.string->len >= sizeof(buf)) | 
 | 			error("newproc(): too many arguments"); | 
 | 		memmove(buf, res.store.u.string->string, res.store.u.string->len); | 
 | 		buf[res.store.u.string->len] = '\0'; | 
 | 		p = buf; | 
 | 		e = buf+res.store.u.string->len; | 
 | 		for(;;) { | 
 | 			while(p < e && (*p == '\t' || *p == ' ')) | 
 | 				*p++ = '\0'; | 
 | 			if(p >= e) | 
 | 				break; | 
 | 			argv[i++] = p; | 
 | 			if(i >= Maxarg) | 
 | 				error("newproc: too many arguments"); | 
 | 			while(p < e && *p != '\t' && *p != ' ') | 
 | 				p++; | 
 | 		} | 
 | 	} | 
 | 	argv[i] = 0; | 
 | 	r->op = OCONST; | 
 | 	r->type = TINT; | 
 | 	r->store.fmt = 'D'; | 
 | 	r->store.u.ival = nproc(argv); | 
 | } | 
 |  | 
 | void | 
 | step1(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("step1(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("step1(pid): arg type"); | 
 |  | 
 | 	msg(res.store.u.ival, "step"); | 
 | 	notes(res.store.u.ival); | 
 | 	dostop(res.store.u.ival); | 
 | } | 
 |  | 
 | void | 
 | startstop(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("startstop(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("startstop(pid): arg type"); | 
 |  | 
 | 	msg(res.store.u.ival, "startstop"); | 
 | 	notes(res.store.u.ival); | 
 | 	dostop(res.store.u.ival); | 
 | } | 
 |  | 
 | void | 
 | waitstop(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("waitstop(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("waitstop(pid): arg type"); | 
 |  | 
 | 	Bflush(bout); | 
 | 	msg(res.store.u.ival, "waitstop"); | 
 | 	notes(res.store.u.ival); | 
 | 	dostop(res.store.u.ival); | 
 | } | 
 |  | 
 | void | 
 | waitsyscall(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("waitsyscall(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("waitsycall(pid): arg type"); | 
 |  | 
 | 	Bflush(bout); | 
 | 	msg(res.store.u.ival, "sysstop"); | 
 | 	notes(res.store.u.ival); | 
 | 	dostop(res.store.u.ival); | 
 | } | 
 |  | 
 | void | 
 | start(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("start(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("start(pid): arg type"); | 
 |  | 
 | 	msg(res.store.u.ival, "start"); | 
 | } | 
 |  | 
 | void | 
 | stop(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("stop(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("stop(pid): arg type"); | 
 |  | 
 | 	Bflush(bout); | 
 | 	msg(res.store.u.ival, "stop"); | 
 | 	notes(res.store.u.ival); | 
 | 	dostop(res.store.u.ival); | 
 | } | 
 |  | 
 | void | 
 | xkill(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("kill(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("kill(pid): arg type"); | 
 |  | 
 | 	msg(res.store.u.ival, "kill"); | 
 | 	deinstall(res.store.u.ival); | 
 | } | 
 |  | 
 | void | 
 | xregister(Node *r, Node *args) | 
 | { | 
 | 	int tid; | 
 | 	Regdesc *rp; | 
 | 	Node res, resid; | 
 | 	Node *av[Maxarg]; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 | 	if(na != 1/* && na != 2 */) | 
 | 		error("register(name): arg count"); | 
 |  | 
 | 	expr(av[0], &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("register(name): arg type: name should be string"); | 
 | 	tid = 0; | 
 | 	if(na == 2){ | 
 | 		expr(av[1], &resid); | 
 | 		if(resid.type != TINT) | 
 | 			error("register(name[, threadid]): arg type: threadid should be int"); | 
 | 		tid = resid.store.u.ival; | 
 | 	} | 
 | 	if((rp = regdesc(res.store.u.string->string)) == nil) | 
 | 		error("no such register"); | 
 | 	r->op = OCONST; | 
 | 	r->type = TREG; | 
 | 	r->store.fmt = rp->format; | 
 | 	r->store.u.reg.name = rp->name; | 
 | 	r->store.u.reg.thread = tid; | 
 | } | 
 |  | 
 | void | 
 | refconst(Node *r, Node *args) | 
 | { | 
 | 	Node *n; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("refconst(expr): arg count"); | 
 |  | 
 | 	n = an(OCONST, ZN, ZN); | 
 | 	expr(args, n); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TCON; | 
 | 	r->store.u.con = n; | 
 | } | 
 |  | 
 | void | 
 | dolook(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	Lsym *l; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("var(string): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("var(string): arg type"); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	if((l = look(res.store.u.string->string)) == nil || l->v->set == 0){ | 
 | 		r->type = TLIST; | 
 | 		r->store.u.l = nil; | 
 | 	}else{ | 
 | 		r->type = l->v->type; | 
 | 		r->store = l->v->store; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | status(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	char *p; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("status(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("status(pid): arg type"); | 
 |  | 
 | 	p = getstatus(res.store.u.ival); | 
 | 	r->store.u.string = strnode(p); | 
 | 	r->op = OCONST; | 
 | 	r->store.fmt = 's'; | 
 | 	r->type = TSTRING; | 
 | } | 
 |  | 
 | void | 
 | reason(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("reason(cause): no cause"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("reason(cause): arg type"); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TSTRING; | 
 | 	r->store.fmt = 's'; | 
 | 	r->store.u.string = strnode((*mach->exc)(cormap, acidregs)); | 
 | } | 
 |  | 
 | void | 
 | follow(Node *r, Node *args) | 
 | { | 
 | 	int n, i; | 
 | 	Node res; | 
 | 	u64int f[10]; | 
 | 	List **tail, *l; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("follow(addr): no addr"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("follow(addr): arg type"); | 
 |  | 
 | 	n = (*mach->foll)(cormap, acidregs, res.store.u.ival, f); | 
 | 	if (n < 0) | 
 | 		error("follow(addr): %r"); | 
 | 	tail = &r->store.u.l; | 
 | 	for(i = 0; i < n; i++) { | 
 | 		l = al(TINT); | 
 | 		l->store.u.ival = f[i]; | 
 | 		l->store.fmt = 'X'; | 
 | 		*tail = l; | 
 | 		tail = &l->next; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | funcbound(Node *r, Node *args) | 
 | { | 
 | 	int n; | 
 | 	Node res; | 
 | 	u64int bounds[2]; | 
 | 	List *l; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("fnbound(addr): no addr"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("fnbound(addr): arg type"); | 
 |  | 
 | 	n = fnbound(res.store.u.ival, bounds); | 
 | 	if (n >= 0) { | 
 | 		r->store.u.l = al(TINT); | 
 | 		l = r->store.u.l; | 
 | 		l->store.u.ival = bounds[0]; | 
 | 		l->store.fmt = 'X'; | 
 | 		l->next = al(TINT); | 
 | 		l = l->next; | 
 | 		l->store.u.ival = bounds[1]; | 
 | 		l->store.fmt = 'X'; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | setproc(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("setproc(pid): no pid"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("setproc(pid): arg type"); | 
 |  | 
 | 	sproc(res.store.u.ival); | 
 | } | 
 |  | 
 | void | 
 | filepc(Node *r, Node *args) | 
 | { | 
 | 	int i; | 
 | 	Node res; | 
 | 	char *p, c; | 
 | 	u64int v; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("filepc(filename:line): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("filepc(filename:line): arg type"); | 
 |  | 
 | 	p = strchr(res.store.u.string->string, ':'); | 
 | 	if(p == 0) | 
 | 		error("filepc(filename:line): bad arg format"); | 
 |  | 
 | 	c = *p; | 
 | 	*p++ = '\0'; | 
 | 	i = file2pc(res.store.u.string->string, atoi(p), &v); | 
 | 	p[-1] = c; | 
 | 	if(i < 0) | 
 | 		error("filepc(filename:line): can't find address"); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TINT; | 
 | 	r->store.fmt = 'D'; | 
 | 	r->store.u.ival = v; | 
 | } | 
 |  | 
 | void | 
 | interpret(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	int isave; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("interpret(string): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("interpret(string): arg type"); | 
 |  | 
 | 	pushstr(&res); | 
 |  | 
 | 	isave = interactive; | 
 | 	interactive = 0; | 
 | 	r->store.u.ival = yyparse(); | 
 | 	interactive = isave; | 
 | 	popio(); | 
 | 	r->op = OCONST; | 
 | 	r->type = TINT; | 
 | 	r->store.fmt = 'D'; | 
 | } | 
 |  | 
 | void | 
 | include(Node *r, Node *args) | 
 | { | 
 | 	char *file, *libfile; | 
 | 	static char buf[1024]; | 
 | 	Node res; | 
 | 	int isave; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("include(string): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("include(string): arg type"); | 
 |  | 
 | 	Bflush(bout); | 
 |  | 
 | 	libfile = nil; | 
 | 	file = res.store.u.string->string; | 
 | 	if(access(file, AREAD) < 0 && file[0] != '/'){ | 
 | 		snprint(buf, sizeof buf, "#9/acid/%s", file); | 
 | 		libfile = unsharp(buf); | 
 | 		if(access(libfile, AREAD) >= 0){ | 
 | 			strecpy(buf, buf+sizeof buf, libfile); | 
 | 			file = buf; | 
 | 		} | 
 | 		free(libfile); | 
 | 	} | 
 |  | 
 | 	pushfile(file); | 
 | 	isave = interactive; | 
 | 	interactive = 0; | 
 | 	r->store.u.ival = yyparse(); | 
 | 	interactive = isave; | 
 | 	popio(); | 
 | 	r->op = OCONST; | 
 | 	r->type = TINT; | 
 | 	r->store.fmt = 'D'; | 
 | } | 
 |  | 
 | void | 
 | includepipe(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	int i, isave, pid, pip[2]; | 
 | 	char *argv[4]; | 
 | 	Waitmsg *w; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("includepipe(string): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("includepipe(string): arg type"); | 
 |  | 
 | 	Bflush(bout); | 
 |  | 
 | 	argv[0] = "rc"; | 
 | 	argv[1] = "-c"; | 
 | 	argv[2] = res.store.u.string->string; | 
 | 	argv[3] = 0; | 
 |  | 
 | 	if(pipe(pip) < 0) | 
 | 		error("pipe: %r"); | 
 | 		 | 
 | 	pid = fork(); | 
 | 	switch(pid) { | 
 | 	case -1: | 
 | 		close(pip[0]); | 
 | 		close(pip[1]); | 
 | 		error("fork: %r"); | 
 | 	case 0: | 
 | 		close(pip[0]); | 
 | 		close(0); | 
 | 		open("/dev/null", OREAD); | 
 | 		dup(pip[1], 1); | 
 | 		if(pip[1] > 1) | 
 | 			close(pip[1]); | 
 | 		for(i=3; i<100; i++) | 
 | 			close(i); | 
 | 		exec("rc", argv); | 
 | 		sysfatal("exec rc: %r"); | 
 | 	} | 
 |  | 
 | 	close(pip[1]); | 
 | 	pushfd(pip[0]); | 
 | 	 | 
 | 	isave = interactive; | 
 | 	interactive = 0; | 
 | 	r->store.u.ival = yyparse(); | 
 | 	interactive = isave; | 
 | 	popio(); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TINT; | 
 | 	r->store.fmt = 'D'; | 
 |  | 
 | 	w = waitfor(pid); | 
 | 	if(w->msg && w->msg[0]) | 
 | 		error("includepipe(\"%s\"): %s", argv[2], w->msg);	/* leaks w */ | 
 | 	free(w); | 
 | } | 
 |  | 
 | void | 
 | rc(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	int pid; | 
 | 	char *p, *q, *argv[4]; | 
 | 	Waitmsg *w; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("rc(string): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("rc(string): arg type"); | 
 |  | 
 | 	argv[0] = "rc"; | 
 | 	argv[1] = "-c"; | 
 | 	argv[2] = res.store.u.string->string; | 
 | 	argv[3] = 0; | 
 |  | 
 | 	pid = fork(); | 
 | 	switch(pid) { | 
 | 	case -1: | 
 | 		error("fork %r"); | 
 | 	case 0: | 
 | 		exec("rc", argv); | 
 | 		exits(0); | 
 | 	default: | 
 | 		w = waitfor(pid); | 
 | 		break; | 
 | 	} | 
 | 	p = w->msg; | 
 | 	q = strrchr(p, ':'); | 
 | 	if (q) | 
 | 		p = q+1; | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TSTRING; | 
 | 	r->store.u.string = strnode(p); | 
 | 	free(w); | 
 | 	r->store.fmt = 's'; | 
 | } | 
 |  | 
 | void | 
 | doerror(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	USED(r); | 
 | 	if(args == 0) | 
 | 		error("error(string): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("error(string): arg type"); | 
 |  | 
 | 	error(res.store.u.string->string); | 
 | } | 
 |  | 
 | void | 
 | doaccess(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("access(filename): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("access(filename): arg type"); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TINT; | 
 | 	r->store.fmt = 'D'; | 
 | 	r->store.u.ival = 0;		 | 
 | 	if(access(res.store.u.string->string, 4) == 0) | 
 | 		r->store.u.ival = 1; | 
 | } | 
 |  | 
 | void | 
 | readfile(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	int n, fd; | 
 | 	char *buf; | 
 | 	Dir *db; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("readfile(filename): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("readfile(filename): arg type"); | 
 |  | 
 | 	fd = open(res.store.u.string->string, OREAD); | 
 | 	if(fd < 0) | 
 | 		return; | 
 |  | 
 | 	db = dirfstat(fd); | 
 | 	if(db == nil || db->length == 0) | 
 | 		n = 8192; | 
 | 	else | 
 | 		n = db->length; | 
 | 	free(db); | 
 |  | 
 | 	buf = malloc(n); | 
 | 	n = read(fd, buf, n); | 
 |  | 
 | 	if(n > 0) { | 
 | 		r->op = OCONST; | 
 | 		r->type = TSTRING; | 
 | 		r->store.u.string = strnodlen(buf, n); | 
 | 		r->store.fmt = 's'; | 
 | 	} | 
 | 	free(buf); | 
 | 	close(fd); | 
 | } | 
 |  | 
 | void | 
 | getfile(Node *r, Node *args) | 
 | { | 
 | 	int n; | 
 | 	char *p; | 
 | 	Node res; | 
 | 	String *s; | 
 | 	Biobuf *bp; | 
 | 	List **l, *new; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("file(filename): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("file(filename): arg type"); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TLIST; | 
 | 	r->store.u.l = 0; | 
 |  | 
 | 	p = res.store.u.string->string; | 
 | 	bp = Bopen(p, OREAD); | 
 | 	if(bp == 0) | 
 | 		return; | 
 |  | 
 | 	l = &r->store.u.l; | 
 | 	for(;;) { | 
 | 		p = Brdline(bp, '\n'); | 
 | 		n = Blinelen(bp); | 
 | 		if(p == 0) { | 
 | 			if(n == 0) | 
 | 				break; | 
 | 			s = strnodlen(0, n); | 
 | 			Bread(bp, s->string, n); | 
 | 		} | 
 | 		else | 
 | 			s = strnodlen(p, n-1); | 
 |  | 
 | 		new = al(TSTRING); | 
 | 		new->store.u.string = s; | 
 | 		new->store.fmt = 's'; | 
 | 		*l = new; | 
 | 		l = &new->next; | 
 | 	} | 
 | 	Bterm(bp); | 
 | } | 
 |  | 
 | void | 
 | cvtatof(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("atof(string): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("atof(string): arg type"); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TFLOAT; | 
 | 	r->store.u.fval = atof(res.store.u.string->string); | 
 | 	r->store.fmt = 'f'; | 
 | } | 
 |  | 
 | void | 
 | cvtatoi(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("atoi(string): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("atoi(string): arg type"); | 
 |  | 
 | 	r->op = OCONST; | 
 | 	r->type = TINT; | 
 | 	r->store.u.ival = strtoull(res.store.u.string->string, 0, 0); | 
 | 	r->store.fmt = 'D'; | 
 | } | 
 |  | 
 | void | 
 | cvtitoa(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	Node *av[Maxarg]; | 
 | 	s64int ival; | 
 | 	char buf[128], *fmt; | 
 |  | 
 | 	if(args == 0) | 
 | err: | 
 | 		error("itoa(number [, printformat]): arg count"); | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 | 	if(na == 0 || na > 2) | 
 | 		goto err; | 
 | 	expr(av[0], &res); | 
 | 	if(res.type != TINT) | 
 | 		error("itoa(integer): arg type"); | 
 | 	ival = (int)res.store.u.ival; | 
 | 	fmt = "%d"; | 
 | 	if(na == 2){ | 
 | 		expr(av[1], &res); | 
 | 		if(res.type != TSTRING) | 
 | 			error("itoa(integer, string): arg type"); | 
 | 		fmt = res.store.u.string->string; | 
 | 	} | 
 |  | 
 | 	sprint(buf, fmt, ival); | 
 | 	r->op = OCONST; | 
 | 	r->type = TSTRING; | 
 | 	r->store.u.string = strnode(buf); | 
 | 	r->store.fmt = 's'; | 
 | } | 
 |  | 
 | List* | 
 | mapent(Map *m) | 
 | { | 
 | 	int i; | 
 | 	List *l, *n, **t, *h; | 
 |  | 
 | 	h = 0; | 
 | 	t = &h; | 
 | 	for(i = 0; i < m->nseg; i++) { | 
 | 		l = al(TSTRING); | 
 | 		n = al(TLIST); | 
 | 		n->store.u.l = l; | 
 | 		*t = n; | 
 | 		t = &n->next; | 
 | 		l->store.u.string = strnode(m->seg[i].name); | 
 | 		l->store.fmt = 's'; | 
 | 		l->next = al(TSTRING); | 
 | 		l = l->next; | 
 | 		l->store.u.string = strnode(m->seg[i].file ? m->seg[i].file : ""); | 
 | 		l->store.fmt = 's'; | 
 | 		l->next = al(TINT); | 
 | 		l = l->next; | 
 | 		l->store.u.ival = m->seg[i].base; | 
 | 		l->store.fmt = 'X'; | 
 | 		l->next = al(TINT); | 
 | 		l = l->next; | 
 | 		l->store.u.ival = m->seg[i].base + m->seg[i].size; | 
 | 		l->store.fmt = 'X'; | 
 | 		l->next = al(TINT); | 
 | 		l = l->next; | 
 | 		l->store.u.ival = m->seg[i].offset; | 
 | 		l->store.fmt = 'X'; | 
 | 	} | 
 | 	return h; | 
 | } | 
 |  | 
 | void | 
 | map(Node *r, Node *args) | 
 | { | 
 | 	int i; | 
 | 	Map *m; | 
 | 	List *l; | 
 | 	char *nam, *fil; | 
 | 	Node *av[Maxarg], res; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 |  | 
 | 	if(na != 0) { | 
 | 		expr(av[0], &res); | 
 | 		if(res.type != TLIST) | 
 | 			error("map(list): map needs a list"); | 
 | 		if(listlen(res.store.u.l) != 5) | 
 | 			error("map(list): list must have 5 entries"); | 
 |  | 
 | 		l = res.store.u.l; | 
 | 		if(l->type != TSTRING) | 
 | 			error("map name must be a string"); | 
 | 		nam = l->store.u.string->string; | 
 | 		l = l->next; | 
 | 		if(l->type != TSTRING) | 
 | 			error("map file must be a string"); | 
 | 		fil = l->store.u.string->string; | 
 | 		m = symmap; | 
 | 		i = findseg(m, nam, fil); | 
 | 		if(i < 0) { | 
 | 			m = cormap; | 
 | 			i = findseg(m, nam, fil); | 
 | 		} | 
 | 		if(i < 0) | 
 | 			error("%s %s is not a map entry", nam, fil);	 | 
 | 		l = l->next; | 
 | 		if(l->type != TINT) | 
 | 			error("map entry not int"); | 
 | 		m->seg[i].base = l->store.u.ival; | 
 | /* | 
 | 		if (strcmp(ent, "text") == 0) | 
 | 			textseg(l->store.u.ival, &fhdr); | 
 | */ | 
 | 		l = l->next; | 
 | 		if(l->type != TINT) | 
 | 			error("map entry not int"); | 
 | 		m->seg[i].size = l->store.u.ival - m->seg[i].base; | 
 | 		l = l->next; | 
 | 		if(l->type != TINT) | 
 | 			error("map entry not int"); | 
 | 		m->seg[i].offset = l->store.u.ival; | 
 | 	} | 
 |  | 
 | 	r->type = TLIST; | 
 | 	r->store.u.l = 0; | 
 | 	if(symmap) | 
 | 		r->store.u.l = mapent(symmap); | 
 | 	if(cormap) { | 
 | 		if(r->store.u.l == 0) | 
 | 			r->store.u.l = mapent(cormap); | 
 | 		else { | 
 | 			for(l = r->store.u.l; l->next; l = l->next) | 
 | 				; | 
 | 			l->next = mapent(cormap); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | void  | 
 | flatten(Node **av, Node *n) | 
 | { | 
 | 	if(n == 0) | 
 | 		return; | 
 |  | 
 | 	switch(n->op) { | 
 | 	case OLIST: | 
 | 		flatten(av, n->left); | 
 | 		flatten(av, n->right); | 
 | 		break; | 
 | 	default: | 
 | 		av[na++] = n; | 
 | 		if(na >= Maxarg) | 
 | 			error("too many function arguments"); | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | static struct | 
 | { | 
 | 	char *name; | 
 | 	u64int val; | 
 | } sregs[Maxarg/2]; | 
 | static int nsregs; | 
 |  | 
 | static int | 
 | straceregrw(Regs *regs, char *name, u64int *val, int isr) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	if(!isr){ | 
 | 		werrstr("saved registers cannot be written"); | 
 | 		return -1; | 
 | 	} | 
 | 	for(i=0; i<nsregs; i++) | 
 | 		if(strcmp(sregs[i].name, name) == 0){ | 
 | 			*val = sregs[i].val; | 
 | 			return 0; | 
 | 		} | 
 | 	return rget(acidregs, name, val); | 
 | } | 
 |  | 
 | void | 
 | strace(Node *r, Node *args) | 
 | { | 
 | 	Node *av[Maxarg], res; | 
 | 	List *l; | 
 | 	Regs regs; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 |  | 
 | 	if(na != 1) | 
 | 		error("strace(list): want one arg"); | 
 |  | 
 | 	expr(av[0], &res); | 
 | 	if(res.type != TLIST) | 
 | 		error("strace(list): strace needs a list"); | 
 | 	l = res.store.u.l; | 
 | 	if(listlen(l)%2) | 
 | 		error("strace(list): strace needs an even-length list"); | 
 | 	for(nsregs=0; l; nsregs++){ | 
 | 		if(l->type != TSTRING) | 
 | 			error("strace({r,v,r,v,...}): non-string name"); | 
 | 		sregs[nsregs].name = l->store.u.string->string; | 
 | 		if(regdesc(sregs[nsregs].name) == nil) | 
 | 			error("strace: bad register '%s'", sregs[nsregs].name); | 
 | 		l = l->next; | 
 |  | 
 | 		if(l == nil) | 
 | 			error("cannot happen in strace"); | 
 | 		if(l->type != TINT) | 
 | 			error("strace: non-int value for %s", sregs[nsregs].name); | 
 | 		sregs[nsregs].val = l->store.u.ival; | 
 | 		l = l->next; | 
 | 	} | 
 | 	regs.rw = straceregrw; | 
 | 	 | 
 | 	tracelist = 0; | 
 | 	if(stacktrace(cormap, ®s, trlist) <= 0) | 
 | 		error("no stack frame"); | 
 | 	r->type = TLIST; | 
 | 	r->store.u.l = tracelist; | 
 | } | 
 |  | 
 | void | 
 | regerror(char *msg) | 
 | { | 
 | 	error(msg); | 
 | } | 
 |  | 
 | void | 
 | regexp(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	Reprog *rp; | 
 | 	Node *av[Maxarg]; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 | 	if(na != 2) | 
 | 		error("regexp(pattern, string): arg count"); | 
 | 	expr(av[0], &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("regexp(pattern, string): pattern must be string"); | 
 | 	rp = regcomp(res.store.u.string->string); | 
 | 	if(rp == 0) | 
 | 		return; | 
 |  | 
 | 	expr(av[1], &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("regexp(pattern, string): bad string"); | 
 |  | 
 | 	r->store.fmt = 'D'; | 
 | 	r->type = TINT; | 
 | 	r->store.u.ival = regexec(rp, res.store.u.string->string, 0, 0); | 
 | 	free(rp); | 
 | } | 
 |  | 
 | char vfmt[] = "aBbcCdDfFgGiIoOqQrRsSuUVxXYZ"; | 
 |  | 
 | void | 
 | fmt(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	Node *av[Maxarg]; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 | 	if(na != 2) | 
 | 		error("fmt(obj, fmt): arg count"); | 
 | 	expr(av[1], &res); | 
 | 	if(res.type != TINT || strchr(vfmt, res.store.u.ival) == 0) | 
 | 		error("fmt(obj, fmt): bad format '%c'", (char)res.store.u.ival); | 
 | 	expr(av[0], r); | 
 | 	r->store.fmt = res.store.u.ival; | 
 | } | 
 |  | 
 | void | 
 | patom(char type, Store *res) | 
 | { | 
 | 	int i; | 
 | 	char buf[512]; | 
 | 	extern char *typenames[]; | 
 | 	Node *n; | 
 |  | 
 | 	switch(type){ | 
 | 	case TREG: | 
 | 		if(res->u.reg.thread) | 
 | 			Bprint(bout, "register(\"%s\", %#ux)", res->u.reg.name, res->u.reg.thread); | 
 | 		else | 
 | 			Bprint(bout, "register(\"%s\")", res->u.reg.name); | 
 | 		return; | 
 | 	case TCON: | 
 | 		Bprint(bout, "refconst("); | 
 | 		n = res->u.con; | 
 | 		patom(n->type, &n->store); | 
 | 		Bprint(bout, ")"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	switch(res->fmt){ | 
 | 	case 'c': | 
 | 	case 'C': | 
 | 	case 'r': | 
 | 	case 'B': | 
 | 	case 'b': | 
 | 	case 'X': | 
 | 	case 'x': | 
 | 	case 'W': | 
 | 	case 'D': | 
 | 	case 'd': | 
 | 	case 'u': | 
 | 	case 'U': | 
 | 	case 'Z': | 
 | 	case 'V': | 
 | 	case 'Y': | 
 | 	case 'o': | 
 | 	case 'O': | 
 | 	case 'q': | 
 | 	case 'Q': | 
 | 	case 'a': | 
 | 	case 'A': | 
 | 	case 'I': | 
 | 	case 'i': | 
 | 		if(type != TINT){ | 
 | 		badtype: | 
 | 			Bprint(bout, "*%s\\%c*", typenames[(uchar)type], res->fmt); | 
 | 			return; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	case 'f': | 
 | 	case 'F': | 
 | 		if(type != TFLOAT) | 
 | 			goto badtype; | 
 | 		break; | 
 |  | 
 | 	case 's': | 
 | 	case 'g': | 
 | 	case 'G': | 
 | 	case 'R': | 
 | 		if(type != TSTRING) | 
 | 			goto badtype; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	switch(res->fmt) { | 
 | 	case 'c': | 
 | 		Bprint(bout, "%c", (int)res->u.ival); | 
 | 		break; | 
 | 	case 'C': | 
 | 		if(res->u.ival < ' ' || res->u.ival >= 0x7f) | 
 | 			Bprint(bout, "%3d", (int)res->u.ival&0xff); | 
 | 		else | 
 | 			Bprint(bout, "%3c", (int)res->u.ival); | 
 | 		break; | 
 | 	case 'r': | 
 | 		Bprint(bout, "%C", (int)res->u.ival); | 
 | 		break; | 
 | 	case 'B': | 
 | 		memset(buf, '0', 34); | 
 | 		buf[1] = 'b'; | 
 | 		for(i = 0; i < 32; i++) { | 
 | 			if(res->u.ival & (1<<i)) | 
 | 				buf[33-i] = '1'; | 
 | 		} | 
 | 		buf[35] = '\0'; | 
 | 		Bprint(bout, "%s", buf); | 
 | 		break; | 
 | 	case 'b': | 
 | 		Bprint(bout, "%#.2x", (int)res->u.ival&0xff); | 
 | 		break; | 
 | 	case 'X': | 
 | 		Bprint(bout, "%#.8lux", (ulong)res->u.ival); | 
 | 		break; | 
 | 	case 'x': | 
 | 		Bprint(bout, "%#.4lux", (ulong)res->u.ival&0xffff); | 
 | 		break; | 
 | 	case 'W': | 
 | 		Bprint(bout, "%#.16llux", res->u.ival); | 
 | 		break; | 
 | 	case 'D': | 
 | 		Bprint(bout, "%d", (int)res->u.ival); | 
 | 		break; | 
 | 	case 'd': | 
 | 		Bprint(bout, "%d", (ushort)res->u.ival); | 
 | 		break; | 
 | 	case 'u': | 
 | 		Bprint(bout, "%d", (int)res->u.ival&0xffff); | 
 | 		break; | 
 | 	case 'U': | 
 | 		Bprint(bout, "%lud", (ulong)res->u.ival); | 
 | 		break; | 
 | 	case 'Z': | 
 | 		Bprint(bout, "%llud", res->u.ival); | 
 | 		break; | 
 | 	case 'V': | 
 | 		Bprint(bout, "%lld", res->u.ival); | 
 | 		break; | 
 | 	case 'Y': | 
 | 		Bprint(bout, "%#.16llux", res->u.ival); | 
 | 		break; | 
 | 	case 'o': | 
 | 		Bprint(bout, "%#.11uo", (int)res->u.ival&0xffff); | 
 | 		break; | 
 | 	case 'O': | 
 | 		Bprint(bout, "%#.6uo", (int)res->u.ival); | 
 | 		break; | 
 | 	case 'q': | 
 | 		Bprint(bout, "%#.11o", (short)(res->u.ival&0xffff)); | 
 | 		break; | 
 | 	case 'Q': | 
 | 		Bprint(bout, "%#.6o", (int)res->u.ival); | 
 | 		break; | 
 | 	case 'f': | 
 | 	case 'F': | 
 | 		Bprint(bout, "%g", res->u.fval); | 
 | 		break; | 
 | 	case 's': | 
 | 	case 'g': | 
 | 	case 'G': | 
 | 		Bwrite(bout, res->u.string->string, res->u.string->len); | 
 | 		break; | 
 | 	case 'R': | 
 | 		Bprint(bout, "%S", (Rune*)res->u.string->string); | 
 | 		break; | 
 | 	case 'a': | 
 | 	case 'A': | 
 | 		symoff(buf, sizeof(buf), res->u.ival, CANY); | 
 | 		Bprint(bout, "%s", buf); | 
 | 		break; | 
 | 	case 'I': | 
 | 	case 'i': | 
 | 		if (symmap == nil || (*mach->das)(symmap, res->u.ival, res->fmt, buf, sizeof(buf)) < 0) | 
 | 			Bprint(bout, "no instruction"); | 
 | 		else | 
 | 			Bprint(bout, "%s", buf); | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | blprint(List *l) | 
 | { | 
 | 	Store *res; | 
 |  | 
 | 	Bprint(bout, "{"); | 
 | 	while(l) { | 
 | 		switch(l->type) { | 
 | 		case TINT: | 
 | 			res = &l->store; | 
 | 			if(res->fmt == 'c'){ | 
 | 				Bprint(bout, "\'%c\'", (int)res->u.ival); | 
 | 				break; | 
 | 			}else if(res->fmt == 'r'){ | 
 | 				Bprint(bout, "\'%C\'", (int)res->u.ival); | 
 | 				break; | 
 | 			} | 
 | 			/* fall through */ | 
 | 		default: | 
 | 			patom(l->type, &l->store); | 
 | 			break; | 
 | 		case TSTRING: | 
 | 			Bputc(bout, '"'); | 
 | 			patom(l->type, &l->store); | 
 | 			Bputc(bout, '"'); | 
 | 			break; | 
 | 		case TLIST: | 
 | 			blprint(l->store.u.l); | 
 | 			break; | 
 | 		case TCODE: | 
 | 			pcode(l->store.u.cc, 0); | 
 | 			break; | 
 | 		} | 
 | 		l = l->next; | 
 | 		if(l) | 
 | 			Bprint(bout, ", "); | 
 | 	} | 
 | 	Bprint(bout, "}"); | 
 | } | 
 |  | 
 | int | 
 | comx(Node res) | 
 | { | 
 | 	Lsym *sl; | 
 | 	Node *n, xx; | 
 |  | 
 | 	if(res.store.fmt != 'a' && res.store.fmt != 'A') | 
 | 		return 0; | 
 |  | 
 | 	if(res.store.comt == 0 || res.store.comt->base == 0) | 
 | 		return 0; | 
 |  | 
 | 	sl = res.store.comt->base; | 
 | 	if(sl->proc) { | 
 | 		res.left = ZN; | 
 | 		res.right = ZN; | 
 | 		n = an(ONAME, ZN, ZN); | 
 | 		n->sym = sl; | 
 | 		n = an(OCALL, n, &res); | 
 | 			n->left->sym = sl; | 
 | 		expr(n, &xx); | 
 | 		return 1; | 
 | 	} | 
 | 	print("(%s)", sl->name); | 
 | 	return 0; | 
 | } | 
 |  | 
 | void | 
 | bprint(Node *r, Node *args) | 
 | { | 
 | 	int i, nas; | 
 | 	Node res, *av[Maxarg]; | 
 |  | 
 | 	USED(r); | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 | 	nas = na; | 
 | 	for(i = 0; i < nas; i++) { | 
 | 		expr(av[i], &res); | 
 | 		switch(res.type) { | 
 | 		default: | 
 | 			if(comx(res)) | 
 | 				break; | 
 | 			patom(res.type, &res.store); | 
 | 			break; | 
 | 		case TCODE: | 
 | 			pcode(res.store.u.cc, 0); | 
 | 			break; | 
 | 		case TLIST: | 
 | 			blprint(res.store.u.l); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	if(ret == 0) | 
 | 		Bputc(bout, '\n'); | 
 | } | 
 |  | 
 | void | 
 | printto(Node *r, Node *args) | 
 | { | 
 | 	int fd; | 
 | 	Biobuf *b; | 
 | 	int i, nas; | 
 | 	Node res, *av[Maxarg]; | 
 |  | 
 | 	USED(r); | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 | 	nas = na; | 
 |  | 
 | 	expr(av[0], &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("printto(string, ...): need string"); | 
 |  | 
 | 	fd = create(res.store.u.string->string, OWRITE, 0666); | 
 | 	if(fd < 0) | 
 | 		fd = open(res.store.u.string->string, OWRITE); | 
 | 	if(fd < 0) | 
 | 		error("printto: open %s: %r", res.store.u.string->string); | 
 |  | 
 | 	b = gmalloc(sizeof(Biobuf)); | 
 | 	Binit(b, fd, OWRITE); | 
 |  | 
 | 	Bflush(bout); | 
 | 	io[iop++] = bout; | 
 | 	bout = b; | 
 |  | 
 | 	for(i = 1; i < nas; i++) { | 
 | 		expr(av[i], &res); | 
 | 		switch(res.type) { | 
 | 		default: | 
 | 			if(comx(res)) | 
 | 				break; | 
 | 			patom(res.type, &res.store); | 
 | 			break; | 
 | 		case TLIST: | 
 | 			blprint(res.store.u.l); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	if(ret == 0) | 
 | 		Bputc(bout, '\n'); | 
 |  | 
 | 	Bterm(b); | 
 | 	close(fd); | 
 | 	free(b); | 
 | 	bout = io[--iop]; | 
 | } | 
 |  | 
 | void | 
 | pcfile(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	char *p, buf[128]; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("pcfile(addr): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("pcfile(addr): arg type"); | 
 |  | 
 | 	r->type = TSTRING; | 
 | 	r->store.fmt = 's'; | 
 | 	if(fileline(res.store.u.ival, buf, sizeof(buf)) < 0) { | 
 | 		r->store.u.string = strnode("?file?"); | 
 | 		return; | 
 | 	} | 
 | 	p = strrchr(buf, ':'); | 
 | 	if(p == 0) | 
 | 		error("pcfile(addr): funny file %s", buf); | 
 | 	*p = '\0'; | 
 | 	r->store.u.string = strnode(buf);	 | 
 | } | 
 |  | 
 | void | 
 | pcline(Node *r, Node *args) | 
 | { | 
 | 	Node res; | 
 | 	char *p, buf[128]; | 
 |  | 
 | 	if(args == 0) | 
 | 		error("pcline(addr): arg count"); | 
 | 	expr(args, &res); | 
 | 	if(res.type != TINT) | 
 | 		error("pcline(addr): arg type"); | 
 |  | 
 | 	r->type = TINT; | 
 | 	r->store.fmt = 'D'; | 
 | 	if(fileline(res.store.u.ival, buf, sizeof(buf)) < 0) { | 
 | 		r->store.u.ival = 0; | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	p = strrchr(buf, ':'); | 
 | 	if(p == 0) | 
 | 		error("pcline(addr): funny file %s", buf); | 
 | 	r->store.u.ival = atoi(p+1);	 | 
 | } | 
 |  | 
 | void | 
 | textfile(Node *r, Node *args) | 
 | { | 
 | 	char *file; | 
 | 	long base; | 
 | 	Fhdr *fp; | 
 | 	Node res, *av[Maxarg]; | 
 | 	List *l, *l2, **tail, *list, *tl; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 |  | 
 | 	if(na != 0) { | 
 | 		expr(av[0], &res); | 
 | 		if(res.type != TLIST) | 
 | 			error("textfile(list): textfile needs a list"); | 
 | 		if(listlen(res.store.u.l) != 2) | 
 | 			error("textfile(list): list must have 2 entries"); | 
 |  | 
 | 		l = res.store.u.l; | 
 | 		if(l->type != TSTRING) | 
 | 			error("textfile name must be a string"); | 
 | 		file = l->store.u.string->string; | 
 |  | 
 | 		l = l->next; | 
 | 		if(l->type != TINT) | 
 | 			error("textfile base must be an int"); | 
 | 		base = l->store.u.ival; | 
 |  | 
 | 		if((fp = crackhdr(file, OREAD)) == nil) | 
 | 			error("crackhdr %s: %r", file); | 
 | 		Bflush(bout); | 
 | 		fp->base = base; | 
 | 		fprint(2, "%s: %s %s %s\n", file, fp->aname, fp->mname, fp->fname); | 
 | 		if(mapfile(fp, base, symmap, nil) < 0) | 
 | 			fprint(2, "mapping %s: %r\n", file); | 
 | 		if(corhdr){ | 
 | 			unmapfile(corhdr, cormap); | 
 | 			mapfile(fp, base, cormap, nil); | 
 | 			free(correg); | 
 | 			correg = nil; | 
 | 			mapfile(corhdr, 0, cormap, &correg); | 
 | 		} | 
 | 		if(symopen(fp) < 0) | 
 | 			fprint(2, "symopen %s: %r\n", file); | 
 | 		else | 
 | 			addvarsym(fp); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	l2 = nil; | 
 | 	tail = &l2; | 
 | 	for(fp=fhdrlist; fp; fp=fp->next){ | 
 | 		if(fp->ftype == FCORE) | 
 | 			continue; | 
 | 		tl = al(TLIST); | 
 | 		*tail = tl; | 
 | 		tail = &tl->next; | 
 |  | 
 | 		list = al(TSTRING); | 
 | 		tl->store.u.l = list; | 
 | 		list->store.u.string = strnode(fp->filename); | 
 | 		list->store.fmt = 's'; | 
 | 		list->next = al(TINT); | 
 | 		list = list->next; | 
 | 		list->store.fmt = 'X'; | 
 | 		list->store.u.ival = fp->base; | 
 | 	} | 
 |  | 
 | 	r->type = TLIST; | 
 | 	r->store.u.l = l2; | 
 | } | 
 |  | 
 | void | 
 | deltextfile(Node *r, Node *args) | 
 | { | 
 | 	int did; | 
 | 	char *file; | 
 | 	Fhdr *fp, *fpnext; | 
 | 	Node res, *av[Maxarg]; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 |  | 
 | 	if(na != 1) | 
 | 		error("deltextfile(string): arg count"); | 
 |  | 
 | 	expr(av[0], &res); | 
 | 	if(res.type != TSTRING) | 
 | 		error("deltextfile(string): arg type"); | 
 | 	file = res.store.u.string->string; | 
 |  | 
 | 	did = 0; | 
 | 	for(fp=fhdrlist; fp; fp=fpnext){ | 
 | 		fpnext = fp->next; | 
 | 		if(fp->ftype == FCORE) | 
 | 			continue; | 
 | 		if(strcmp(file, fp->filename) == 0){ | 
 | 			did = 1; | 
 | 			if(fp == symhdr) | 
 | 				error("cannot remove symbols from main text file"); | 
 | 			unmapfile(fp, symmap); | 
 | 			uncrackhdr(fp); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	delvarsym(file); | 
 | 	if(!did) | 
 | 		error("symbol file %s not open", file); | 
 | } | 
 |  | 
 | void | 
 | stringn(Node *r, Node *args) | 
 | { | 
 | 	uint addr; | 
 | 	int i, n, ret; | 
 | 	Node res, *av[Maxarg]; | 
 | 	char *buf; | 
 |  | 
 | 	na = 0; | 
 | 	flatten(av, args); | 
 | 	if(na != 2) | 
 | 		error("stringn(addr, n): arg count"); | 
 |  | 
 | 	expr(av[0], &res); | 
 | 	if(res.type != TINT) | 
 | 		error("stringn(addr, n): arg type"); | 
 | 	addr = res.store.u.ival; | 
 |  | 
 | 	expr(av[1], &res); | 
 | 	if(res.type != TINT) | 
 | 		error("stringn(addr,n): arg type"); | 
 | 	n = res.store.u.ival; | 
 |  | 
 | 	buf = malloc(n+1); | 
 | 	if(buf == nil) | 
 | 		error("out of memory"); | 
 |  | 
 | 	r->type = TSTRING; | 
 | 	for(i=0; i<n; i++){ | 
 | 		ret = get1(cormap, addr, (uchar*)&buf[i], 1); | 
 | 		if(ret < 0){ | 
 | 			free(buf); | 
 | 			error("indir: %r"); | 
 | 		} | 
 | 		addr++; | 
 | 	} | 
 | 	buf[n] = 0; | 
 | 	r->store.u.string = strnode(buf); | 
 | 	free(buf); | 
 | } |