aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/acid
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-04-19 19:32:07 +0000
committerrsc <devnull@localhost>2004-04-19 19:32:07 +0000
commit564ca709d0e20d4fc46744bd858f14cff94ab382 (patch)
tree47bf49710238a6cb7112682e7bd0885f82544759 /src/cmd/acid
parent22a7368ef24d5c530a275dd0db0bd4f23b1c3ce3 (diff)
downloadplan9port-564ca709d0e20d4fc46744bd858f14cff94ab382.tar.gz
plan9port-564ca709d0e20d4fc46744bd858f14cff94ab382.tar.bz2
plan9port-564ca709d0e20d4fc46744bd858f14cff94ab382.zip
acid
Diffstat (limited to 'src/cmd/acid')
-rw-r--r--src/cmd/acid/Notes49
-rw-r--r--src/cmd/acid/acid.h313
-rw-r--r--src/cmd/acid/builtin.c1460
-rw-r--r--src/cmd/acid/dbg.y413
-rw-r--r--src/cmd/acid/dot.c153
-rw-r--r--src/cmd/acid/exec.c538
-rw-r--r--src/cmd/acid/expr.c1018
-rw-r--r--src/cmd/acid/lex.c661
-rw-r--r--src/cmd/acid/list.c270
-rw-r--r--src/cmd/acid/main.c630
-rw-r--r--src/cmd/acid/mkfile32
-rw-r--r--src/cmd/acid/print.c446
-rw-r--r--src/cmd/acid/proc.c251
-rw-r--r--src/cmd/acid/util.c344
14 files changed, 6578 insertions, 0 deletions
diff --git a/src/cmd/acid/Notes b/src/cmd/acid/Notes
new file mode 100644
index 00000000..597bf58c
--- /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 00000000..729341b3
--- /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 00000000..492fdfb4
--- /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 00000000..afdf538c
--- /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 00000000..07b8c194
--- /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 00000000..1d5b231a
--- /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 00000000..46e522bf
--- /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 00000000..40f6400e
--- /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 00000000..76214b2d
--- /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 00000000..dccff268
--- /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 00000000..dded62a6
--- /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 00000000..00d7b737
--- /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 00000000..3b862aa9
--- /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 00000000..a40416c2
--- /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;
+}