|  | #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); | 
|  | } |