acid
diff --git a/src/cmd/acid/Notes b/src/cmd/acid/Notes
new file mode 100644
index 0000000..597bf58
--- /dev/null
+++ b/src/cmd/acid/Notes
@@ -0,0 +1,49 @@
+Changes from the Plan 9 acid (beyond the 
+obvious changes necessary to compile without
+Ken's compiler and with the new libmach interface).
+========================================
+
+the input-line print verb is %Z, so that %L can be for locations
+
+the register block is explicitly "mapped" at 0 by xget1, xget2, ...
+	for compatibility with old acid, even though libmach
+	doesn't participate in this lie anymore.
+
+main accepts pid, file, and core in any order
+
+the ptab only records pids (no ctl fd anymore).
+
+new builtin sysstop(pid) runs pid until the next syscall
+
+map() returns 5-tuples: (name, filename, base, end, offset)
+	filename is new
+
+map() expects a 5-tuple too
+
+strace expects a list of register names and values like
+	{"PC", *PC, "SP", *SP}
+
+strace returns 5-tuples now:
+	{fn, pc, callerpc, paramlist, locallist}
+
+new builtin includepipe(cmd) includes the standard output
+	of running cmd.
+
+symbols returns 4-tuples now: {name, type, addr, file}
+
+new builtin textfile() returns the current set of open text files
+	as a list of {file, base} pairs.
+
+new builtin textfile({file, base}) adds a new file's symbols and text
+	offset by base for relocatables.
+
+new builtin deltextfile(file) removes a file from the set of open text files.
+
+both textfile and deltextfile update symbols.
+
+====
+
+yet to be done:
+
+elflink has the linking info for elf on linux
+
diff --git a/src/cmd/acid/acid.h b/src/cmd/acid/acid.h
new file mode 100644
index 0000000..729341b
--- /dev/null
+++ b/src/cmd/acid/acid.h
@@ -0,0 +1,313 @@
+/* acid.h */
+enum
+{
+	Eof		= -1,
+	Strsize		= 4096,
+	Hashsize	= 128,
+	Maxarg		= 512,
+	NFD		= 100,
+	Maxproc		= 50,
+	Maxval		= 10,
+	Mempergc	= 1024*1024,
+};
+
+/* #pragma varargck type "L"	void */
+
+typedef struct Node	Node;
+typedef struct String	String;
+typedef struct Lsym	Lsym;
+typedef struct List	List;
+typedef struct Store	Store;
+typedef struct Gc	Gc;
+typedef struct Strc	Strc;
+typedef struct Rplace	Rplace;
+typedef struct Ptab	Ptab;
+typedef struct Value	Value;
+typedef struct Type	Type;
+typedef struct Frtype	Frtype;
+
+Extern int	kernel;
+Extern int nlcount;
+Extern int	remote;
+Extern int	text;
+Extern int cor;
+Extern int	silent;
+Extern Fhdr	*fhdr;
+Extern Fhdr	*chdr;
+Extern int	line;
+Extern Biobuf*	bout;
+Extern Biobuf*	io[32];
+Extern int	iop;
+Extern int pid;
+Extern char	symbol[Strsize];
+Extern int	interactive;
+Extern Node*	code;
+Extern int	na;
+Extern int	wtflag;
+Extern Regs*	correg;
+Extern Map*	cormap;
+Extern Map*	symmap;
+Extern Lsym*	hash[Hashsize];
+Extern long	dogc;
+Extern Rplace*	ret;
+Extern char*	symfil;
+Extern char*	corfil;
+Extern int	gotint;
+Extern long	flen;
+Extern Gc*	gcl;
+Extern int	stacked;
+Extern jmp_buf	err;
+Extern Node*	prnt;
+Extern Node*	fomt;
+Extern List*	tracelist;
+Extern int	initialising;
+Extern int	quiet;
+Extern Fhdr*	corhdr;
+Extern Fhdr*	symhdr;
+
+extern void	(*expop[])(Node*, Node*);
+#define expr(n, r) (r)->store.comt=0; (*expop[(unsigned char)((n)->op)])(n, r);
+
+enum
+{
+	TINT,
+	TFLOAT,
+	TSTRING,
+	TLIST,
+	TCODE,
+};
+
+struct Type
+{
+	Type*	next;
+	int	offset;
+	char	fmt;
+	char	depth;
+	Lsym*	type;
+	Lsym*	tag;
+	Lsym*	base;
+};
+
+struct Frtype
+{
+	Lsym*	var;
+	Type*	type;
+	Frtype*	next;
+};
+
+struct Ptab
+{
+	int	pid;
+/*	int	ctl; */
+};
+Extern Ptab	ptab[Maxproc];
+
+struct Rplace
+{
+	jmp_buf	rlab;
+	Node*	stak;
+	Node*	val;
+	Lsym*	local;
+	Lsym**	tail;
+};
+
+struct Gc
+{
+	char	gcmark;
+	Gc*	gclink;
+};
+
+struct Store
+{
+	char	fmt;
+	Type*	comt;
+	union {
+		vlong	ival;
+		double	fval;
+		String*	string;
+		List*	l;
+		Node*	cc;
+	} u;
+};
+
+struct List
+{
+	Gc gc;
+	List*	next;
+	char	type;
+	Store store;
+};
+
+struct Value
+{
+	char	set;
+	char	type;
+	Store store;
+	Value*	pop;
+	Lsym*	scope;
+	Rplace*	ret;
+};
+
+struct Lsym
+{
+	char*	name;
+	int	lexval;
+	Lsym*	hash;
+	Value*	v;
+	Type*	lt;
+	Node*	proc;
+	Frtype*	local;
+	void	(*builtin)(Node*, Node*);
+};
+
+struct Node
+{
+	Gc gc;
+	char	op;
+	char	type;
+	Node*	left;
+	Node*	right;
+	Lsym*	sym;
+	int	builtin;
+	Store store;
+};
+#define ZN	(Node*)0
+
+struct String
+{
+	Gc gc;
+	char	*string;
+	int	len;
+};
+
+List*	addlist(List*, List*);
+void	addvarsym(Fhdr*);
+List*	al(int);
+Node*	an(int, Node*, Node*);
+void	append(Node*, Node*, Node*);
+int	bool(Node*);
+void	build(Node*);
+void	call(char*, Node*, Node*, Node*, Node*);
+void	catcher(void*, char*);
+void	checkqid(int, int);
+void	cmd(void);
+Node*	con(int);
+List*	construct(Node*);
+void	ctrace(int);
+void	decl(Node*);
+void	defcomplex(Node*, Node*);
+void	deinstall(int);
+void	delete(List*, int n, Node*);
+void	delvarsym(char*);
+void	dostop(int);
+Lsym*	enter(char*, int);
+void	error(char*, ...);
+void	execute(Node*);
+void	fatal(char*, ...);
+ulong	findframe(ulong);
+void	flatten(Node**, Node*);
+void	gc(void);
+char*	getstatus(int);
+void*	gmalloc(long);
+void	indir(Map*, ulong, char, Node*);
+void	installbuiltin(void);
+void	kinit(void);
+int	Zfmt(Fmt*);
+int	listcmp(List*, List*);
+int	listlen(List*);
+List*	listvar(char*, long);
+void	loadmodule(char*);
+void	loadvars(void);
+Lsym*	look(char*);
+void	ltag(char*);
+void	marklist(List*);
+Lsym*	mkvar(char*);
+void	msg(int, char*);
+void	notes(int);
+int	nproc(char**);
+void	nthelem(List*, int, Node*);
+int	numsym(char);
+void	odot(Node*, Node*);
+void	pcode(Node*, int);
+void	pexpr(Node*);
+int	popio(void);
+void	pstr(String*);
+void	pushfd(int);
+void	pushfile(char*);
+void	pushstr(Node*);
+ulong	raddr(char*);
+void	readtext(char*);
+void	readcore(void);
+void	restartio(void);
+String	*runenode(Rune*);
+int	scmp(String*, String*);
+void	sproc(int);
+String*	stradd(String*, String*);
+String*	strnode(char*);
+String*	strnodlen(char*, int);
+#define system acidsystem
+char*	system(void);
+int	trlist(Map*, Regs*, ulong, ulong, Symbol*, int);
+void	unwind(void);
+void	userinit(void);
+void	varreg(void);
+void	varsym(void);
+Waitmsg*	waitfor(int);
+void	whatis(Lsym*);
+void	windir(Map*, Node*, Node*, Node*);
+void	yyerror(char*, ...);
+int	yylex(void);
+int	yyparse(void);
+
+enum
+{
+	ONAME,
+	OCONST,
+	OMUL,
+	ODIV,
+	OMOD,
+	OADD,
+	OSUB,
+	ORSH,
+	OLSH,
+	OLT,
+	OGT,
+	OLEQ,
+	OGEQ,
+	OEQ,
+	ONEQ,
+	OLAND,
+	OXOR,
+	OLOR,
+	OCAND,
+	OCOR,
+	OASGN,
+	OINDM,
+	OEDEC,
+	OEINC,
+	OPINC,
+	OPDEC,
+	ONOT,
+	OIF,
+	ODO,
+	OLIST,
+	OCALL,
+	OCTRUCT,
+	OWHILE,
+	OELSE,
+	OHEAD,
+	OTAIL,
+	OAPPEND,
+	ORET,
+	OINDEX,
+	OINDC,
+	ODOT,
+	OLOCAL,
+	OFRAME,
+	OCOMPLEX,
+	ODELETE,
+	OCAST,
+	OFMT,
+	OEVAL,
+	OWHAT,
+};
diff --git a/src/cmd/acid/builtin.c b/src/cmd/acid/builtin.c
new file mode 100644
index 0000000..492fdfb
--- /dev/null
+++ b/src/cmd/acid/builtin.c
@@ -0,0 +1,1460 @@
+#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*);
+
+typedef struct Btab Btab;
+struct Btab
+{
+	char	*name;
+	void	(*fn)(Node*, Node*);
+} tab[] =
+{
+	"atof",		cvtatof,
+	"atoi",		cvtatoi,
+	"deltextfile",	deltextfile,
+	"error",	doerror,
+	"file",		getfile,
+	"readfile",	readfile,
+	"access",	doaccess,
+	"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,
+	"reason",	reason,
+	"regexp",	regexp,
+	"setproc",	setproc,
+	"start",	start,
+	"startstop",	startstop,
+	"status",	status,
+	"stop",		stop,
+	"strace",	strace,
+	"sysstop",		sysstop,
+	"textfile",	textfile,
+	"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
+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, correg));
+}
+
+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, correg, 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)
+{
+	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);
+	pushfile(res.store.u.string->string);
+
+	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.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(correg, 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[];
+
+	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':
+		if(type != TFLOAT)
+			Bprint(bout, "*%c<%s>*", res->fmt, typenames[(uchar)type]);
+		else
+			Bprint(bout, "%g", res->u.fval);
+		break;
+	case 's':
+	case 'g':
+	case 'G':
+		if(type != TSTRING)
+			Bprint(bout, "*%c<%s>*", res->fmt, typenames[(uchar)type]);
+		else
+			Bwrite(bout, res->u.string->string, res->u.string->len);
+		break;
+	case 'R':
+		if(type != TSTRING)
+			Bprint(bout, "*%c<%s>*", res->fmt, typenames[(uchar)type]);
+		else
+			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(type != TINT)
+			Bprint(bout, "*%c<%s>*", res->fmt, typenames[(uchar)type]);
+		else {
+			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);
+			mapfile(corhdr, 0, cormap, &correg);
+		}
+		if(syminit(fp) < 0)
+			fprint(2, "syminit %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);
+}
+
diff --git a/src/cmd/acid/dbg.y b/src/cmd/acid/dbg.y
new file mode 100644
index 0000000..afdf538
--- /dev/null
+++ b/src/cmd/acid/dbg.y
@@ -0,0 +1,413 @@
+%{
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+%}
+
+%union
+{
+	Node	*node;
+	Lsym	*sym;
+	ulong	ival;
+	float	fval;
+	String	*string;
+}
+
+%type <node> expr monexpr term stmnt name args zexpr slist
+%type <node> member members mname castexpr idlist
+%type <sym> zname
+
+%left	';'
+%right	'='
+%left	Tfmt
+%left	Toror
+%left	Tandand
+%left	'|'
+%left	'^'
+%left	'&'
+%left	Teq Tneq
+%left	'<' '>' Tleq Tgeq
+%left	Tlsh Trsh
+%left	'+' '-'
+%left	'*' '/' '%'
+%right	Tdec Tinc Tindir '.' '[' '('
+
+%token <sym>	Tid
+%token <ival>	Tconst Tfmt
+%token <fval>	Tfconst
+%token <string>	Tstring
+%token Tif Tdo Tthen Telse Twhile Tloop Thead Ttail Tappend Tfn Tret Tlocal
+%token Tcomplex Twhat Tdelete Teval Tbuiltin
+
+%%
+
+prog		: 
+		| prog bigstmnt
+		;
+
+bigstmnt	: stmnt
+		{
+			/* hold on to current command for gc */
+			mkvar("_thiscmd")->proc = $1;
+			execute($1);
+			gc();
+			if(interactive && nlcount){
+				Bprint(bout, "acid; ");
+				nlcount = 0;
+			}
+		}
+		| Tfn Tid '(' args ')' zsemi '{' slist '}'
+		{
+			$2->proc = an(OLIST, $4, $8);
+		}
+		| Tfn Tid
+		{
+			$2->proc = nil;
+		}
+		| Tcomplex name '{' members '}' ';'
+		{
+			defcomplex($2, $4);
+		}
+		;
+
+zsemi		:
+		| ';' zsemi
+
+members		: member
+		| members member
+		{
+			$$ = an(OLIST, $1, $2);
+		}
+		;
+
+mname		: Tid
+		{
+			$$ = an(ONAME, ZN, ZN);
+			$$->sym = $1;
+		}
+		;
+
+member		: Tconst Tconst mname ';'
+		{
+			$3->store.u.ival = $2;
+			$3->store.fmt = $1;
+			$$ = $3;
+		}
+		| Tconst mname Tconst mname ';'
+		{
+			$4->store.u.ival = $3;
+			$4->store.fmt = $1;
+			$4->right = $2;
+			$$ = $4;
+		}
+		| mname Tconst mname ';'
+		{
+			$3->store.u.ival = $2;
+			$3->left = $1;
+			$$ = $3;
+		}
+		| '{' members '}' ';'
+		{
+			$$ = an(OCTRUCT, $2, ZN);
+		}
+		;
+
+zname		: 
+		{ $$ = 0; }
+		| Tid
+		;
+
+slist		: stmnt
+		| slist stmnt
+		{
+			$$ = an(OLIST, $1, $2);
+		}
+		;
+
+stmnt		: zexpr ';'
+		| '{' slist '}'
+		{
+			$$ = $2;
+		}
+		| Tif expr Tthen stmnt
+		{
+			$$ = an(OIF, $2, $4);
+		}
+		| Tif expr Tthen stmnt Telse stmnt
+		{
+			$$ = an(OIF, $2, an(OELSE, $4, $6));
+		}
+		| Tloop expr ',' expr Tdo stmnt
+		{
+			$$ = an(ODO, an(OLIST, $2, $4), $6);
+		}
+		| Twhile expr Tdo stmnt
+		{
+			$$ = an(OWHILE, $2, $4);
+		}
+		| Tret expr ';'
+		{
+			$$ = an(ORET, $2, ZN);
+		}
+		| Tlocal idlist
+		{
+			$$ = an(OLOCAL, $2, ZN);
+		}
+		| Tcomplex Tid name ';'
+		{
+			$$ = an(OCOMPLEX, $3, ZN);
+			$$->sym = $2;
+		}
+		;
+
+idlist		: Tid
+		{
+			$$ = an(ONAME, ZN, ZN);
+			$$->sym = $1;
+		}
+		| idlist ',' Tid
+		{
+			$$ = an(ONAME, $1, ZN);
+			$$->sym = $3;
+		}
+		;
+
+zexpr		:
+		{ $$ = 0; }
+		| expr
+		;
+
+expr		: castexpr
+		| expr '*' expr
+		{
+			$$ = an(OMUL, $1, $3); 
+		}
+		| expr '/' expr
+		{
+			$$ = an(ODIV, $1, $3);
+		}
+		| expr '%' expr
+		{
+			$$ = an(OMOD, $1, $3);
+		}
+		| expr '+' expr
+		{
+			$$ = an(OADD, $1, $3);
+		}
+		| expr '-' expr
+		{
+			$$ = an(OSUB, $1, $3);
+		}
+		| expr Trsh expr
+		{
+			$$ = an(ORSH, $1, $3);
+		}
+		| expr Tlsh expr
+		{
+			$$ = an(OLSH, $1, $3);
+		}
+		| expr '<' expr
+		{
+			$$ = an(OLT, $1, $3);
+		}
+		| expr '>' expr
+		{
+			$$ = an(OGT, $1, $3);
+		}
+		| expr Tleq expr
+		{
+			$$ = an(OLEQ, $1, $3);
+		}
+		| expr Tgeq expr
+		{
+			$$ = an(OGEQ, $1, $3);
+		}
+		| expr Teq expr
+		{
+			$$ = an(OEQ, $1, $3);
+		}
+		| expr Tneq expr
+		{
+			$$ = an(ONEQ, $1, $3);
+		}
+		| expr '&' expr
+		{
+			$$ = an(OLAND, $1, $3);
+		}
+		| expr '^' expr
+		{
+			$$ = an(OXOR, $1, $3);
+		}
+		| expr '|' expr
+		{
+			$$ = an(OLOR, $1, $3);
+		}
+		| expr Tandand expr
+		{
+			$$ = an(OCAND, $1, $3);
+		}
+		| expr Toror expr
+		{
+			$$ = an(OCOR, $1, $3);
+		}
+		| expr '=' expr
+		{
+			$$ = an(OASGN, $1, $3);
+		}
+		| expr Tfmt
+		{
+			$$ = an(OFMT, $1, con($2));
+		}
+		;
+
+castexpr	: monexpr
+		| '(' Tid ')' monexpr
+		{
+			$$ = an(OCAST, $4, ZN);
+			$$->sym = $2;
+		}
+		;
+
+monexpr		: term
+		| '*' monexpr 
+		{
+			$$ = an(OINDM, $2, ZN);
+		}
+		| '@' monexpr 
+		{
+			$$ = an(OINDC, $2, ZN);
+		}
+		| '+' monexpr
+		{
+			$$ = con(0);
+			$$ = an(OADD, $2, $$);
+		}
+		| '-' monexpr
+		{
+			$$ = con(0);
+			$$ = an(OSUB, $$, $2);
+		}
+		| Tdec monexpr
+		{
+			$$ = an(OEDEC, $2, ZN);
+		}
+		| Tinc monexpr
+		{
+			$$ = an(OEINC, $2, ZN);
+		}
+		| Thead monexpr
+		{
+			$$ = an(OHEAD, $2, ZN);
+		}
+		| Ttail monexpr
+		{
+			$$ = an(OTAIL, $2, ZN);
+		}
+		| Tappend monexpr ',' monexpr
+		{
+			$$ = an(OAPPEND, $2, $4);
+		}
+		| Tdelete monexpr ',' monexpr
+		{
+			$$ = an(ODELETE, $2, $4);
+		}
+		| '!' monexpr
+		{
+			$$ = an(ONOT, $2, ZN);
+		}
+		| '~' monexpr
+		{
+			$$ = an(OXOR, $2, con(-1));
+		}
+		| Teval monexpr
+		{
+			$$ = an(OEVAL, $2, ZN);	
+		}
+		;
+
+term		: '(' expr ')'
+		{
+			$$ = $2;
+		}
+		| '{' args '}'
+		{
+			$$ = an(OCTRUCT, $2, ZN);
+		}
+		| term '[' expr ']'
+		{
+			$$ = an(OINDEX, $1, $3);
+		}
+		| term Tdec
+		{
+			$$ = an(OPDEC, $1, ZN);
+		}
+		| term '.' Tid
+		{
+			$$ = an(ODOT, $1, ZN);
+			$$->sym = $3;
+		}
+		| term Tindir Tid
+		{
+			$$ = an(ODOT, an(OINDM, $1, ZN), ZN);
+			$$->sym = $3;
+		}
+		| term Tinc
+		{
+			$$ = an(OPINC, $1, ZN);
+		}
+		| name '(' args ')'
+		{
+			$$ = an(OCALL, $1, $3);
+		}
+		| Tbuiltin name '(' args ')'
+		{
+			$$ = an(OCALL, $2, $4);
+			$$->builtin = 1;
+		}
+		| name
+		| Tconst
+		{
+			$$ = con($1);
+		}
+		| Tfconst
+		{
+			$$ = an(OCONST, ZN, ZN);
+			$$->type = TFLOAT;
+			$$->store.fmt = 'f';
+			$$->store.u.fval = $1;
+		}
+		| Tstring
+		{
+			$$ = an(OCONST, ZN, ZN);
+			$$->type = TSTRING;
+			$$->store.u.string = $1;
+			$$->store.fmt = 's';
+		}
+		| Twhat zname
+		{
+			$$ = an(OWHAT, ZN, ZN);
+			$$->sym = $2;
+		}
+		;
+
+name		: Tid
+		{
+			$$ = an(ONAME, ZN, ZN);
+			$$->sym = $1;
+		}
+		| Tid ':' name
+		{
+			$$ = an(OFRAME, $3, ZN);
+			$$->sym = $1;
+		}
+		;
+
+args		: zexpr
+		| args ','  zexpr
+		{
+			$$ = an(OLIST, $1, $3);
+		}
+		;
diff --git a/src/cmd/acid/dot.c b/src/cmd/acid/dot.c
new file mode 100644
index 0000000..07b8c19
--- /dev/null
+++ b/src/cmd/acid/dot.c
@@ -0,0 +1,153 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+
+Type*
+srch(Type *t, char *s)
+{
+	Type *f;
+
+	f = 0;
+	while(t) {
+		if(strcmp(t->tag->name, s) == 0) {
+			if(f == 0 || t->depth < f->depth)
+				f = t;
+		}
+		t = t->next;
+	}
+	return f;
+}
+
+void
+odot(Node *n, Node *r)
+{
+	char *s;
+	Type *t;
+	Node res;
+	ulong addr;
+
+	s = n->sym->name;
+	if(s == 0)
+		fatal("dodot: no tag");
+
+	expr(n->left, &res);
+	if(res.store.comt == 0)
+		error("no type specified for (expr).%s", s);
+
+	if(res.type != TINT)
+		error("pointer must be integer for (expr).%s", s);
+
+	t = srch(res.store.comt, s);
+	if(t == 0)
+		error("no tag for (expr).%s", s);
+
+	/* Propagate types */
+	if(t->type) 
+		r->store.comt = t->type->lt;
+	
+	addr = res.store.u.ival+t->offset;
+	if(t->fmt == 'a') {
+		r->op = OCONST;
+		r->store.fmt = 'a';
+		r->type = TINT;
+		r->store.u.ival = addr;
+	}
+	else 
+		indir(cormap, addr, t->fmt, r);
+
+}
+
+static Type **tail;
+static Lsym *base;
+
+void
+buildtype(Node *m, int d)
+{
+	Type *t;
+
+	if(m == ZN)
+		return;
+
+	switch(m->op) {
+	case OLIST:
+		buildtype(m->left, d);		
+		buildtype(m->right, d);
+		break;
+
+	case OCTRUCT:
+		buildtype(m->left, d+1);
+		break;
+	default:
+		t = malloc(sizeof(Type));
+		t->next = 0;
+		t->depth = d;
+		t->tag = m->sym;
+		t->base = base;
+		t->offset = m->store.u.ival;
+		if(m->left) {
+			t->type = m->left->sym;
+			t->fmt = 'a';			
+		}
+		else {
+			t->type = 0;
+			if(m->right)
+				t->type = m->right->sym;
+			t->fmt = m->store.fmt;
+		}
+
+		*tail = t;
+		tail = &t->next;
+	}			
+}
+
+void
+defcomplex(Node *tn, Node *m)
+{
+	tail = &tn->sym->lt;
+	base = tn->sym;
+	buildtype(m, 0);
+}
+
+void
+decl(Node *n)
+{
+	Node *l;
+	Value *v;
+	Frtype *f;
+	Lsym *type;
+
+	type = n->sym;
+	if(type->lt == 0)
+		error("%s is not a complex type", type->name);
+
+	l = n->left;
+	if(l->op == ONAME) {
+		v = l->sym->v;
+		v->store.comt = type->lt;
+		v->store.fmt = 'a';
+		return;
+	}
+
+	/*
+	 * Frame declaration
+	 */
+	for(f = l->sym->local; f; f = f->next) {
+		if(f->var == l->left->sym) {
+			f->type = n->sym->lt;
+			return;
+		}
+	}
+	f = malloc(sizeof(Frtype));
+	if(f == 0)
+		fatal("out of memory");
+
+	f->type = type->lt;
+
+	f->var = l->left->sym;
+	f->next = l->sym->local;
+	l->sym->local = f;
+}
diff --git a/src/cmd/acid/exec.c b/src/cmd/acid/exec.c
new file mode 100644
index 0000000..1d5b231
--- /dev/null
+++ b/src/cmd/acid/exec.c
@@ -0,0 +1,538 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+
+void
+error(char *fmt, ...)
+{
+	int i;
+	char buf[2048];
+	va_list arg;
+
+	/* Unstack io channels */
+	if(iop != 0) {
+		for(i = 1; i < iop; i++)
+			Bterm(io[i]);
+		bout = io[0];
+		iop = 0;
+	}
+
+	ret = 0;
+	gotint = 0;
+	Bflush(bout);
+	if(silent)
+		silent = 0;
+	else {
+		va_start(arg, fmt);
+		vseprint(buf, buf+sizeof(buf), fmt, arg);
+		va_end(arg);
+		fprint(2, "%Z: (error) %s\n", buf);
+	}
+	while(popio())
+		;
+	interactive = 1;
+	longjmp(err, 1);
+}
+
+void
+unwind(void)
+{
+	int i;
+	Lsym *s;
+	Value *v;
+
+	for(i = 0; i < Hashsize; i++) {
+		for(s = hash[i]; s; s = s->hash) {
+			while(s->v->pop) {
+				v = s->v->pop;
+				free(s->v);
+				s->v = v;
+			}
+		}
+	}
+}
+
+void
+execute(Node *n)
+{
+	Value *v;
+	Lsym *sl;
+	Node *l, *r;
+	int i, s, e;
+	Node res, xx;
+	static int stmnt;
+
+	gc();
+	if(gotint)
+		error("interrupted");
+
+	if(n == 0)
+		return;
+
+	if(stmnt++ > 5000) {
+		Bflush(bout);
+		stmnt = 0;
+	}
+
+	l = n->left;
+	r = n->right;
+
+	switch(n->op) {
+	default:
+		expr(n, &res);
+		if(ret || (res.type == TLIST && res.store.u.l == 0))
+			break;
+		prnt->right = &res;
+		expr(prnt, &xx);
+		break;
+	case OASGN:
+	case OCALL:
+		expr(n, &res);
+		break;
+	case OCOMPLEX:
+		decl(n);
+		break;
+	case OLOCAL:
+		for(n = n->left; n; n = n->left) {
+			if(ret == 0)
+				error("local not in function");
+			sl = n->sym;
+			if(sl->v->ret == ret)
+				error("%s declared twice", sl->name);
+			v = gmalloc(sizeof(Value));
+			v->ret = ret;
+			v->pop = sl->v;
+			sl->v = v;
+			v->scope = 0;
+			*(ret->tail) = sl;
+			ret->tail = &v->scope;
+			v->set = 0;
+		}
+		break;
+	case ORET:
+		if(ret == 0)
+			error("return not in function");
+		expr(n->left, ret->val);
+		longjmp(ret->rlab, 1);
+	case OLIST:
+		execute(n->left);
+		execute(n->right);
+		break;
+	case OIF:
+		expr(l, &res);
+		if(r && r->op == OELSE) {
+			if(bool(&res))
+				execute(r->left);
+			else
+				execute(r->right);
+		}
+		else if(bool(&res))
+			execute(r);
+		break;
+	case OWHILE:
+		for(;;) {
+			expr(l, &res);
+			if(!bool(&res))
+				break;
+			execute(r);
+		}
+		break;
+	case ODO:
+		expr(l->left, &res);
+		if(res.type != TINT)
+			error("loop must have integer start");
+		s = res.store.u.ival;
+		expr(l->right, &res);
+		if(res.type != TINT)
+			error("loop must have integer end");
+		e = res.store.u.ival;
+		for(i = s; i <= e; i++)
+			execute(r);
+		break;
+	}
+}
+
+int
+bool(Node *n)
+{
+	int true = 0;
+
+	if(n->op != OCONST)
+		fatal("bool: not const");
+
+	switch(n->type) {
+	case TINT:
+		if(n->store.u.ival != 0)
+			true = 1;
+		break;
+	case TFLOAT:
+		if(n->store.u.fval != 0.0)
+			true = 1;
+		break;
+	case TSTRING:
+		if(n->store.u.string->len)
+			true = 1;
+		break;
+	case TLIST:
+		if(n->store.u.l)
+			true = 1;
+		break;
+	}
+	return true;
+}
+
+void
+convflt(Node *r, char *flt)
+{
+	char c;
+
+	c = flt[0];
+	if(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) {
+		r->type = TSTRING;
+		r->store.fmt = 's';
+		r->store.u.string = strnode(flt);
+	}
+	else {
+		r->type = TFLOAT;
+		r->store.u.fval = atof(flt);
+	}
+}
+
+static char*
+regbyoff(ulong addr)
+{
+	Regdesc *r;
+
+	if(mach == nil)
+		error("no mach, no registers");
+	for(r=mach->reglist; r->name; r++)
+		if(r->offset == addr)
+			return r->name;
+	error("no register at %#lux", addr);
+	return nil;
+}
+
+int
+xget1(Map *m, ulong addr, u8int *a, int n)
+{
+	if(addr < 0x100)
+		return lget1(m, correg, locreg(regbyoff(addr)), a, n);
+	else
+		return get1(m, addr, a, n);
+}
+
+int
+xget2(Map *m, ulong addr, u16int *a)
+{
+	if(addr < 0x100)
+		return lget2(m, correg, locreg(regbyoff(addr)), a);
+	else
+		return get2(m, addr, a);
+}
+
+int
+xget4(Map *m, ulong addr, u32int *a)
+{
+	if(addr < 0x100)
+		return lget4(m, correg, locreg(regbyoff(addr)), a);
+	else
+		return get4(m, addr, a);
+}
+
+int
+xget8(Map *m, ulong addr, u64int *a)
+{
+	if(addr < 0x100)
+		return lget8(m, correg, locreg(regbyoff(addr)), a);
+	else
+		return get8(m, addr, a);
+}
+
+void
+indir(Map *m, ulong addr, char fmt, Node *r)
+{
+	int i;
+	u32int ival;
+	u64int vval;
+	int ret;
+	u8int cval;
+	u16int sval;
+	char buf[512], reg[12];
+
+	r->op = OCONST;
+	r->store.fmt = fmt;
+	switch(fmt) {
+	default:
+		error("bad pointer format '%c' for *", fmt);
+	case 'c':
+	case 'C':
+	case 'b':
+		r->type = TINT;
+		ret = xget1(m, addr, &cval, 1);
+		if (ret < 0)
+			error("indir: %r");
+		r->store.u.ival = cval;
+		break;
+	case 'x':
+	case 'd':
+	case 'u':
+	case 'o':
+	case 'q':
+	case 'r':
+		r->type = TINT;
+		ret = xget2(m, addr, &sval);
+		if (ret < 0)
+			error("indir: %r");
+		r->store.u.ival = sval;
+		break;
+	case 'a':
+	case 'A':
+	case 'B':
+	case 'X':
+	case 'D':
+	case 'U':
+	case 'O':
+	case 'Q':
+		r->type = TINT;
+		ret = xget4(m, addr, &ival);
+		if (ret < 0)
+			error("indir: %r");
+		r->store.u.ival = ival;
+		break;
+	case 'V':
+	case 'W':
+	case 'Y':
+	case 'Z':
+		r->type = TINT;
+		ret = xget8(m, addr, &vval);
+		if (ret < 0)
+			error("indir: %r");
+		r->store.u.ival = vval;
+		break;
+	case 's':
+		r->type = TSTRING;
+		for(i = 0; i < sizeof(buf)-1; i++) {
+			ret = xget1(m, addr, (uchar*)&buf[i], 1);
+			if (ret < 0)
+				error("indir: %r");
+			addr++;
+			if(buf[i] == '\0')
+				break;
+		}
+		buf[i] = 0;
+		if(i == 0)
+			strcpy(buf, "(null)");
+		r->store.u.string = strnode(buf);
+		break;
+	case 'R':
+		r->type = TSTRING;
+		for(i = 0; i < sizeof(buf)-2; i += 2) {
+			ret = xget1(m, addr, (uchar*)&buf[i], 2);
+			if (ret < 0)
+				error("indir: %r");
+			addr += 2;
+			if(buf[i] == 0 && buf[i+1] == 0)
+				break;
+		}
+		buf[i++] = 0;
+		buf[i] = 0;
+		r->store.u.string = runenode((Rune*)buf);
+		break;
+	case 'i':
+	case 'I':
+		if ((*mach->das)(m, addr, fmt, buf, sizeof(buf)) < 0)
+			error("indir: %r");
+		r->type = TSTRING;
+		r->store.fmt = 's';
+		r->store.u.string = strnode(buf);
+		break;
+	case 'f':
+		ret = xget1(m, addr, (uchar*)buf, mach->szfloat);
+		if (ret < 0)
+			error("indir: %r");
+		mach->ftoa32(buf, sizeof(buf), (void*) buf);
+		convflt(r, buf);
+		break;
+	case 'g':
+		ret = xget1(m, addr, (uchar*)buf, mach->szfloat);
+		if (ret < 0)
+			error("indir: %r");
+		mach->ftoa32(buf, sizeof(buf), (void*) buf);
+		r->type = TSTRING;
+		r->store.u.string = strnode(buf);
+		break;
+	case 'F':
+		ret = xget1(m, addr, (uchar*)buf, mach->szdouble);
+		if (ret < 0)
+			error("indir: %r");
+		mach->ftoa64(buf, sizeof(buf), (void*) buf);
+		convflt(r, buf);
+		break;
+	case '3':	/* little endian ieee 80 with hole in bytes 8&9 */
+		ret = xget1(m, addr, (uchar*)reg, 10);
+		if (ret < 0)
+			error("indir: %r");
+		memmove(reg+10, reg+8, 2);	/* open hole */
+		memset(reg+8, 0, 2);		/* fill it */
+		leieeeftoa80(buf, sizeof(buf), reg);
+		convflt(r, buf);
+		break;
+	case '8':	/* big-endian ieee 80 */
+		ret = xget1(m, addr, (uchar*)reg, 10);
+		if (ret < 0)
+			error("indir: %r");
+		beieeeftoa80(buf, sizeof(buf), reg);
+		convflt(r, buf);
+		break;
+	case 'G':
+		ret = xget1(m, addr, (uchar*)buf, mach->szdouble);
+		if (ret < 0)
+			error("indir: %r");
+		mach->ftoa64(buf, sizeof(buf), (void*) buf);
+		r->type = TSTRING;
+		r->store.u.string = strnode(buf);
+		break;
+	}
+}
+
+void
+windir(Map *m, Node *addr, Node *rval, Node *r)
+{
+	uchar cval;
+	ushort sval;
+	Node res, aes;
+	int ret;
+
+	if(m == 0)
+		error("no map for */@=");
+
+	expr(rval, &res);
+	expr(addr, &aes);
+
+	if(aes.type != TINT)
+		error("bad type lhs of @/*");
+
+	if(m != cormap && wtflag == 0)
+		error("not in write mode");
+
+	r->type = res.type;
+	r->store.fmt = res.store.fmt;
+	r->store = res.store;
+
+	switch(res.store.fmt) {
+	default:
+		error("bad pointer format '%c' for */@=", res.store.fmt);
+	case 'c':
+	case 'C':
+	case 'b':
+		cval = res.store.u.ival;
+		ret = put1(m, aes.store.u.ival, &cval, 1);
+		break;
+	case 'r':
+	case 'x':
+	case 'd':
+	case 'u':
+	case 'o':
+		sval = res.store.u.ival;
+		ret = put2(m, aes.store.u.ival, sval);
+		r->store.u.ival = sval;
+		break;
+	case 'a':
+	case 'A':
+	case 'B':
+	case 'X':
+	case 'D':
+	case 'U':
+	case 'O':
+		ret = put4(m, aes.store.u.ival, res.store.u.ival);
+		break;
+	case 'V':
+	case 'W':
+	case 'Y':
+	case 'Z':
+		ret = put8(m, aes.store.u.ival, res.store.u.ival);
+		break;
+	case 's':
+	case 'R':
+		ret = put1(m, aes.store.u.ival, (uchar*)res.store.u.string->string, res.store.u.string->len);
+		break;
+	}
+	if (ret < 0)
+		error("windir: %r");
+}
+
+void
+call(char *fn, Node *parameters, Node *local, Node *body, Node *retexp)
+{
+	int np, i;
+	Rplace rlab;
+	Node *n, res;
+	Value *v, *f;
+	Lsym *s, *next;
+	Node *avp[Maxarg], *ava[Maxarg];
+
+	rlab.local = 0;
+
+	na = 0;
+	flatten(avp, parameters);
+	np = na;
+	na = 0;
+	flatten(ava, local);
+	if(np != na) {
+		if(np < na)
+			error("%s: too few arguments", fn);
+		error("%s: too many arguments", fn);
+	}
+
+	rlab.tail = &rlab.local;
+
+	ret = &rlab;
+	for(i = 0; i < np; i++) {
+		n = ava[i];
+		switch(n->op) {
+		default:
+			error("%s: %d formal not a name", fn, i);
+		case ONAME:
+			expr(avp[i], &res);
+			s = n->sym;
+			break;
+		case OINDM:
+			res.store.u.cc = avp[i];
+			res.type = TCODE;
+			res.store.comt = 0;
+			if(n->left->op != ONAME)
+				error("%s: %d formal not a name", fn, i);
+			s = n->left->sym;
+			break;
+		}
+		if(s->v->ret == ret)
+			error("%s already declared at this scope", s->name);
+
+		v = gmalloc(sizeof(Value));
+		v->ret = ret;
+		v->pop = s->v;
+		s->v = v;
+		v->scope = 0;
+		*(rlab.tail) = s;
+		rlab.tail = &v->scope;
+
+		v->store = res.store;
+		v->type = res.type;
+		v->set = 1;
+	}
+
+	ret->val = retexp;
+	if(setjmp(rlab.rlab) == 0)
+		execute(body);
+
+	for(s = rlab.local; s; s = next) {
+		f = s->v;
+		next = f->scope;
+		s->v = f->pop;
+		free(f);
+	}
+}
diff --git a/src/cmd/acid/expr.c b/src/cmd/acid/expr.c
new file mode 100644
index 0000000..46e522b
--- /dev/null
+++ b/src/cmd/acid/expr.c
@@ -0,0 +1,1018 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+
+static int fsize[] =
+{
+	['A'] 4,
+	['B'] 4,
+	['C'] 1,
+	['D'] 4,
+	['F'] 8,
+	['G'] 8,
+	['O'] 4,
+	['Q'] 4,
+	['R'] 4,
+	['S'] 4,
+	['U'] 4,
+	['V'] 8,
+	['X'] 4,
+	['Y'] 8,
+	['W'] 8,
+	['Z'] 8,
+	['a'] 4,
+	['b'] 1,
+	['c'] 1,
+	['d'] 2,
+	['f'] 4,
+	['g'] 4,
+	['o'] 2,
+	['q'] 2,
+	['r'] 2,
+	['s'] 4,
+	['u'] 2,
+	['x'] 2,
+};
+
+int
+fmtsize(Value *v)
+{
+	int ret;
+
+	switch(v->store.fmt) {
+	default:
+		return  fsize[(unsigned char)v->store.fmt];
+	case 'i':
+	case 'I':
+		if(v->type != TINT || mach == 0)
+			error("no size for i fmt pointer ++/--");
+		ret = (*mach->instsize)(symmap, v->store.u.ival);
+		if(ret < 0) {
+			ret = (*mach->instsize)(symmap, v->store.u.ival);
+			if(ret < 0)
+				error("%r");
+		}
+		return ret;
+	}
+}
+
+void
+chklval(Node *lp)
+{
+	if(lp->op != ONAME)
+		error("need l-value");
+}
+
+void
+olist(Node *n, Node *res)
+{
+	expr(n->left, res);
+	expr(n->right, res);
+}
+
+void
+oeval(Node *n, Node *res)
+{
+	expr(n->left, res);
+	if(res->type != TCODE)
+		error("bad type for eval");
+	expr(res->store.u.cc, res);
+}
+
+void
+ocast(Node *n, Node *res)
+{
+	if(n->sym->lt == 0)
+		error("%s is not a complex type", n->sym->name);
+
+	expr(n->left, res);
+	res->store.comt = n->sym->lt;
+	res->store.fmt = 'a';
+}
+
+void
+oindm(Node *n, Node *res)
+{
+	Map *m;
+	Node l;
+
+	m = cormap;
+	if(m == 0)
+		m = symmap;
+	expr(n->left, &l);
+	if(l.type != TINT)
+		error("bad type for *");
+	if(m == 0)
+		error("no map for *");
+	indir(m, l.store.u.ival, l.store.fmt, res);
+	res->store.comt = l.store.comt;
+}
+
+void
+oindc(Node *n, Node *res)
+{
+	Map *m;
+	Node l;
+
+	m = symmap;
+	if(m == 0)
+		m = cormap;
+	expr(n->left, &l);
+	if(l.type != TINT)
+		error("bad type for @");
+	if(m == 0)
+		error("no map for @");
+	indir(m, l.store.u.ival, l.store.fmt, res);
+	res->store.comt = l.store.comt;
+}
+
+void
+oframe(Node *n, Node *res)
+{
+	char *p;
+	Node *lp;
+	ulong ival;
+	Frtype *f;
+
+	p = n->sym->name;
+	while(*p && *p == '$')
+		p++;
+	lp = n->left;
+	if(localaddr(cormap, correg, p, lp->sym->name, &ival) < 0)
+		error("colon: %r");
+		
+	res->store.u.ival = ival;
+	res->op = OCONST;
+	res->store.fmt = 'X';
+	res->type = TINT;
+
+	/* Try and set comt */
+	for(f = n->sym->local; f; f = f->next) {
+		if(f->var == lp->sym) {
+			res->store.comt = f->type;
+			res->store.fmt = 'a';
+			break;
+		}
+	}
+}
+
+void
+oindex(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+
+	if(r.type != TINT)
+		error("bad type for []");
+
+	switch(l.type) {
+	default:
+		error("lhs[] has bad type");
+	case TINT:
+		indir(cormap, l.store.u.ival+(r.store.u.ival*fsize[(unsigned char)l.store.fmt]), l.store.fmt, res);
+		res->store.comt = l.store.comt;
+		res->store.fmt = l.store.fmt;
+		break;
+	case TLIST:
+		nthelem(l.store.u.l, r.store.u.ival, res);
+		break;
+	case TSTRING:
+		res->store.u.ival = 0;
+		if(r.store.u.ival >= 0 && r.store.u.ival < l.store.u.string->len) {
+			int xx8;	/* to get around bug in vc */
+			xx8 = r.store.u.ival;
+			res->store.u.ival = l.store.u.string->string[xx8];
+		}
+		res->op = OCONST;
+		res->type = TINT;
+		res->store.fmt = 'c';
+		break;
+	}
+}
+
+void
+oappend(Node *n, Node *res)
+{
+	Node r, l;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	if(l.type != TLIST)
+		error("must append to list");
+	append(res, &l, &r);
+}
+
+void
+odelete(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	if(l.type != TLIST)
+		error("must delete from list");
+	if(r.type != TINT)
+		error("delete index must be integer");
+
+	delete(l.store.u.l, r.store.u.ival, res);
+}
+
+void
+ohead(Node *n, Node *res)
+{
+	Node l;
+
+	expr(n->left, &l);
+	if(l.type != TLIST)
+		error("head needs list");
+	res->op = OCONST;
+	if(l.store.u.l) {
+		res->type = l.store.u.l->type;
+		res->store = l.store.u.l->store;
+	}
+	else {
+		res->type = TLIST;
+		res->store.u.l = 0;
+	}
+}
+
+void
+otail(Node *n, Node *res)
+{
+	Node l;
+
+	expr(n->left, &l);
+	if(l.type != TLIST)
+		error("tail needs list");
+	res->op = OCONST;
+	res->type = TLIST;
+	if(l.store.u.l)
+		res->store.u.l = l.store.u.l->next;
+	else
+		res->store.u.l = 0;
+}
+
+void
+oconst(Node *n, Node *res)
+{
+	res->op = OCONST;
+	res->type = n->type;
+	res->store = n->store;
+	res->store.comt = n->store.comt;
+}
+
+void
+oname(Node *n, Node *res)
+{
+	Value *v;
+
+	v = n->sym->v;
+	if(v->set == 0)
+		error("%s used but not set", n->sym->name);
+	res->op = OCONST;
+	res->type = v->type;
+	res->store = v->store;
+	res->store.comt = v->store.comt;
+}
+
+void
+octruct(Node *n, Node *res)
+{
+	res->op = OCONST;
+	res->type = TLIST;
+	res->store.u.l = construct(n->left);
+}
+
+void
+oasgn(Node *n, Node *res)
+{
+	Node *lp, r;
+	Value *v;
+
+	lp = n->left;
+	switch(lp->op) {
+	case OINDM:
+		windir(cormap, lp->left, n->right, res);
+		break;
+	case OINDC:
+		windir(symmap, lp->left, n->right, res);
+		break;
+	default:
+		chklval(lp);
+		v = lp->sym->v;
+		expr(n->right, &r);
+		v->set = 1;
+		v->type = r.type;
+		v->store = r.store;
+		res->op = OCONST;
+		res->type = v->type;
+		res->store = v->store;
+		res->store.comt = v->store.comt;
+	}
+}
+
+void
+oadd(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TFLOAT;
+	switch(l.type) {
+	default:
+		error("bad lhs type +");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->type = TINT;
+			res->store.u.ival = l.store.u.ival+r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.fval = l.store.u.ival+r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type +");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.fval = l.store.u.fval+r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.fval = l.store.u.fval+r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type +");
+		}
+		break;
+	case TSTRING:
+		if(r.type == TSTRING) {
+			res->type = TSTRING;
+			res->store.fmt = 's';
+			res->store.u.string = stradd(l.store.u.string, r.store.u.string); 
+			break;
+		}
+		error("bad rhs for +");
+	case TLIST:
+		res->type = TLIST;
+		switch(r.type) {
+		case TLIST:
+			res->store.u.l = addlist(l.store.u.l, r.store.u.l);
+			break;
+		default:
+			r.left = 0;
+			r.right = 0;
+			res->store.u.l = addlist(l.store.u.l, construct(&r));
+			break;
+		}
+	}
+}
+
+void
+osub(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TFLOAT;
+	switch(l.type) {
+	default:
+		error("bad lhs type -");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->type = TINT;
+			res->store.u.ival = l.store.u.ival-r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.fval = l.store.u.ival-r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type -");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.fval = l.store.u.fval-r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.fval = l.store.u.fval-r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type -");
+		}
+		break;
+	}
+}
+
+void
+omul(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TFLOAT;
+	switch(l.type) {
+	default:
+		error("bad lhs type *");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->type = TINT;
+			res->store.u.ival = l.store.u.ival*r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.fval = l.store.u.ival*r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type *");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.fval = l.store.u.fval*r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.fval = l.store.u.fval*r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type *");
+		}
+		break;
+	}
+}
+
+void
+odiv(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TFLOAT;
+	switch(l.type) {
+	default:
+		error("bad lhs type /");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->type = TINT;
+			if(r.store.u.ival == 0)
+				error("zero divide");
+			res->store.u.ival = l.store.u.ival/r.store.u.ival;
+			break;
+		case TFLOAT:
+			if(r.store.u.fval == 0)
+				error("zero divide");
+			res->store.u.fval = l.store.u.ival/r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type /");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.fval = l.store.u.fval/r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.fval = l.store.u.fval/r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type /");
+		}
+		break;
+	}
+}
+
+void
+omod(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type %");
+	res->store.u.ival = l.store.u.ival%r.store.u.ival;
+}
+
+void
+olsh(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type <<");
+	res->store.u.ival = l.store.u.ival<<r.store.u.ival;
+}
+
+void
+orsh(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type >>");
+	res->store.u.ival = (unsigned)l.store.u.ival>>r.store.u.ival;
+}
+
+void
+olt(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	switch(l.type) {
+	default:
+		error("bad lhs type <");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.ival < r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.ival < r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type <");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.fval < r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.fval < r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type <");
+		}
+		break;
+	}
+}
+
+void
+ogt(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = 'D';
+	res->op = OCONST;
+	res->type = TINT;
+	switch(l.type) {
+	default:
+		error("bad lhs type >");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.ival > r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.ival > r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type >");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.fval > r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.fval > r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type >");
+		}
+		break;
+	}
+}
+
+void
+oleq(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = 'D';
+	res->op = OCONST;
+	res->type = TINT;
+	switch(l.type) {
+	default:
+		error("bad expr type <=");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.ival <= r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.ival <= r.store.u.fval;
+			break;
+		default:
+			error("bad expr type <=");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.fval <= r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.fval <= r.store.u.fval;
+			break;
+		default:
+			error("bad expr type <=");
+		}
+		break;
+	}
+}
+
+void
+ogeq(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = 'D';
+	res->op = OCONST;
+	res->type = TINT;
+	switch(l.type) {
+	default:
+		error("bad lhs type >=");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.ival >= r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.ival >= r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type >=");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.fval >= r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.fval >= r.store.u.fval;
+			break;
+		default:
+			error("bad rhs type >=");
+		}
+		break;
+	}
+}
+
+void
+oeq(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = 'D';
+	res->op = OCONST;
+	res->type = TINT;
+	res->store.u.ival = 0;
+	switch(l.type) {
+	default:
+		break;
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.ival == r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.ival == r.store.u.fval;
+			break;
+		default:
+			break;
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->store.u.ival = l.store.u.fval == r.store.u.ival;
+			break;
+		case TFLOAT:
+			res->store.u.ival = l.store.u.fval == r.store.u.fval;
+			break;
+		default:
+			break;
+		}
+		break;
+	case TSTRING:
+		if(r.type == TSTRING) {
+			res->store.u.ival = scmp(r.store.u.string, l.store.u.string);
+			break;
+		}
+		break;
+	case TLIST:
+		if(r.type == TLIST) {
+			res->store.u.ival = listcmp(l.store.u.l, r.store.u.l);
+			break;
+		}
+		break;
+	}
+	if(n->op == ONEQ)
+		res->store.u.ival = !res->store.u.ival;
+}
+
+
+void
+oland(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type &");
+	res->store.u.ival = l.store.u.ival&r.store.u.ival;
+}
+
+void
+oxor(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type ^");
+	res->store.u.ival = l.store.u.ival^r.store.u.ival;
+}
+
+void
+olor(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type |");
+	res->store.u.ival = l.store.u.ival|r.store.u.ival;
+}
+
+void
+ocand(Node *n, Node *res)
+{
+	Node l, r;
+
+	res->store.fmt = l.store.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	res->store.u.ival = 0;
+	expr(n->left, &l);
+	if(bool(&l) == 0)
+		return;
+	expr(n->right, &r);
+	if(bool(&r) == 0)
+		return;
+	res->store.u.ival = 1;
+}
+
+void
+onot(Node *n, Node *res)
+{
+	Node l;
+
+	res->op = OCONST;
+	res->type = TINT;
+	res->store.u.ival = 0;
+	expr(n->left, &l);
+	if(bool(&l) == 0)
+		res->store.u.ival = 1;
+}
+
+void
+ocor(Node *n, Node *res)
+{
+	Node l, r;
+
+	res->op = OCONST;
+	res->type = TINT;
+	res->store.u.ival = 0;
+	expr(n->left, &l);
+	if(bool(&l)) {
+		res->store.u.ival = 1;
+		return;
+	}
+	expr(n->right, &r);
+	if(bool(&r)) {
+		res->store.u.ival = 1;
+		return;
+	}
+}
+
+void
+oeinc(Node *n, Node *res)
+{
+	Value *v;
+
+	chklval(n->left);
+	v = n->left->sym->v;
+	res->op = OCONST;
+	res->type = v->type;
+	switch(v->type) {
+	case TINT:
+		if(n->op == OEDEC)
+			v->store.u.ival -= fmtsize(v);
+		else
+			v->store.u.ival += fmtsize(v);
+		break;			
+	case TFLOAT:
+		if(n->op == OEDEC)
+			v->store.u.fval--;
+		else
+			v->store.u.fval++;
+		break;
+	default:
+		error("bad type for pre --/++");
+	}
+	res->store = v->store;
+}
+
+void
+opinc(Node *n, Node *res)
+{
+	Value *v;
+
+	chklval(n->left);
+	v = n->left->sym->v;
+	res->op = OCONST;
+	res->type = v->type;
+	res->store = v->store;
+	switch(v->type) {
+	case TINT:
+		if(n->op == OPDEC)
+			v->store.u.ival -= fmtsize(v);
+		else
+			v->store.u.ival += fmtsize(v);
+		break;			
+	case TFLOAT:
+		if(n->op == OPDEC)
+			v->store.u.fval--;
+		else
+			v->store.u.fval++;
+		break;
+	default:
+		error("bad type for post --/++");
+	}
+}
+
+void
+ocall(Node *n, Node *res)
+{
+	Lsym *s;
+	Rplace *rsav;
+
+	res->op = OCONST;		/* Default return value */
+	res->type = TLIST;
+	res->store.u.l = 0;
+
+	chklval(n->left);
+	s = n->left->sym;
+
+	if(n->builtin && !s->builtin){
+		error("no builtin %s", s->name);
+		return;
+	}
+	if(s->builtin && (n->builtin || s->proc == 0)) {
+		(*s->builtin)(res, n->right);
+		return;
+	}
+	if(s->proc == 0)
+		error("no function %s", s->name);
+
+	rsav = ret;
+	call(s->name, n->right, s->proc->left, s->proc->right, res);
+	ret = rsav;
+}
+
+void
+ofmt(Node *n, Node *res)
+{
+	expr(n->left, res);
+	res->store.fmt = n->right->store.u.ival;
+}
+
+void
+owhat(Node *n, Node *res)
+{
+	res->op = OCONST;		/* Default return value */
+	res->type = TLIST;
+	res->store.u.l = 0;
+	whatis(n->sym);
+}
+
+void (*expop[])(Node*, Node*) =
+{
+	[ONAME]		oname,
+	[OCONST]	oconst,
+	[OMUL]		omul,
+	[ODIV]		odiv,
+	[OMOD]		omod,
+	[OADD]		oadd,
+	[OSUB]		osub,
+	[ORSH]		orsh,
+	[OLSH]		olsh,
+	[OLT]		olt,
+	[OGT]		ogt,
+	[OLEQ]		oleq,
+	[OGEQ]		ogeq,
+	[OEQ]		oeq,
+	[ONEQ]		oeq,
+	[OLAND]		oland,
+	[OXOR]		oxor,
+	[OLOR]		olor,
+	[OCAND]		ocand,
+	[OCOR]		ocor,
+	[OASGN]		oasgn,
+	[OINDM]		oindm,
+	[OEDEC]		oeinc,
+	[OEINC]		oeinc,
+	[OPINC]		opinc,
+	[OPDEC]		opinc,
+	[ONOT]		onot,
+	[OIF]		0,
+	[ODO]		0,
+	[OLIST]		olist,
+	[OCALL]		ocall,
+	[OCTRUCT]	octruct,
+	[OWHILE]	0,
+	[OELSE]		0,
+	[OHEAD]		ohead,
+	[OTAIL]		otail,
+	[OAPPEND]	oappend,
+	[ORET]		0,
+	[OINDEX]	oindex,
+	[OINDC]		oindc,
+	[ODOT]		odot,
+	[OLOCAL]	0,
+	[OFRAME]	oframe,
+	[OCOMPLEX]	0,
+	[ODELETE]	odelete,
+	[OCAST]		ocast,
+	[OFMT]		ofmt,
+	[OEVAL]		oeval,
+	[OWHAT]		owhat,
+};
diff --git a/src/cmd/acid/lex.c b/src/cmd/acid/lex.c
new file mode 100644
index 0000000..40f6400
--- /dev/null
+++ b/src/cmd/acid/lex.c
@@ -0,0 +1,661 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+#include "y.tab.h"
+
+struct keywd
+{
+	char	*name;
+	int	terminal;
+}
+keywds[] =
+{
+	"do",		Tdo,
+	"if",		Tif,
+	"then",		Tthen,
+	"else",		Telse,
+	"while",	Twhile,
+	"loop",		Tloop,
+	"head",		Thead,
+	"tail",		Ttail,
+	"append",	Tappend,
+	"defn",		Tfn,
+	"return",	Tret,
+	"local",	Tlocal,
+	"aggr",		Tcomplex,
+	"union",	Tcomplex,
+	"adt",		Tcomplex,
+	"complex",	Tcomplex,
+	"delete",	Tdelete,
+	"whatis",	Twhat,
+	"eval",		Teval,
+	"builtin",	Tbuiltin,
+	0,		0
+};
+
+char cmap[256] =
+{
+	['0']	'\0'+1,
+	['n']	'\n'+1,
+	['r']	'\r'+1,
+	['t']	'\t'+1,
+	['b']	'\b'+1,
+	['f']	'\f'+1,
+	['a']	'\a'+1,
+	['v']	'\v'+1,
+	['\\']	'\\'+1,
+	['"']	'"'+1,
+};
+
+void
+kinit(void)
+{
+	int i;
+	
+	for(i = 0; keywds[i].name; i++) 
+		enter(keywds[i].name, keywds[i].terminal);
+}
+
+typedef struct IOstack IOstack;
+struct IOstack
+{
+	char	*name;
+	int	line;
+	char	*text;
+	char	*ip;
+	Biobuf	*fin;
+	IOstack	*prev;
+};
+IOstack *lexio;
+
+void
+setacidfile(void)
+{
+	char *name;
+	Lsym *l;
+
+	if(lexio)
+		name = lexio->name;
+	else
+		name = "";
+	l = mkvar("acidfile");
+	l->v->set = 1;
+	l->v->store.fmt = 's';
+	l->v->type = TSTRING;
+	l->v->store.u.string = strnode(name);
+}
+
+void
+pushfile(char *file)
+{
+	Biobuf *b;
+	IOstack *io;
+
+	if(file)
+		b = Bopen(file, OREAD);
+	else{
+		b = Bopen(unsharp("#d/0"), OREAD);
+		file = "<stdin>";
+	}
+
+	if(b == 0)
+		error("pushfile: %s: %r", file);
+
+	io = malloc(sizeof(IOstack));
+	if(io == 0)
+		fatal("no memory");
+	io->name = strdup(file);
+	if(io->name == 0)
+		fatal("no memory");
+	io->line = line;
+	line = 1;
+	io->text = 0;
+	io->fin = b;
+	io->prev = lexio;
+	lexio = io;
+	setacidfile();
+}
+
+void
+pushfd(int fd)
+{
+	pushfile("/dev/null");
+	close(lexio->fin->fid);
+	free(lexio->name);
+	lexio->name = smprint("<fd#d>", fd);
+	lexio->fin->fid = fd;
+}
+
+void
+pushstr(Node *s)
+{
+	IOstack *io;
+
+	io = malloc(sizeof(IOstack));
+	if(io == 0)
+		fatal("no memory");
+	io->line = line;
+	line = 1;
+	io->name = strdup("<string>");
+	if(io->name == 0)
+		fatal("no memory");
+	io->line = line;
+	line = 1;
+	io->text = strdup(s->store.u.string->string);
+	if(io->text == 0)
+		fatal("no memory");
+	io->ip = io->text;
+	io->fin = 0;
+	io->prev = lexio;
+	lexio = io;
+	setacidfile();
+}
+
+void
+restartio(void)
+{
+	Bflush(lexio->fin);
+	Binit(lexio->fin, 0, OREAD);
+}
+
+int
+popio(void)
+{
+	IOstack *s;
+
+	if(lexio == 0)
+		return 0;
+
+	if(lexio->prev == 0){
+		if(lexio->fin)
+			restartio();
+		return 0;
+	}
+
+	if(lexio->fin)
+		Bterm(lexio->fin);
+	else
+		free(lexio->text);
+	free(lexio->name);
+	line = lexio->line;
+	s = lexio;
+	lexio = s->prev;
+	free(s);
+	setacidfile();
+	return 1;
+}
+
+int
+Zfmt(Fmt *f)
+{
+	int i;
+	char buf[1024];
+	IOstack *e;
+
+	e = lexio;
+	if(e) {
+		i = sprint(buf, "%s:%d", e->name, line);
+		while(e->prev) {
+			e = e->prev;
+			if(initialising && e->prev == 0)
+				break;
+			i += sprint(buf+i, " [%s:%d]", e->name, e->line);
+		}
+	} else
+		sprint(buf, "no file:0");
+	fmtstrcpy(f, buf);
+	return 0;
+}
+
+void
+unlexc(int s)
+{
+	if(s == '\n')
+		line--;
+
+	if(lexio->fin)
+		Bungetc(lexio->fin);
+	else
+		lexio->ip--;
+}
+
+int
+lexc(void)
+{
+	int c;
+
+	if(lexio->fin) {
+		c = Bgetc(lexio->fin);
+		if(gotint)
+			error("interrupt");
+		return c;
+	}
+
+	c = *lexio->ip++;
+	if(c == 0)
+		return -1;
+	return c;
+}
+
+int
+escchar(char c)
+{
+	int n;
+	char buf[Strsize];
+
+	if(c >= '0' && c <= '9') {
+		n = 1;
+		buf[0] = c;
+		for(;;) {
+			c = lexc();
+			if(c == Eof)
+				error("%d: <eof> in escape sequence", line);
+			if(strchr("0123456789xX", c) == 0) {
+				unlexc(c);
+				break;
+			}
+			buf[n++] = c;
+		}
+		buf[n] = '\0';
+		return strtol(buf, 0, 0);
+	}
+
+	n = cmap[(unsigned char)c];
+	if(n == 0)
+		return c;
+	return n-1;
+}
+
+void
+eatstring(void)
+{
+	int esc, c, cnt;
+	char buf[Strsize];
+
+	esc = 0;
+	for(cnt = 0;;) {
+		c = lexc();
+		switch(c) {
+		case Eof:
+			error("%d: <eof> in string constant", line);
+
+		case '\n':
+			error("newline in string constant");
+			goto done;
+
+		case '\\':
+			if(esc)
+				goto Default;
+			esc = 1;
+			break;
+
+		case '"':
+			if(esc == 0)
+				goto done;
+
+			/* Fall through */
+		default:
+		Default:
+			if(esc) {
+				c = escchar(c);
+				esc = 0;
+			}
+			buf[cnt++] = c;
+			break;
+		}
+		if(cnt >= Strsize)
+			error("string token too long");
+	}
+done:
+	buf[cnt] = '\0';
+	yylval.string = strnode(buf);
+}
+
+void
+eatnl(void)
+{
+	int c;
+
+	line++;
+	for(;;) {
+		c = lexc();
+		if(c == Eof)
+			error("eof in comment");
+		if(c == '\n')
+			return;
+	}
+}
+
+int
+yylex(void)
+{
+	int c;
+	extern char vfmt[];
+
+loop:
+	Bflush(bout);
+	c = lexc();
+	switch(c) {
+	case Eof:
+		if(gotint) {
+			gotint = 0;
+			stacked = 0;
+			Bprint(bout, "\nacid; ");
+			goto loop;
+		}
+		return Eof;
+
+	case '"':
+		eatstring();
+		return Tstring;
+
+	case ' ':
+	case '\t':
+		goto loop;
+
+	case '\n':
+		line++;
+		if(interactive == 0)
+			goto loop;
+		if(stacked) {
+			print("\t");
+			goto loop;
+		}
+		nlcount++;
+		return ';';
+
+	case '.':
+		c = lexc();
+		unlexc(c);
+		if(isdigit(c))
+			return numsym('.');
+
+		return '.';
+ 
+	case '(':
+	case ')':
+	case '[':
+	case ']':
+	case ';':
+	case ':':
+	case ',':
+	case '~':
+	case '?':
+	case '*':
+	case '@':
+	case '^':
+	case '%':
+		return c;
+	case '{':
+		stacked++;
+		return c;
+	case '}':
+		stacked--;
+		return c;
+
+	case '\\':
+		c = lexc();
+		if(strchr(vfmt, c) == 0) {
+			unlexc(c);
+			return '\\';
+		}
+		yylval.ival = c;
+		return Tfmt;
+
+	case '!':
+		c = lexc();
+		if(c == '=')
+			return Tneq;
+		unlexc(c);
+		return '!';
+
+	case '+':
+		c = lexc();
+		if(c == '+')
+			return Tinc;
+		unlexc(c);
+		return '+';
+
+	case '/':
+		c = lexc();
+		if(c == '/') {
+			eatnl();
+			goto loop;
+		}
+		unlexc(c);
+		return '/';
+
+	case '\'':
+		c = lexc();
+		if(c == '\\')
+			yylval.ival = escchar(lexc());
+		else
+			yylval.ival = c;
+		c = lexc();
+		if(c != '\'') {
+			error("missing '");
+			unlexc(c);
+		}
+		return Tconst;
+
+	case '&':
+		c = lexc();
+		if(c == '&')
+			return Tandand;
+		unlexc(c);
+		return '&';
+
+	case '=':
+		c = lexc();
+		if(c == '=')
+			return Teq;
+		unlexc(c);
+		return '=';
+
+	case '|':
+		c = lexc();
+		if(c == '|')
+			return Toror;
+		unlexc(c);
+		return '|';
+
+	case '<':
+		c = lexc();
+		if(c == '=')
+			return Tleq;
+		if(c == '<')
+			return Tlsh;
+		unlexc(c);
+		return '<';
+
+	case '>':
+		c = lexc();
+		if(c == '=')
+			return Tgeq;
+		if(c == '>')
+			return Trsh;
+		unlexc(c);
+		return '>';
+
+	case '-':
+		c = lexc();
+
+		if(c == '>')
+			return Tindir;
+
+		if(c == '-')
+			return Tdec;
+		unlexc(c);
+		return '-';
+
+	default:
+		return numsym(c);
+	}
+}
+
+int
+numsym(char first)
+{
+	int c, isbin, isfloat, ishex;
+	char *sel, *p;
+	Lsym *s;
+
+	symbol[0] = first;
+	p = symbol;
+
+	ishex = 0;
+	isbin = 0;
+	isfloat = 0;
+	if(first == '.')
+		isfloat = 1;
+
+	if(isdigit(*p++) || isfloat) {
+		for(;;) {
+			c = lexc();
+			if(c < 0)
+				error("%d: <eof> eating symbols", line);
+
+			if(c == '\n')
+				line++;
+			sel = "01234567890.xb";
+			if(ishex)
+				sel = "01234567890abcdefABCDEF";
+			else if(isbin)
+				sel = "01";
+			else if(isfloat)
+				sel = "01234567890eE-+";
+
+			if(strchr(sel, c) == 0) {
+				unlexc(c);
+				break;
+			}
+			if(c == '.')
+				isfloat = 1;
+			if(!isbin && c == 'x')
+				ishex = 1;
+			if(!ishex && c == 'b')
+				isbin = 1;
+			*p++ = c;
+		}
+		*p = '\0';
+		if(isfloat) {
+			yylval.fval = atof(symbol);
+			return Tfconst;
+		}
+
+		if(isbin)
+			yylval.ival = strtoul(symbol+2, 0, 2);
+		else
+			yylval.ival = strtoul(symbol, 0, 0);
+		return Tconst;
+	}
+
+	for(;;) {
+		c = lexc();
+		if(c < 0)
+			error("%d <eof> eating symbols", line);
+		if(c == '\n')
+			line++;
+		if(c != '_' && c != '$' && c <= '~' && !isalnum(c)) {	/* checking against ~ lets UTF names through */
+			unlexc(c);
+			break;
+		}
+		*p++ = c;
+	}
+
+	*p = '\0';
+
+	s = look(symbol);
+	if(s == 0)
+		s = enter(symbol, Tid);
+
+	yylval.sym = s;
+	return s->lexval;
+}
+
+Lsym*
+enter(char *name, int t)
+{
+	Lsym *s;
+	ulong h;
+	char *p;
+	Value *v;
+
+	h = 0;
+	for(p = name; *p; p++)
+		h = h*3 + *p;
+	h %= Hashsize;
+
+	s = gmalloc(sizeof(Lsym));
+	memset(s, 0, sizeof(Lsym));
+	s->name = strdup(name);
+
+	s->hash = hash[h];
+	hash[h] = s;
+	s->lexval = t;
+
+	v = gmalloc(sizeof(Value));
+	s->v = v;
+
+	v->store.fmt = 'X';
+	v->type = TINT;
+	memset(v, 0, sizeof(Value));
+
+	return s;
+}
+
+void
+delsym(Lsym *s)
+{
+	char *q;
+	ulong h;
+	Lsym *p;
+
+	h = 0;
+	for(q = s->name; *q; q++)
+		h = h*3 + *q;
+	h %= Hashsize;
+	
+	if(hash[h] == s)
+		hash[h] = s->hash;
+	else{
+		for(p=hash[h]; p && p->hash != s; p=p->hash)
+			;
+		if(p)
+			p->hash = s->hash;
+	}
+	s->hash = nil;
+}
+
+Lsym*
+look(char *name)
+{
+	Lsym *s;
+	ulong h;
+	char *p;
+
+	h = 0;
+	for(p = name; *p; p++)
+		h = h*3 + *p;
+	h %= Hashsize;
+
+	for(s = hash[h]; s; s = s->hash)
+		if(strcmp(name, s->name) == 0)
+			return s;
+	return 0;
+}
+
+Lsym*
+mkvar(char *s)
+{
+	Lsym *l;
+
+	l = look(s);
+	if(l == 0)
+		l = enter(s, Tid);
+	return l;
+}
diff --git a/src/cmd/acid/list.c b/src/cmd/acid/list.c
new file mode 100644
index 0000000..76214b2
--- /dev/null
+++ b/src/cmd/acid/list.c
@@ -0,0 +1,270 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+
+static List **tail;
+
+List*
+construct(Node *l)
+{
+	List *lh, **save;
+
+	save = tail;
+	lh = 0;
+	tail = &lh;
+	build(l);
+	tail = save;
+
+	return lh;
+}
+
+int
+listlen(List *l)
+{
+	int len;
+
+	len = 0;
+	while(l) {
+		len++;
+		l = l->next;
+	}
+	return len;
+}
+
+void
+build(Node *n)
+{
+	List *l;
+	Node res;
+
+	if(n == 0)
+		return;
+
+	switch(n->op) {
+	case OLIST:
+		build(n->left);
+		build(n->right);
+		return;
+	default:
+		expr(n, &res);
+		l = al(res.type);
+		l->store = res.store;
+		*tail = l;
+		tail = &l->next;	
+	}
+}
+
+List*
+addlist(List *l, List *r)
+{
+	List *f;
+
+	if(l == 0)
+		return r;
+
+	for(f = l; f->next; f = f->next)
+		;
+	f->next = r;
+
+	return l;
+}
+
+void
+append(Node *r, Node *list, Node *val)
+{
+	List *l, *f;
+
+	l = al(val->type);
+	l->store = val->store;
+	l->next = 0;
+
+	r->op = OCONST;
+	r->type = TLIST;
+
+	if(list->store.u.l == 0) {
+		list->store.u.l = l;
+		r->store.u.l = l;
+		return;
+	}
+	for(f = list->store.u.l; f->next; f = f->next)
+		;
+	f->next = l;
+	r->store.u.l = list->store.u.l;
+}
+
+int
+listcmp(List *l, List *r)
+{
+	if(l == r)
+		return 1;
+
+	while(l) {
+		if(r == 0)
+			return 0;
+		if(l->type != r->type)
+			return 0;
+		switch(l->type) {
+		case TINT:
+			if(l->store.u.ival != r->store.u.ival)
+				return 0;
+			break;
+		case TFLOAT:
+			if(l->store.u.fval != r->store.u.fval)
+				return 0;
+			break;
+		case TSTRING:
+			if(scmp(l->store.u.string, r->store.u.string) == 0)
+				return 0;
+			break;
+		case TLIST:
+			if(listcmp(l->store.u.l, r->store.u.l) == 0)
+				return 0;
+			break;
+		}
+		l = l->next;
+		r = r->next;
+	}
+	if(l != r)
+		return 0;
+	return 1;
+}
+
+void
+nthelem(List *l, int n, Node *res)
+{
+	if(n < 0)
+		error("negative index in []");
+
+	while(l && n--)
+		l = l->next;
+
+	res->op = OCONST;
+	if(l == 0) {
+		res->type = TLIST;
+		res->store.u.l = 0;
+		return;
+	}
+	res->type = l->type;
+	res->store = l->store;
+}
+
+void
+delete(List *l, int n, Node *res)
+{
+	List **tl;
+
+	if(n < 0)
+		error("negative index in delete");
+
+	res->op = OCONST;
+	res->type = TLIST;
+	res->store.u.l = l;
+
+	for(tl = &res->store.u.l; l && n--; l = l->next)
+		tl = &l->next;
+
+	if(l == 0)
+		error("element beyond end of list");
+	*tl = l->next;
+}
+
+List*
+listvar(char *s, long v)
+{
+	List *l, *tl;
+
+	tl = al(TLIST);
+
+	l = al(TSTRING);
+	tl->store.u.l = l;
+	l->store.fmt = 's';
+	l->store.u.string = strnode(s);
+	l->next = al(TINT);
+	l = l->next;
+	l->store.fmt = 'X';
+	l->store.u.ival = v;
+
+	return tl;
+}
+
+static List*
+listlocals(Map *map, Regs *regs, Symbol *fn, int class)
+{
+	int i;
+	u32int val;
+	Symbol s;
+	List **tail, *l2;
+
+	l2 = 0;
+	tail = &l2;
+	if(fn == nil)
+		return l2;
+	for(i = 0; indexlsym(fn, i, &s)>=0; i++) {
+		if(s.class != class)
+			continue;
+		if(class == CAUTO && s.name[0] == '.')
+			continue;
+		if(lget4(map, regs, s.loc, &val) < 0)
+			continue;
+		*tail = listvar(s.name, val);
+		tail = &(*tail)->next;
+	}
+	return l2;
+}
+
+static List*
+listparams(Map *map, Regs *regs, Symbol *fn)
+{
+	return listlocals(map, regs, fn, CPARAM);
+}
+
+static List*
+listautos(Map *map, Regs *regs, Symbol *fn)
+{
+	return listlocals(map, regs, fn, CAUTO);
+}
+
+int
+trlist(Map *map, Regs *regs, ulong pc, ulong callerpc, Symbol *sym, int depth)
+{
+	List *q, *l;
+	static List **tail;
+
+	if (tracelist == 0)		/* first time */
+		tail = &tracelist;
+
+	q = al(TLIST);
+	*tail = q;
+	tail = &q->next;
+
+	l = al(TINT);			/* Function address */
+	q->store.u.l = l;
+	l->store.u.ival = sym ? sym->loc.addr : pc;
+	l->store.fmt = 'X';
+
+	l->next = al(TINT);		/* actual pc address */
+	l = l->next;
+	l->store.u.ival = pc;
+	l->store.fmt = 'X';
+
+	l->next = al(TINT);		/* called from address */
+	l = l->next;
+	l->store.u.ival = callerpc;
+	l->store.fmt = 'X';
+
+	l->next = al(TLIST);		/* make list of params */
+	l = l->next;
+	if(sym)
+		l->store.u.l = listparams(map, regs, sym);
+
+	l->next = al(TLIST);		/* make list of locals */
+	l = l->next;
+	if(sym)
+		l->store.u.l = listautos(map, regs, sym);
+
+	return depth<40;
+}
diff --git a/src/cmd/acid/main.c b/src/cmd/acid/main.c
new file mode 100644
index 0000000..dccff26
--- /dev/null
+++ b/src/cmd/acid/main.c
@@ -0,0 +1,630 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#define Extern
+#include "acid.h"
+#include "y.tab.h"
+
+extern int __ifmt(Fmt*);
+
+static Biobuf	bioout;
+static char*	lm[16];
+static int	nlm;
+static char*	mtype;
+
+static	int attachfiles(int, char**);
+int	xfmt(Fmt*);
+int	isnumeric(char*);
+void	die(void);
+void	setcore(Fhdr*);
+
+void
+usage(void)
+{
+	fprint(2, "usage: acid [-c core] [-l module] [-m machine] [-qrw] [-k] [pid] [file]\n");
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	Lsym *l;
+	Node *n;
+	char buf[128], *s;
+	int pid, i;
+
+	argv0 = argv[0];
+	pid = 0;
+	quiet = 1;
+
+	mtype = 0;
+	ARGBEGIN{
+	case 'A':
+		abort();
+		break;
+	case 'm':
+		mtype = ARGF();
+		break;
+	case 'w':
+		wtflag = 1;
+		break;
+	case 'l':
+		s = ARGF();
+		if(s == 0)
+			usage();
+		lm[nlm++] = s;
+		break;
+	case 'k':
+		kernel++;
+		break;
+	case 'q':
+		quiet = 0;
+		break;
+	case 'r':
+		pid = 1;
+		remote++;
+		kernel++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	fmtinstall('x', xfmt);
+	fmtinstall('Z', Zfmt);
+	fmtinstall('L', locfmt);
+	Binit(&bioout, 1, OWRITE);
+	bout = &bioout;
+
+	kinit();
+	initialising = 1;
+	pushfile(0);
+	loadvars();
+	installbuiltin();
+
+	if(mtype && machbyname(mtype) == 0)
+		print("unknown machine %s", mtype);
+
+	if (attachfiles(argc, argv) < 0)
+		varreg();		/* use default register set on error */
+	if(mach == nil)
+		mach = machcpu;
+
+	symhdr = nil;	/* not supposed to use this anymore */
+
+	l = mkvar("acid");
+	l->v->set = 1;
+	l->v->type = TLIST;
+	l->v->store.u.l = nil;
+
+	loadmodule("/usr/local/plan9/acid/port");
+	for(i = 0; i < nlm; i++) {
+		if(access(lm[i], AREAD) >= 0)
+			loadmodule(lm[i]);
+		else {
+			sprint(buf, "/usr/local/plan9/acid/%s", lm[i]);
+			loadmodule(buf);
+		}
+	}
+
+	userinit();
+	varsym();
+
+	l = look("acidmap");
+	if(l && l->proc) {
+		n = an(ONAME, ZN, ZN);
+		n->sym = l;
+		n = an(OCALL, n, ZN);
+		execute(n);
+	}
+
+	interactive = 1;
+	initialising = 0;
+	line = 1;
+
+	notify(catcher);
+
+	for(;;) {
+		if(setjmp(err)) {
+			Binit(&bioout, 1, OWRITE);
+			unwind();
+		}
+		stacked = 0;
+
+		Bprint(bout, "acid; ");
+
+		if(yyparse() != 1)
+			die();
+		restartio();
+
+		unwind();
+	}
+	Bputc(bout, '\n');
+	exits(0);
+}
+
+static int
+attachfiles(int argc, char **argv)
+{
+	char *s;
+	int i, omode;
+	Fhdr *hdr;
+	Lsym *l;
+	Value *v;
+
+	interactive = 0;
+	if(setjmp(err))
+		return -1;
+
+	/*
+	 * Unix and Plan 9 differ on what the right order of pid, text, and core is.
+	 * I never remember anyway.  Let's just accept them in any order.
+	 */
+	omode = wtflag ? ORDWR : OREAD;
+	for(i=0; i<argc; i++){
+		if(isnumeric(argv[i])){
+			if(pid){
+				fprint(2, "already have pid %d; ignoring pid %d\n", pid, argv[i]);
+				continue;
+			}
+			if(corhdr){
+				fprint(2, "already have core %s; ignoring pid %d\n", corfil, pid);
+				continue;
+			}
+			pid = atoi(argv[i]);
+			continue;
+		}
+		if((hdr = crackhdr(argv[i], omode)) == nil){
+			fprint(2, "crackhdr %s: %r\n", argv[i]);
+			continue;
+		}
+		fprint(2, "%s: %s %s %s\n", argv[i], hdr->aname, hdr->mname, hdr->fname);
+		if(hdr->ftype == FCORE){
+			if(pid){
+				fprint(2, "already have pid %d; ignoring core %s\n", pid, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			if(corhdr){
+				fprint(2, "already have core %s; ignoring core %s\n", corfil, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			corhdr = hdr;
+			corfil = argv[i];
+		}else{
+			if(symhdr){
+				fprint(2, "already have text %s; ignoring text %s\n", symfil, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			symhdr = hdr;
+			symfil = argv[i];
+		}
+	}
+
+	if(symhdr==nil){
+		symfil = "a.out";
+		if(pid){
+			if((s = proctextfile(pid)) != nil){
+				fprint(2, "pid %d: text %s\n", pid, s);
+				symfil = s;
+			}
+		}
+		/* XXX pull command from core */
+
+		if((hdr = crackhdr(symfil, omode)) == nil){
+			fprint(2, "crackhdr %s: %r\n", symfil);
+			symfil = nil;
+		}
+	}
+
+	if(symhdr)
+		syminit(symhdr);
+
+	if(!mach)
+		mach = machcpu;
+
+	/*
+	 * Set up maps.
+	 */
+	symmap = allocmap();
+	cormap = allocmap();
+	if(symmap == nil || cormap == nil)
+		sysfatal("allocating maps: %r");
+
+	if(symhdr){
+		if(mapfile(symhdr, 0, symmap, nil) < 0)
+			fprint(2, "mapping %s: %r\n", symfil);
+		mapfile(symhdr, 0, cormap, nil);
+	}
+
+	l = mkvar("objtype");
+	v = l->v;
+	v->store.fmt = 's';
+	v->set = 1;
+	v->store.u.string = strnode(mach->name);
+	v->type = TSTRING;
+
+	l = mkvar("textfile");
+	v = l->v;
+	v->store.fmt = 's';
+	v->set = 1;
+	v->store.u.string = strnode(symfil ? symfil : "");
+	v->type = TSTRING;
+
+	l = mkvar("systype");
+	v = l->v;
+	v->store.fmt = 's';
+	v->set = 1;
+	v->store.u.string = strnode(symhdr ? symhdr->aname : "");
+	v->type = TSTRING;
+
+	l = mkvar("corefile");
+	v = l->v;
+	v->store.fmt = 's';
+	v->set = 1;
+	v->store.u.string = strnode(corfil ? corfil : "");
+	v->type = TSTRING;
+
+	if(pid)
+		sproc(pid);
+	if(corhdr)
+		setcore(corhdr);
+	varreg();
+	return 0;
+}
+
+void
+setcore(Fhdr *hdr)
+{
+	unmapproc(cormap);
+	unmapfile(corhdr, cormap);
+	free(correg);
+	correg = nil;
+
+	if(hdr == nil)
+		error("no core");
+	if(mapfile(hdr, 0, cormap, &correg) < 0)
+		error("mapfile %s: %r", hdr->filename);
+	corhdr = hdr;
+	corfil = hdr->filename;
+}
+
+void
+die(void)
+{
+	Lsym *s;
+	List *f;
+
+	Bprint(bout, "\n");
+
+	s = look("proclist");
+	if(s && s->v->type == TLIST) {
+		for(f = s->v->store.u.l; f; f = f->next){
+			detachproc((int)f->store.u.ival);
+			Bprint(bout, "/bin/kill -9 %d\n", (int)f->store.u.ival);
+		}
+	}
+	exits(0);
+}
+
+void
+userinit(void)
+{
+	Lsym *l;
+	Node *n;
+	char buf[128], *p;
+
+	sprint(buf, "/usr/local/plan9/acid/%s", mach->name);
+	loadmodule(buf);
+	p = getenv("home");
+	if(p != 0) {
+		sprint(buf, "%s/lib/acid", p);
+		silent = 1;
+		loadmodule(buf);
+	}
+
+	interactive = 0;
+	if(setjmp(err)) {
+		unwind();
+		return;
+	}
+	l = look("acidinit");
+	if(l && l->proc) {
+		n = an(ONAME, ZN, ZN);
+		n->sym = l;
+		n = an(OCALL, n, ZN);
+		execute(n);
+	}
+}
+
+void
+loadmodule(char *s)
+{
+	interactive = 0;
+	if(setjmp(err)) {
+		unwind();
+		return;
+	}
+	pushfile(s);
+	silent = 0;
+	yyparse();
+	popio();
+	return;
+}
+
+Node*
+an(int op, Node *l, Node *r)
+{
+	Node *n;
+
+	n = gmalloc(sizeof(Node));
+	memset(n, 0, sizeof(Node));
+	n->gc.gclink = gcl;
+	gcl = (Gc*)n;
+	n->op = op;
+	n->left = l;
+	n->right = r;
+	return n;
+}
+
+List*
+al(int t)
+{
+	List *l;
+
+	l = gmalloc(sizeof(List));
+	memset(l, 0, sizeof(List));
+	l->type = t;
+	l->gc.gclink = gcl;
+	gcl = (Gc*)l;
+	return l;
+}
+
+Node*
+con(int v)
+{
+	Node *n;
+
+	n = an(OCONST, ZN, ZN);
+	n->store.u.ival = v;
+	n->store.fmt = 'X';
+	n->type = TINT;
+	return n;
+}
+
+void
+fatal(char *fmt, ...)
+{
+	char buf[128];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%s: %Z (fatal problem) %s\n", argv0, buf);
+	exits(buf);
+}
+
+void
+yyerror(char *fmt, ...)
+{
+	char buf[128];
+	va_list arg;
+
+	if(strcmp(fmt, "syntax error") == 0) {
+		yyerror("syntax error, near symbol '%s'", symbol);
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%Z: %s\n", buf);
+}
+
+void
+marktree(Node *n)
+{
+
+	if(n == 0)
+		return;
+
+	marktree(n->left);
+	marktree(n->right);
+
+	n->gc.gcmark = 1;
+	if(n->op != OCONST)
+		return;
+
+	switch(n->type) {
+	case TSTRING:
+		n->store.u.string->gc.gcmark = 1;
+		break;
+	case TLIST:
+		marklist(n->store.u.l);
+		break;
+	case TCODE:
+		marktree(n->store.u.cc);
+		break;
+	}
+}
+
+void
+marklist(List *l)
+{
+	while(l) {
+		l->gc.gcmark = 1;
+		switch(l->type) {
+		case TSTRING:
+			l->store.u.string->gc.gcmark = 1;
+			break;
+		case TLIST:
+			marklist(l->store.u.l);
+			break;
+		case TCODE:
+			marktree(l->store.u.cc);
+			break;
+		}
+		l = l->next;
+	}
+}
+
+void
+gc(void)
+{
+	int i;
+	Lsym *f;
+	Value *v;
+	Gc *m, **p, *next;
+
+	if(dogc < Mempergc)
+		return;
+	dogc = 0;
+
+	/* Mark */
+	for(m = gcl; m; m = m->gclink)
+		m->gcmark = 0;
+
+	/* Scan */
+	for(i = 0; i < Hashsize; i++) {
+		for(f = hash[i]; f; f = f->hash) {
+			marktree(f->proc);
+			if(f->lexval != Tid)
+				continue;
+			for(v = f->v; v; v = v->pop) {
+				switch(v->type) {
+				case TSTRING:
+					v->store.u.string->gc.gcmark = 1;
+					break;
+				case TLIST:
+					marklist(v->store.u.l);
+					break;
+				case TCODE:
+					marktree(v->store.u.cc);
+					break;
+				}
+			}
+		}
+	}
+
+	/* Free */
+	p = &gcl;
+	for(m = gcl; m; m = next) {
+		next = m->gclink;
+		if(m->gcmark == 0) {
+			*p = next;
+			free(m);	/* Sleazy reliance on my malloc */
+		}
+		else
+			p = &m->gclink;
+	}
+}
+
+void*
+gmalloc(long l)
+{
+	void *p;
+
+	dogc += l;
+	p = malloc(l);
+	if(p == 0)
+		fatal("out of memory");
+	return p;
+}
+
+void
+checkqid(int f1, int pid)
+{
+	int fd;
+	Dir *d1, *d2;
+	char buf[128];
+
+	if(kernel)
+		return;
+
+	d1 = dirfstat(f1);
+	if(d1 == nil){
+		print("checkqid: (qid not checked) dirfstat: %r\n");
+		return;
+	}
+
+	sprint(buf, "/proc/%d/text", pid);
+	fd = open(buf, OREAD);
+	if(fd < 0 || (d2 = dirfstat(fd)) == nil){
+		print("checkqid: (qid not checked) dirstat %s: %r\n", buf);
+		free(d1);
+		if(fd >= 0)
+			close(fd);
+		return;
+	}
+
+	close(fd);
+
+	if(d1->qid.path != d2->qid.path || d1->qid.vers != d2->qid.vers || d1->qid.type != d2->qid.type){
+		print("path %llux %llux vers %lud %lud type %d %d\n",
+			d1->qid.path, d2->qid.path, d1->qid.vers, d2->qid.vers, d1->qid.type, d2->qid.type);
+		print("warning: image does not match text for pid %d\n", pid);
+	}
+	free(d1);
+	free(d2);
+}
+
+void
+catcher(void *junk, char *s)
+{
+	USED(junk);
+
+	if(strstr(s, "interrupt")) {
+		gotint = 1;
+		noted(NCONT);
+	}
+	if(strstr(s, "child"))
+		noted(NCONT);
+fprint(2, "note: %s\n", s);
+	noted(NDFLT);
+}
+
+char*
+system(void)
+{
+	char *cpu, *p, *q;
+	static char kernel[128];
+
+	cpu = getenv("cputype");
+	if(cpu == 0) {
+		cpu = "mips";
+		print("$cputype not set; assuming %s\n", cpu);
+	}
+	p = getenv("terminal");
+	if(p == 0 || (p=strchr(p, ' ')) == 0 || p[1] == ' ' || p[1] == 0) {
+		p = "9power";
+		print("missing or bad $terminal; assuming %s\n", p);
+	}
+	else{
+		p++;
+		q = strchr(p, ' ');
+		if(q)
+			*q = 0;
+		sprint(kernel, "/%s/9%s", cpu, p);
+	}
+	return kernel;
+}
+
+int
+isnumeric(char *s)
+{
+	while(*s) {
+		if(*s < '0' || *s > '9')
+			return 0;
+		s++;
+	}
+	return 1;
+}
+
+int
+xfmt(Fmt *f)
+{
+	f->flags ^= FmtSharp;
+	return __ifmt(f);
+}
diff --git a/src/cmd/acid/mkfile b/src/cmd/acid/mkfile
new file mode 100644
index 0000000..dded62a
--- /dev/null
+++ b/src/cmd/acid/mkfile
@@ -0,0 +1,32 @@
+<$PLAN9/src/mkhdr
+
+TARG=acid
+UOFILES=\
+	main.$O\
+	lex.$O\
+	util.$O\
+	exec.$O\
+	expr.$O\
+	list.$O\
+	builtin.$O\
+	proc.$O\
+	dot.$O\
+	print.$O\
+
+OFILES=$UOFILES y.tab.$O
+
+YFILES=dbg.y
+HFILES=acid.h
+
+BIN=/home/rsc/bin
+
+SHORTLIB=mach2 regexp9 bio 9
+
+<$PLAN9/src/mkone
+
+CFLAGS=$CFLAGS -I../libmach2
+
+lex.$O:		y.tab.h
+util.$O:	y.tab.h
+builtin.$O:	y.tab.h
+main.$O:	y.tab.h
diff --git a/src/cmd/acid/print.c b/src/cmd/acid/print.c
new file mode 100644
index 0000000..00d7b73
--- /dev/null
+++ b/src/cmd/acid/print.c
@@ -0,0 +1,446 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+
+static char *binop[] =
+{
+	[OMUL]	"*",
+	[ODIV]	"/",
+	[OMOD]	"%",
+	[OADD]	"+",
+	[OSUB]	"-",
+	[ORSH]	">>",
+	[OLSH]	"<<",
+	[OLT]	"<",
+	[OGT]	">",
+	[OLEQ]	"<=",
+	[OGEQ]	">=",
+	[OEQ]	"==",
+	[ONEQ]	"!=",
+	[OLAND]	"&",
+	[OXOR]	"^",
+	[OLOR]	"|",
+	[OCAND]	"&&",
+	[OCOR]	"||",
+	[OASGN]	" = ",
+};
+
+static char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+char *typenames[] =
+{
+	[TINT]		"integer",
+	[TFLOAT]	"float",
+	[TSTRING]	"string",
+	[TLIST]		"list",
+	[TCODE]		"code",
+};
+
+int
+cmp(const void *va, const void *vb)
+{
+	char **a = (char**)va;
+	char **b = (char**)vb;
+
+	return strcmp(*a, *b);
+}
+
+void
+fundefs(void)
+{
+	Lsym *l;
+	char **vec;
+	int i, j, n, max, col, f, g, s;
+
+	max = 0;
+	f = 0;
+	g = 100;
+	vec = malloc(sizeof(char*)*g);
+	if(vec == 0)
+		fatal("out of memory");
+
+	for(i = 0; i < Hashsize; i++) {
+		for(l = hash[i]; l; l = l->hash) {
+			if(l->proc == 0 && l->builtin == 0)
+				continue;
+			n = strlen(l->name);
+			if(n > max)
+				max = n;
+			if(f >= g) {
+				g *= 2;
+				vec = realloc(vec, sizeof(char*)*g);
+				if(vec == 0)
+					fatal("out of memory");
+			}
+			vec[f++] = l->name;
+		}
+	}
+        qsort(vec, f, sizeof(char*), cmp);
+	max++;
+	col = 60/max;
+	s = (f+col-1)/col;
+
+	for(i = 0; i < s; i++) {
+		for(j = i; j < f; j += s)
+			Bprint(bout, "%-*s", max, vec[j]);
+		Bprint(bout, "\n");
+	}
+}
+
+void
+whatis(Lsym *l)
+{
+	int t;
+	int def;
+	Type *ti;
+
+	if(l == 0) {
+		fundefs();
+		return;
+	}
+
+	def = 0;
+	if(l->v->set) {
+		t = l->v->type;
+		Bprint(bout, "%s variable", typenames[t]);
+		if(t == TINT || t == TFLOAT)
+			Bprint(bout, " format %c", l->v->store.fmt);
+		if(l->v->store.comt)
+			Bprint(bout, " complex %s",
+						l->v->store.comt->base->name);
+		Bputc(bout, '\n');
+		def = 1;
+	}
+	if(l->lt) {
+		Bprint(bout, "complex %s {\n", l->name);
+		for(ti = l->lt; ti; ti = ti->next) {
+			if(ti->type) {
+				if(ti->fmt == 'a') {
+					Bprint(bout, "\t%s %d %s;\n",
+					ti->type->name, ti->offset,
+					ti->tag->name);
+				}
+				else {
+					Bprint(bout, "\t'%c' %s %d %s;\n",
+					ti->fmt, ti->type->name, ti->offset,
+					ti->tag->name);
+				}
+			}
+			else
+				Bprint(bout, "\t'%c' %d %s;\n",
+				ti->fmt, ti->offset, ti->tag->name);
+		}
+		Bprint(bout, "};\n");
+		def = 1;
+	}
+	if(l->proc) {
+		Bprint(bout, "defn %s(", l->name);
+		pexpr(l->proc->left);
+		Bprint(bout, ") {\n");
+		pcode(l->proc->right, 1);
+		Bprint(bout, "}\n");
+		def = 1;
+	}
+	if(l->builtin) {
+		Bprint(bout, "builtin function\n");
+		def = 1;
+	}
+	if(def == 0)
+		Bprint(bout, "%s is undefined\n", l->name);
+}
+
+void
+slist(Node *n, int d)
+{
+	if(n == 0)
+		return;
+	if(n->op == OLIST)
+		Bprint(bout, "%.*s{\n", d-1, tabs);
+	pcode(n, d);
+	if(n->op == OLIST)
+		Bprint(bout, "%.*s}\n", d-1, tabs);
+}
+
+void
+pcode(Node *n, int d)
+{
+	Node *r, *l;
+
+	if(n == 0)
+		return;
+
+	r = n->right;
+	l = n->left;
+
+	switch(n->op) {
+	default:
+		Bprint(bout, "%.*s", d, tabs);
+		pexpr(n);
+		Bprint(bout, ";\n");
+		break;
+	case OLIST:
+		pcode(n->left, d);
+		pcode(n->right, d);
+		break;
+	case OLOCAL:
+		Bprint(bout, "%.*slocal", d, tabs);
+		while(l) {
+			Bprint(bout, " %s", l->sym->name);
+			l = l->left;
+			if(l == 0)
+				Bprint(bout, ";\n");
+			else
+				Bprint(bout, ",");
+		}
+		break;
+	case OCOMPLEX:
+		Bprint(bout, "%.*scomplex %s %s;\n", d, tabs, n->sym->name, l->sym->name);
+		break;
+	case OIF:
+		Bprint(bout, "%.*sif ", d, tabs);
+		pexpr(l);
+		d++;
+		Bprint(bout, " then\n");
+		if(r && r->op == OELSE) {
+			slist(r->left, d);
+			Bprint(bout, "%.*selse\n", d-1, tabs);
+			slist(r->right, d);
+		}
+		else
+			slist(r, d);
+		break;
+	case OWHILE:
+		Bprint(bout, "%.*swhile ", d, tabs);
+		pexpr(l);
+		d++;
+		Bprint(bout, " do\n");
+		slist(r, d);
+		break;
+	case ORET:
+		Bprint(bout, "%.*sreturn ", d, tabs);
+		pexpr(l);
+		Bprint(bout, ";\n");
+		break;
+	case ODO:
+		Bprint(bout, "%.*sloop ", d, tabs);
+		pexpr(l->left);
+		Bprint(bout, ", ");
+		pexpr(l->right);
+		Bprint(bout, " do\n");
+		slist(r, d+1);
+	}
+}
+
+void
+pexpr(Node *n)
+{
+	Node *r, *l;
+
+	if(n == 0)
+		return;
+
+	r = n->right;
+	l = n->left;
+
+	switch(n->op) {
+	case ONAME:
+		Bprint(bout, "%s", n->sym->name);
+		break;
+	case OCONST:
+		switch(n->type) {
+		case TINT:
+			Bprint(bout, "%d", (int)n->store.u.ival);
+			break;
+		case TFLOAT:
+			Bprint(bout, "%g", n->store.u.fval);
+			break;
+		case TSTRING:
+			pstr(n->store.u.string);
+			break;
+		case TLIST:
+			break;
+		}
+		break;
+	case OMUL:
+	case ODIV:
+	case OMOD:
+	case OADD:
+	case OSUB:
+	case ORSH:
+	case OLSH:
+	case OLT:
+	case OGT:
+	case OLEQ:
+	case OGEQ:
+	case OEQ:
+	case ONEQ:
+	case OLAND:
+	case OXOR:
+	case OLOR:
+	case OCAND:
+	case OCOR:
+		Bputc(bout, '(');
+		pexpr(l);
+		Bprint(bout, binop[(uchar)n->op]);
+		pexpr(r);
+		Bputc(bout, ')');
+		break;
+	case OASGN:
+		pexpr(l);
+		Bprint(bout, binop[(uchar)n->op]);
+		pexpr(r);
+		break;
+	case OINDM:
+		Bprint(bout, "*");
+		pexpr(l);
+		break;
+	case OEDEC:
+		Bprint(bout, "--");
+		pexpr(l);
+		break;
+	case OEINC:
+		Bprint(bout, "++");
+		pexpr(l);
+		break;
+	case OPINC:
+		pexpr(l);
+		Bprint(bout, "++");
+		break;
+	case OPDEC:
+		pexpr(l);
+		Bprint(bout, "--");
+		break;
+	case ONOT:
+		Bprint(bout, "!");
+		pexpr(l);
+		break;
+	case OLIST:
+		pexpr(l);
+		if(r) {
+			Bprint(bout, ",");
+			pexpr(r);
+		}
+		break;
+	case OCALL:
+		pexpr(l);
+		Bprint(bout, "(");
+		pexpr(r);
+		Bprint(bout, ")");
+		break;
+	case OCTRUCT:
+		Bprint(bout, "{");
+		pexpr(l);
+		Bprint(bout, "}");
+		break;
+	case OHEAD:
+		Bprint(bout, "head ");
+		pexpr(l);
+		break;
+	case OTAIL:
+		Bprint(bout, "tail ");
+		pexpr(l);
+		break;
+	case OAPPEND:
+		Bprint(bout, "append ");
+		pexpr(l);
+		Bprint(bout, ",");
+		pexpr(r);
+		break;
+	case ODELETE:
+		Bprint(bout, "delete ");
+		pexpr(l);
+		Bprint(bout, ",");
+		pexpr(r);
+		break;
+	case ORET:
+		Bprint(bout, "return ");
+		pexpr(l);
+		break;
+	case OINDEX:
+		pexpr(l);
+		Bprint(bout, "[");
+		pexpr(r);
+		Bprint(bout, "]");
+		break;
+	case OINDC:
+		Bprint(bout, "@");
+		pexpr(l);
+		break;
+	case ODOT:
+		pexpr(l);
+		Bprint(bout, ".%s", n->sym->name);
+		break;
+	case OFRAME:
+		Bprint(bout, "%s:%s", n->sym->name, l->sym->name);
+		break;
+	case OCAST:
+		Bprint(bout, "(%s)", n->sym->name);
+		pexpr(l);
+		break;
+	case OFMT:
+		pexpr(l);
+		Bprint(bout, "\\%c", (int)r->store.u.ival);
+		break;
+	case OEVAL:
+		Bprint(bout, "eval ");
+		pexpr(l);
+		break;
+	case OWHAT:
+		Bprint(bout, "whatis");
+		if(n->sym)
+			Bprint(bout, " %s", n->sym->name);
+		break;
+	}
+}
+
+void
+pstr(String *s)
+{
+	int i, c;
+
+	Bputc(bout, '"');
+	for(i = 0; i < s->len; i++) {
+		c = s->string[i];
+		switch(c) {
+		case '\0':
+			c = '0';
+			break;
+		case '\n':
+			c = 'n';
+			break;
+		case '\r':
+			c = 'r';
+			break;
+		case '\t':
+			c = 't';
+			break;
+		case '\b':
+			c = 'b';
+			break;
+		case '\f':
+			c = 'f';
+			break;
+		case '\a':
+			c = 'a';
+			break;
+		case '\v':
+			c = 'v';
+			break;
+		case '\\':
+			c = '\\';
+			break;
+		case '"':
+			c = '"';
+			break;
+		default:
+			Bputc(bout, c);
+			continue;
+		}
+		Bputc(bout, '\\');
+		Bputc(bout, c);
+	}
+	Bputc(bout, '"');
+}
diff --git a/src/cmd/acid/proc.c b/src/cmd/acid/proc.c
new file mode 100644
index 0000000..3b862aa
--- /dev/null
+++ b/src/cmd/acid/proc.c
@@ -0,0 +1,251 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+#include "y.tab.h"
+
+static void install(int);
+
+void
+sproc(int xpid)
+{
+	Lsym *s;
+	int i;
+
+	if(symmap == 0)
+		error("no map");
+
+	if(pid == xpid)
+		return;
+
+	if(xpid <= 0)
+		error("bad pid");
+
+	unmapproc(cormap);
+	unmapfile(corhdr, cormap);
+	free(correg);
+	correg = nil;
+
+	if(mapproc(xpid, cormap, &correg) < 0)
+		error("setproc %d: %r", pid);
+
+	/* XXX check text file here? */
+
+	pid = xpid;
+	s = look("pid");
+	s->v->store.u.ival = pid;
+
+	for(i=0; i<cormap->nseg; i++)
+		if(cormap->seg[i].file == nil){
+			if(strcmp(cormap->seg[i].name, "data") == 0)
+				cormap->seg[i].name = "*data";
+			if(strcmp(cormap->seg[i].name, "text") == 0)
+				cormap->seg[i].name = "*text";
+		}
+	install(pid);
+}
+
+int
+nproc(char **argv)
+{
+	char buf[128];
+	int pid, i, fd;
+
+	pid = fork();
+	switch(pid) {
+	case -1:
+		error("new: fork %r");
+	case 0:
+		rfork(RFNAMEG|RFNOTEG);
+		if(ctlproc(getpid(), "hang") < 0)
+			fatal("new: hang %d: %r", getpid());
+
+		close(0);
+		close(1);
+		close(2);
+		for(i = 3; i < NFD; i++)
+			close(i);
+
+		open("/dev/tty", OREAD);
+		open("/dev/tty", OWRITE);
+		open("/dev/tty", OWRITE);
+		exec(argv[0], argv);
+		fatal("new: exec %s: %r");
+	default:
+		install(pid);
+		msg(pid, "waitstop");
+		notes(pid);
+		sproc(pid);
+		dostop(pid);
+		break;
+	}
+
+	return pid;
+}
+
+void
+notes(int pid)
+{
+	Lsym *s;
+	Value *v;
+	int i, n;
+	char **notes;
+	List *l, **tail;
+
+	s = look("notes");
+	if(s == 0)
+		return;
+
+	v = s->v;
+	n = procnotes(pid, &notes);
+	if(n < 0)
+		error("procnotes pid=%d: %r", pid);
+
+	v->set = 1;
+	v->type = TLIST;
+	v->store.u.l = 0;
+	tail = &v->store.u.l;
+	for(i=0; i<n; i++) {
+		l = al(TSTRING);
+		l->store.u.string = strnode(notes[i]);
+		l->store.fmt = 's';
+		*tail = l;
+		tail = &l->next;
+	}
+	free(notes);
+}
+
+void
+dostop(int pid)
+{
+	Lsym *s;
+	Node *np, *p;
+
+	s = look("stopped");
+	if(s && s->proc) {
+		np = an(ONAME, ZN, ZN);
+		np->sym = s;
+		np->store.fmt = 'D';
+		np->type = TINT;
+		p = con(pid);
+		p->store.fmt = 'D';
+		np = an(OCALL, np, p);
+		execute(np);
+	}
+}
+
+static void
+install(int pid)
+{
+	Lsym *s;
+	List *l;
+	int i, new, p;
+
+	new = -1;
+	for(i = 0; i < Maxproc; i++) {
+		p = ptab[i].pid;
+		if(p == pid)
+			return;
+		if(p == 0 && new == -1)
+			new = i;
+	}
+	if(new == -1)
+		error("no free process slots");
+
+	ptab[new].pid = pid;
+
+	s = look("proclist");
+	l = al(TINT);
+	l->store.fmt = 'D';
+	l->store.u.ival = pid;
+	l->next = s->v->store.u.l;
+	s->v->store.u.l = l;
+	s->v->set = 1;
+}
+
+void
+deinstall(int pid)
+{
+	int i;
+	Lsym *s;
+	List *f, **d;
+
+	for(i = 0; i < Maxproc; i++) {
+		if(ptab[i].pid == pid) {
+			detachproc(pid);
+			// close(ptab[i].ctl);
+			ptab[i].pid = 0;
+			s = look("proclist");
+			d = &s->v->store.u.l;
+			for(f = *d; f; f = f->next) {
+				if(f->store.u.ival == pid) {
+					*d = f->next;
+					break;
+				}
+			}
+			s = look("pid");
+			if(s->v->store.u.ival == pid)
+				s->v->store.u.ival = 0;
+			return;
+		}
+	}
+}
+
+void
+msg(int pid, char *msg)
+{
+	int i;
+	char err[ERRMAX];
+
+	for(i = 0; i < Maxproc; i++) {
+		if(ptab[i].pid == pid) {
+			if(ctlproc(pid, msg) < 0){
+				errstr(err, sizeof err);
+				if(strcmp(err, "process exited") == 0)
+					deinstall(pid);
+				error("msg: pid=%d %s: %s", pid, msg, err);
+			}
+			return;
+		}
+	}
+	error("msg: pid=%d: not found for %s", pid, msg);
+}
+
+char *
+getstatus(int pid)
+{
+	int fd;
+	char *p;
+
+	static char buf[128];
+
+	sprint(buf, "/proc/%d/status", pid);
+	fd = open(buf, OREAD);
+	if(fd < 0)
+		error("open %s: %r", buf);
+	read(fd, buf, sizeof(buf));
+	close(fd);
+	p = buf+56+12;			/* Do better! */
+	while(*p == ' ')
+		p--;
+	p[1] = '\0';
+	return buf+56;			/* ditto */
+}
+
+Waitmsg*
+waitfor(int pid)
+{
+	Waitmsg *w;
+
+	for(;;) {
+		if((w = wait()) == nil)
+			error("wait %r");
+		if(w->pid == pid)
+			return w;
+		free(w);
+	}
+	return nil;	/* ken */
+}
diff --git a/src/cmd/acid/util.c b/src/cmd/acid/util.c
new file mode 100644
index 0000000..a40416c
--- /dev/null
+++ b/src/cmd/acid/util.c
@@ -0,0 +1,344 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#define Extern extern
+#include "acid.h"
+#include "y.tab.h"
+
+static int syren;
+
+Lsym*
+unique(char *buf, Symbol *s)
+{
+	Lsym *l;
+	int i, renamed;
+
+	renamed = 0;
+	strcpy(buf, s->name);
+	for(;;) {
+		l = look(buf);
+		if(l == 0 || (l->lexval == Tid && l->v->set == 0))
+			break;
+
+		if(syren == 0 && !quiet) {
+			print("Symbol renames:\n");
+			syren = 1;
+		}
+		i = strlen(buf)+1;
+		memmove(buf+1, buf, i);
+		buf[0] = '$';
+		renamed++;
+		if(renamed > 5 && !quiet) {
+			print("Too many renames; must be X source!\n");
+			break;
+		}
+	}
+	if(renamed && !quiet)
+		print("\t%s=%s %c/%Z\n", s->name, buf, s->type, s->loc);
+	if(l == 0)
+		l = enter(buf, Tid);
+	return l;	
+}
+
+void
+varsym(void)
+{
+	Lsym *l;
+	Fhdr *fp;
+
+	l = mkvar("symbols");
+	if(l->v->set)
+		return;
+
+	l->v->set = 1;
+	l->v->type = TLIST;
+	l->v->store.u.l = nil;
+
+	for(fp=fhdrlist; fp; fp=fp->next){
+		if(fp->ftype == FCORE)
+			continue;
+		addvarsym(fp);
+	}
+
+	if(l->v->store.u.l == nil)
+		print("no debugging symbols\n");
+}
+
+void
+addvarsym(Fhdr *fp)
+{
+	int i;
+	Symbol s;
+	Lsym *l;
+	String *file;
+	ulong v;
+	char buf[1024];
+	List *list, **tail, *tl;
+
+	if(fp == nil)
+		return;
+
+	l = look("symbols");
+	if(l == nil)
+		return;
+
+	l->v->set = 1;
+	l->v->type = TLIST;
+	tail = &l->v->store.u.l;
+	while(*tail)
+		tail = &(*tail)->next;
+
+	file = strnode(fp->filename);
+	for(i=0; findexsym(fp, i, &s)>=0; i++){
+		switch(s.type) {
+		case 'T':
+		case 'L':
+		case 'D':
+		case 'B':
+		case 'b':
+		case 'd':
+		case 'l':
+		case 't':
+			if(s.name[0] == '.')
+				continue;
+			if(s.loc.type != LADDR)
+				continue;
+			v = s.loc.addr;
+			tl = al(TLIST);
+			*tail = tl;
+			tail = &tl->next;
+
+			l = unique(buf, &s);
+			l->v->set = 1;
+			l->v->type = TINT;
+			l->v->store.u.ival = v;
+			if(l->v->store.comt == 0)
+				l->v->store.fmt = 'X';
+
+			/* Enter as list of { name, type, value, file } */
+			list = al(TSTRING);
+			tl->store.u.l = list;
+			list->store.u.string = strnode(buf);
+			list->store.fmt = 's';
+			list->next = al(TINT);
+			list = list->next;
+			list->store.fmt = 'c';
+			list->store.u.ival = s.type;
+			list->next = al(TINT);
+			list = list->next;
+			list->store.fmt = 'X';
+			list->store.u.ival = v;
+			list->next = al(TSTRING);
+			list = list->next;
+			list->store.fmt = 's';
+			list->store.u.string = file;
+		}
+	}
+	*tail = nil;
+}
+
+static int
+infile(List *list, char *file, char **name)
+{
+	/* name */
+	if(list->type != TSTRING)
+		return 0;
+	*name = list->store.u.string->string;
+	if(list->next == nil)
+		return 0;
+	list = list->next;
+
+	/* type character */
+	if(list->next == nil)
+		return 0;
+	list = list->next;
+
+	/* address */
+	if(list->next == nil)
+		return 0;
+	list = list->next;
+
+	/* file */
+	if(list->type != TSTRING)
+		return 0;
+	return strcmp(list->store.u.string->string, file) == 0;
+}
+
+void
+delvarsym(char *file)
+{
+	char *name;
+	Lsym *l;
+	List **lp, *p;
+
+	l = look("symbols");
+	if(l == nil)
+		return;
+
+	if(l->v->type != TLIST)
+		return;
+
+	for(lp=&l->v->store.u.l; *lp; lp=&(*lp)->next){
+		while(*lp){
+			p = *lp;
+			if(p->type != TLIST)
+				break;
+			if(!infile(p->store.u.l, file, &name))
+				break;
+			*lp = p->next;
+			/* XXX remove from hash tables */
+		}
+		if(*lp == nil)
+			break;
+	}
+}
+
+void
+varreg(void)
+{
+	Lsym *l;
+	Value *v;
+	Regdesc *r;
+	List **tail, *li;
+
+	l = mkvar("registers");
+	v = l->v;
+	v->set = 1;
+	v->type = TLIST;
+	v->store.u.l = 0;
+	tail = &v->store.u.l;
+
+	if(mach == nil)
+		return;
+
+	for(r = mach->reglist; r->name; r++) {
+		l = mkvar(r->name);
+		v = l->v;
+		v->set = 1;
+		v->store.u.ival = r->offset;
+		v->store.fmt = r->format;
+		v->type = TINT;
+
+		li = al(TSTRING);
+		li->store.u.string = strnode(r->name);
+		li->store.fmt = 's';
+		*tail = li;
+		tail = &li->next;
+	}
+
+	l = mkvar("bpinst");	/* Breakpoint text */
+	v = l->v;
+	v->type = TSTRING;
+	v->store.fmt = 's';
+	v->set = 1;
+	v->store.u.string = gmalloc(sizeof(String));
+	v->store.u.string->len = mach->bpsize;
+	v->store.u.string->string = gmalloc(mach->bpsize);
+	memmove(v->store.u.string->string, mach->bpinst, mach->bpsize);
+}
+
+void
+loadvars(void)
+{
+	Lsym *l;
+	Value *v;
+
+	l =  mkvar("proc");
+	v = l->v;
+	v->type = TINT;
+	v->store.fmt = 'X';
+	v->set = 1;
+	v->store.u.ival = 0;
+
+	l = mkvar("pid");		/* Current process */
+	v = l->v;
+	v->type = TINT;
+	v->store.fmt = 'D';
+	v->set = 1;
+	v->store.u.ival = 0;
+
+	mkvar("notes");			/* Pending notes */
+
+	l = mkvar("proclist");		/* Attached processes */
+	l->v->type = TLIST;
+}
+
+String*
+strnodlen(char *name, int len)
+{
+	String *s;
+
+	s = gmalloc(sizeof(String)+len+1);
+	s->string = (char*)s+sizeof(String);
+	s->len = len;
+	if(name != 0)
+		memmove(s->string, name, len);
+	s->string[len] = '\0';
+
+	s->gc.gclink = gcl;
+	gcl = (Gc*)s;
+
+	return s;
+}
+
+String*
+strnode(char *name)
+{
+	return strnodlen(name, strlen(name));
+}
+
+String*
+runenode(Rune *name)
+{
+	int len;
+	Rune *p;
+	String *s;
+
+	p = name;
+	for(len = 0; *p; p++)
+		len++;
+
+	len++;
+	len *= sizeof(Rune);
+	s = gmalloc(sizeof(String)+len);
+	s->string = (char*)s+sizeof(String);
+	s->len = len;
+	memmove(s->string, name, len);
+
+	s->gc.gclink = gcl;
+	gcl = (Gc*)s;
+
+	return s;
+}
+
+String*
+stradd(String *l, String *r)
+{
+	int len;
+	String *s;
+
+	len = l->len+r->len;
+	s = gmalloc(sizeof(String)+len+1);
+	s->gc.gclink = gcl;
+	gcl = (Gc*)s;
+	s->len = len;
+	s->string = (char*)s+sizeof(String);
+	memmove(s->string, l->string, l->len);
+	memmove(s->string+l->len, r->string, r->len);
+	s->string[s->len] = 0;
+	return s;
+}
+
+int
+scmp(String *sr, String *sl)
+{
+	if(sr->len != sl->len)
+		return 0;
+
+	if(memcmp(sr->string, sl->string, sl->len))
+		return 0;
+
+	return 1;
+}