mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
1644 lines
28 KiB
C
1644 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)
|
|
{
|
|
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;
|
|
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, ®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);
|
|
}
|