plan9port/src/cmd/acid/builtin.c
rsc 4f2ac1b76b Working on better handling of multithreading in general
and core dumps in particular.  See notes:

new types: register is something that when dereferenced gives you
	the registers.  the Ureg is no longer mapped at 0.
	refconst is something that gives a constant when dereferenced.

new builtin register("AX") creates register values
new builtin refconst(0x123) creates refconst values

new builtin var("foo") is equivalent to the variable foo
	(it returns foo but can also be used as the lhs of an assignment).

new acid function getregs() returns a list of the current values of registers.
new acid function setregs() sets the current registers to those values.
	note that getregs and setregs operate on register locations, not the
		register values themselves.
new acid function resetregs() sets registers to register("AX"), etc.
new acid function clearregs() sets all registers to constant -1.
the default register settings are as in resetregs(), not small numbers.

new acid variables coretext, pids, systype, corefile, cmdline.

new behavior: local variable lookup, stk, etc., use the acid values of registers
	(*PC, *SP, and so on), so the thread support code can change the context
	completely.

unary + is applicable to more data types and prints more often.
2005-01-23 22:48:19 +00:00

1630 lines
28 KiB
C

#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 sysstop(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*);
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,
"stop", stop,
"strace", strace,
"stringn", stringn,
"sysstop", sysstop,
"textfile", textfile,
"var", dolook,
"waitstop", waitstop,
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
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
sysstop(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, "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)
{
Regdesc *rp;
Node res;
if(args == 0)
error("register(string): arg count");
expr(args, &res);
if(res.type != TSTRING)
error("register(string): arg type");
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 = rp->name;
}
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;
ulong 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;
ulong 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;
ulong 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 = strtoul(res.store.u.string->string, 0, 0);
r->store.fmt = 'D';
}
void
cvtitoa(Node *r, Node *args)
{
Node res;
Node *av[Maxarg];
int 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;
ulong val;
} sregs[Maxarg/2];
static int nsregs;
static int
straceregrw(Regs *regs, char *name, ulong *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, &regs, 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:
Bprint(bout, "register(\"%s\")", res->u.reg);
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, "0%.11uo", (int)res->u.ival&0xffff);
break;
case 'O':
Bprint(bout, "0%.6uo", (int)res->u.ival);
break;
case 'q':
Bprint(bout, "0%.11o", (short)(res->u.ival&0xffff));
break;
case 'Q':
Bprint(bout, "0%.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);
}