#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[, threadid]): arg count");

	expr(av[0], &res);
	if(res.type != TSTRING)
		error("register(name[, threadid]): 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, &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:
		if(res->u.reg.thread)
			Bprint(bout, "register(\"%s\", 0x%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, "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);
}