summaryrefslogtreecommitdiff
path: root/acme/bin/source/adict
diff options
context:
space:
mode:
Diffstat (limited to 'acme/bin/source/adict')
-rw-r--r--acme/bin/source/adict/_adict.c584
-rw-r--r--acme/bin/source/adict/_win.c315
-rw-r--r--acme/bin/source/adict/adict.c591
-rw-r--r--acme/bin/source/adict/adict.h10
-rw-r--r--acme/bin/source/adict/man26
-rw-r--r--acme/bin/source/adict/mkfile11
-rw-r--r--acme/bin/source/adict/win.c315
-rw-r--r--acme/bin/source/adict/win.h59
8 files changed, 1911 insertions, 0 deletions
diff --git a/acme/bin/source/adict/_adict.c b/acme/bin/source/adict/_adict.c
new file mode 100644
index 000000000..f2c14f5ea
--- /dev/null
+++ b/acme/bin/source/adict/_adict.c
@@ -0,0 +1,584 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include "win.h"
+#include "adict.h"
+
+char *prog = "adict";
+char *lprog = "/bin/adict";
+char *xprog = "/bin/dict";
+char *dict, *pattern, *curaddr[MAXMATCH], *curone, *args[6], buffer[80];
+char abuffer[80], fbuffer[80], pbuffer[80];
+int curindex, count, Eopen, Mopen;
+Win Mwin, Ewin, Dwin;
+
+void openwin(char*, char*, Win*, int);
+void handle(Win*, int);
+void rexec(void*);
+void pexec(void*);
+int getaddr(char*);
+
+void
+usage(void)
+{
+ threadprint(2, "usage: %s [-d dictname] [pattern]\n", argv0);
+ threadexitsall(nil);
+}
+
+void
+threadmain(int argc, char** argv)
+{
+ ARGBEGIN{
+ case 'd':
+ dict = strdup(ARGF());
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ /* if running as other name, note that fact */
+ if(access(argv0, AEXIST) == 0)
+ lprog = argv0;
+
+ switch(argc){
+ case 1:
+ pattern = pbuffer;
+ strcpy(pattern,argv[0]);
+ if(dict == nil)
+ dict = "oed";
+ break;
+ case 0:
+ break;
+ default:
+ usage();
+ }
+
+ if ((dict == nil) && (pattern == nil))
+ openwin(prog,"", &Dwin, Dictwin);
+ if (pattern == nil)
+ openwin(prog,"",&Ewin, Entrywin);
+ if ((count = getaddr(pattern)) <= 1)
+ openwin(prog,"Prev Next", &Ewin, Entrywin);
+ else
+ openwin(prog, "", &Mwin, Matchwin);
+}
+
+static int
+procrexec(char *xprog, ...)
+{
+ int fpipe[2];
+ void *rexarg[4];
+ Channel *c;
+ va_list va;
+ int i;
+ char *p;
+
+ pipe(fpipe);
+ va_start(va, xprog);
+ p = xprog;
+ for(i=0; p && i+1<nelem(args); i++){
+ args[i] = p;
+ p = va_arg(va, char*);
+ }
+ args[i] = nil;
+
+ c = chancreate(sizeof(ulong), 0);
+ rexarg[0] = xprog;
+ rexarg[1] = args;
+ rexarg[2] = fpipe;
+ rexarg[3] = c;
+
+ proccreate(rexec, rexarg, 8192);
+ recvul(c);
+ chanfree(c);
+ close(fpipe[1]);
+ return fpipe[0];
+}
+
+int
+getaddr(char *pattern)
+{
+ /* Get char offset into dictionary of matches. */
+
+ int fd, i;
+ Biobuf inbuf;
+ char *bufptr;
+char *obuf;
+
+ if (pattern == nil) {
+ curone = nil;
+ curindex = 0;
+ curaddr[curindex] = nil;
+ return 0;
+ }
+
+ sprint(buffer,"/%s/A", pattern);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ Binit(&inbuf, fd, OREAD);
+ i = 0;
+ curindex = 0;
+ while ((bufptr = Brdline(&inbuf, '\n')) != nil && (i < (MAXMATCH-1))) {
+ bufptr[Blinelen(&inbuf)-1] = 0;
+obuf=bufptr;
+ while (bufptr[0] != '#' && bufptr[0] != 0) bufptr++;
+if(bufptr[0] == 0)
+ print("whoops buf «%s»\n", obuf);
+ curaddr[i] = malloc(strlen(bufptr));
+ strcpy(curaddr[i], bufptr);
+ i++;
+ }
+ curaddr[i] = nil;
+ if (i == MAXMATCH)
+ threadprint(2, "Too many matches!\n");
+ Bterm(&inbuf);
+ close(fd);
+
+ curone = curaddr[curindex];
+ return(i);
+}
+
+char*
+getpattern(char *addr)
+{
+ /* Get the pattern corresponding to an absolute address.*/
+ int fd;
+ char *res, *t;
+
+ res = nil;
+ sprint(buffer,"%sh", addr);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ if (read(fd, pbuffer, 80) > 80)
+ threadprint(2, "Error in getting addres from dict.\n");
+ else {
+ t = pbuffer;
+ /* remove trailing whitespace, newline */
+ if (t != nil){
+ while(*t != 0 && *t != '\n')
+ t++;
+ if(t == 0 && t > pbuffer)
+ t--;
+ while(t >= pbuffer && (*t==' ' || *t=='\n' || *t=='\t' || *t=='\r'))
+ *t-- = 0;
+ }
+ res = pbuffer;
+ }
+ close(fd);
+ return(res);
+}
+
+char*
+chgaddr(int dir)
+{
+ /* Increment or decrement the current address (curone). */
+
+ int fd;
+ char *res, *t;
+
+ res = nil;
+ if (dir < 0)
+ sprint(buffer,"%s-a", curone);
+ else
+ sprint(buffer,"%s+a", curone);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ if (read(fd, abuffer, 80) > 80)
+ threadprint(2, "Error in getting addres from dict.\n");
+ else {
+ res = abuffer;
+ while (*res != '#') res++;
+ t = res;
+ while ((*t != '\n') && (t != nil)) t++;
+ if (t != nil) *t = 0;
+ }
+ close(fd);
+ return(res);
+}
+
+void
+dispdicts(Win *cwin)
+{
+ /* Display available dictionaries in window. */
+
+ int fd, nb, i;
+ char buf[1024], *t;
+
+ fd = procrexec(xprog, "-d", "?", nil);
+ wreplace(cwin, "0,$","",0); /* Clear window */
+ while ((nb = read(fd, buf, 1024)) > 0) {
+ t = buf;
+ i = 0;
+ if (strncmp("Usage", buf, 5) == 0) { /* Remove first line. */
+ while (t[0] != '\n') {
+ t++;
+ i++;
+ }
+ t++;
+ i++;
+ }
+ wwritebody(cwin, t, nb-i);
+ }
+ close(fd);
+ wclean(cwin);
+}
+
+void
+dispentry(Win *cwin)
+{
+ /* Display the current selection in window. */
+
+ int fd, nb;
+ char buf[BUFSIZE];
+
+ if (curone == nil) {
+ if (pattern != nil) {
+ sprint(buf,"Pattern not found.\n");
+ wwritebody(cwin, buf, 19);
+ wclean(cwin);
+ }
+ return;
+ }
+ sprint(buffer,"%sp", curone);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ wreplace(cwin, "0,$","",0); /* Clear window */
+ while ((nb = read(fd, buf, BUFSIZE)) > 0) {
+ wwritebody(cwin, buf, nb);
+ }
+ close(fd);
+ wclean(cwin);
+}
+
+void
+dispmatches(Win *cwin)
+{
+ /* Display the current matches. */
+
+ int fd, nb;
+ char buf[BUFSIZE];
+
+ sprint(buffer,"/%s/H", pattern);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ while ((nb = read(fd, buf, BUFSIZE)) > 0)
+ wwritebody(cwin, buf, nb);
+ close(fd);
+ wclean(cwin);
+}
+
+char*
+format(char *s)
+{
+ /* Format a string to be written in window tag. Acme doesn't like */
+ /* non alpha-num's in the tag line. */
+
+ char *t, *h;
+
+ t = fbuffer;
+ if (s == nil) {
+ *t = 0;
+ return t;
+ }
+ strcpy(t, s);
+ h = t;
+ while (*t != 0) {
+ if (!(((*t >= 'a') && (*t <= 'z')) ||
+ ((*t >= 'A') && (*t <= 'Z')) ||
+ ((*t >= '0') && (*t <= '9'))))
+ *t = '_';
+ t++;
+ }
+ if (strlen(h) > MAXTAG)
+ h[MAXTAG] = 0;
+ if (strcmp(s,h) == 0) return s;
+ return h;
+}
+
+void
+openwin(char *name, char *buttons, Win *twin, int wintype)
+{
+ char buf[80];
+
+ wnew(twin);
+ if (wintype == Dictwin)
+ sprint(buf,"%s",name);
+ else
+ if ((wintype == Entrywin) && (count > 1))
+ sprint(buf,"%s/%s/%s/%d",name, dict, format(pattern), curindex+1);
+ else
+ sprint(buf,"%s/%s/%s",name, dict, format(pattern));
+ wname(twin, buf);
+ wtagwrite(twin, buttons, strlen(buttons));
+ wclean(twin);
+ wdormant(twin);
+ if (wintype == Dictwin)
+ dispdicts(twin);
+ if (wintype == Matchwin) {
+ Mopen = True;
+ dispmatches(twin);
+ }
+ if (wintype == Entrywin) {
+ Eopen = True;
+ dispentry(twin);
+ }
+ handle(twin, wintype);
+}
+
+void
+vopenwin(void *v)
+{
+ void **arg;
+ char *name, *buttons;
+ Win *twin;
+ int wintype;
+
+ arg = v;
+ name = arg[0];
+ buttons = arg[1];
+ twin = arg[2];
+ wintype = (int)arg[3];
+ sendul(arg[4], 0);
+
+ openwin(name, buttons, twin, wintype);
+ threadexits(nil);
+}
+
+void
+procopenwin(char *name, char *buttons, Win *twin, int wintype)
+{
+ void *arg[5];
+ Channel *c;
+
+ c = chancreate(sizeof(ulong), 0);
+ arg[0] = name;
+ arg[1] = buttons;
+ arg[2] = twin;
+ arg[3] = (void*)wintype;
+ arg[4] = c;
+ proccreate(vopenwin, arg, 8192);
+ recvul(c);
+ chanfree(c);
+}
+
+void
+rexec(void *v)
+{
+ void **arg;
+ char *prog;
+ char **args;
+ int *fd;
+ Channel *c;
+
+ arg = v;
+ prog = arg[0];
+ args = arg[1];
+ fd = arg[2];
+ c = arg[3];
+
+ rfork(RFENVG|RFFDG);
+ dup(fd[1], 1);
+ close(fd[1]);
+ close(fd[0]);
+ procexec(c, prog, args);
+ threadprint(2, "Remote pipe execution failed: %s %r\n", prog);
+abort();
+ threadexits(nil);
+}
+
+void
+pexec(void *v)
+{
+ void **arg;
+ char *prog;
+ char **args;
+ Channel *c;
+
+ arg = v;
+ prog = arg[0];
+ args = arg[1];
+ c = arg[2];
+
+ procexec(c, prog, args);
+ threadprint(2, "Remote execution failed: %s %r\n", prog);
+abort();
+ threadexits(nil);
+}
+
+void
+procpexec(char *prog, char **args)
+{
+ void *rexarg[4];
+ Channel *c;
+
+ c = chancreate(sizeof(ulong), 0);
+ rexarg[0] = prog;
+ rexarg[1] = args;
+ rexarg[2] = c;
+
+ proccreate(pexec, rexarg, 8192);
+ recvul(c);
+ chanfree(c);
+}
+
+void
+kill(void)
+{
+ /* Kill all processes related to this one. */
+ int fd;
+
+ sprint(buffer, "/proc/%d/notepg", getpid());
+ fd = open(buffer, OWRITE);
+ rfork(RFNOTEG);
+ write(fd, "kill", 4);
+}
+
+int
+command(char *com, Win *w, int wintype)
+{
+ char *buf;
+
+ if (strncmp(com, "Del", 3) == 0) {
+ switch(wintype){
+ case Entrywin:
+ if (wdel(w)) {
+ Eopen = False;
+ threadexits(nil);
+ }
+ break;
+ case Dictwin:
+ if (wdel(w))
+ threadexits(nil);
+ break;
+ case Matchwin:
+ kill();
+ if (Eopen)
+ if (~wdel(&Ewin)) /* Remove the entry window */
+ wdel(&Ewin);
+ if (!wdel(w))
+ wdel(w);
+ threadexits(nil);
+ break;
+ }
+ return True;
+ }
+ if (strncmp(com, "Next", 4) == 0){
+ if (curone != nil) {
+ curone = chgaddr(1);
+ buf = getpattern(curone);
+ sprint(buffer,"%s/%s/%s", prog, dict, format(buf));
+ wname(w, buffer);
+ dispentry(w);
+ }
+ return True;
+ }
+ if (strncmp(com, "Prev",4) == 0){
+ if (curone != nil) {
+ curone = chgaddr(-1);
+ buf = getpattern(curone);
+ sprint(buffer,"%s/%s/%s", prog, dict, format(buf));
+ wname(w, buffer);
+ dispentry(w);
+ }
+ return True;
+ }
+ if (strncmp(com, "Nmatch",6) == 0){
+ if (curaddr[++curindex] == nil)
+ curindex = 0;
+ curone = curaddr[curindex];
+ if (curone != nil) {
+ sprint(buffer,"%s/%s/%s/%d",prog,dict,format(pattern),curindex+1);
+ wname(w, buffer);
+ dispentry(w);
+ }
+ return True;
+ }
+ return False;
+}
+
+void
+handle(Win *w, int wintype)
+{
+ Event e, e2, ea, etoss;
+ char *s, *t, buf[80];
+ int tmp, na;
+
+ while (True) {
+ wevent(w, &e);
+ switch(e.c2){
+ default:
+ /* threadprint(2,"unknown message %c%c\n", e.c1, e.c2); */
+ break;
+ case 'i':
+ /* threadprint(2,"'%s' inserted in tag at %d\n", e.b, e.q0);*/
+ break;
+ case 'I':
+ /* threadprint(2,"'%s' inserted in body at %d\n", e.b, e.q0);*/
+ break;
+ case 'd':
+ /* threadprint(2, "'%s' deleted in tag at %d\n", e.b, e.q0);*/
+ break;
+ case 'D':
+ /* threadprint(2, "'%s' deleted in body at %d\n", e.b, e.q0);*/
+ break;
+ case 'x':
+ case 'X': /* Execute command. */
+ if (e.flag & 2)
+ wevent(w, &e2);
+ if(e.flag & 8){
+ wevent(w, &ea);
+ wevent(w, &etoss);
+ na = ea.nb;
+ } else
+ na = 0;
+ s = e.b;
+ if ((e.flag & 2) && e.nb == 0)
+ s = e2.b;
+ if(na){
+ t = malloc(strlen(s)+1+na+1);
+ snprint(t, strlen(s)+1+na+1, "%s %s", s, ea.b);
+ s = t;
+ }
+ /* if it's a long message, it can't be for us anyway */
+ if(!command(s, w, wintype)) /* send it back */
+ wwriteevent(w, &e);
+ if(na)
+ free(s);
+ break;
+ case 'l':
+ case 'L': /* Look for something. */
+ if (e.flag & 2)
+ wevent(w, &e);
+ wclean(w); /* Set clean bit. */
+ if (wintype == Dictwin) {
+ strcpy(buf, e.b);
+ args[0] = lprog;
+ args[1] = "-d";
+ args[2] = buf;
+ args[3] = nil;
+ procpexec(lprog, args); /* New adict with chosen dict. */
+ }
+ if (wintype == Entrywin) {
+ strcpy(buf, e.b);
+ args[0] = lprog;
+ args[1] = "-d";
+ args[2] = dict;
+ args[3] = buf;
+ args[4] = nil;
+ procpexec(lprog, args); /* New adict with chosen pattern. */
+ }
+ if (wintype == Matchwin) {
+ tmp = atoi(e.b) - 1;
+ if ((tmp >= 0) && (tmp < MAXMATCH) && (curaddr[tmp] != nil)) {
+ curindex = tmp;
+ curone = curaddr[curindex];
+ /* Display selected match. */
+ if (Eopen) {
+ sprint(buf,"%s/%s/%s/%d",prog,dict,format(pattern),curindex+1);
+ wname(&Ewin, buf);
+ dispentry(&Ewin);
+ }
+ else
+ procopenwin(prog,"Nmatch Prev Next", &Ewin, Entrywin);
+ }
+ }
+ break;
+ }
+ }
+}
diff --git a/acme/bin/source/adict/_win.c b/acme/bin/source/adict/_win.c
new file mode 100644
index 000000000..18657c572
--- /dev/null
+++ b/acme/bin/source/adict/_win.c
@@ -0,0 +1,315 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include "win.h"
+
+void*
+erealloc(void *p, uint n)
+{
+ p = realloc(p, n);
+ if(p == nil)
+ threadprint(2, "realloc failed: %r");
+ return p;
+}
+
+void
+wnew(Win *w)
+{
+ char buf[12];
+
+ w->ctl = open("/mnt/acme/new/ctl", ORDWR);
+ if(w->ctl<0 || read(w->ctl, buf, 12)!=12)
+ threadprint (2, "can't open window ctl file: %r");
+ ctlwrite(w, "noscroll\n");
+ w->winid = atoi(buf);
+ w->event = openfile(w, "event");
+ w->addr = -1; /* will be opened when needed */
+ w->body = nil;
+ w->data = -1;
+}
+
+int
+openfile(Win *w, char *f)
+{
+ char buf[64];
+ int fd;
+
+ sprint(buf, "/mnt/acme/%d/%s", w->winid, f);
+ fd = open(buf, ORDWR|OCEXEC);
+ if(fd < 0)
+ threadprint (2,"can't open window %s file: %r", f);
+ return fd;
+}
+
+void
+openbody(Win *w, int mode)
+{
+ char buf[64];
+
+ sprint(buf, "/mnt/acme/%d/body", w->winid);
+ w->body = Bopen(buf, mode|OCEXEC);
+ if(w->body == nil)
+ threadprint(2,"can't open window body file: %r");
+}
+
+void
+wwritebody(Win *w, char *s, int n)
+{
+ if(w->body == nil)
+ openbody(w, OWRITE);
+ if(Bwrite(w->body, s, n) != n)
+ threadprint(2,"write error to window: %r");
+ Bflush(w->body);
+}
+
+void
+wreplace(Win *w, char *addr, char *repl, int nrepl)
+{
+ if(w->addr < 0)
+ w->addr = openfile(w, "addr");
+ if(w->data < 0)
+ w->data = openfile(w, "data");
+ if(write(w->addr, addr, strlen(addr)) < 0){
+ threadprint(2, "mail: warning: badd address %s:%r\n", addr);
+ return;
+ }
+ if(write(w->data, repl, nrepl) != nrepl)
+ threadprint(2, "writing data: %r");
+}
+
+static int
+nrunes(char *s, int nb)
+{
+ int i, n;
+ Rune r;
+
+ n = 0;
+ for(i=0; i<nb; n++)
+ i += chartorune(&r, s+i);
+ return n;
+}
+
+void
+wread(Win *w, uint q0, uint q1, char *data)
+{
+ int m, n, nr;
+ char buf[256];
+
+ if(w->addr < 0)
+ w->addr = openfile(w, "addr");
+ if(w->data < 0)
+ w->data = openfile(w, "data");
+ m = q0;
+ while(m < q1){
+ n = sprint(buf, "#%d", m);
+ if(write(w->addr, buf, n) != n)
+ threadprint(2,"writing addr: %r");
+ n = read(w->data, buf, sizeof buf);
+ if(n <= 0)
+ threadprint(2,"reading data: %r");
+ nr = nrunes(buf, n);
+ while(m+nr >q1){
+ do; while(n>0 && (buf[--n]&0xC0)==0x80);
+ --nr;
+ }
+ if(n == 0)
+ break;
+ memmove(data, buf, n);
+ data += n;
+ *data = 0;
+ m += nr;
+ }
+}
+
+void
+wselect(Win *w, char *addr)
+{
+ if(w->addr < 0)
+ w->addr = openfile(w, "addr");
+ if(write(w->addr, addr, strlen(addr)) < 0)
+ threadprint(2,"writing addr");
+ ctlwrite(w, "dot=addr\n");
+}
+
+void
+wtagwrite(Win *w, char *s, int n)
+{
+ int fd;
+
+ fd = openfile(w, "tag");
+ if(write(fd, s, n) != n)
+ threadprint(2,"tag write: %r");
+ close(fd);
+}
+
+void
+ctlwrite(Win *w, char *s)
+{
+ int n;
+
+ n = strlen(s);
+ if(write(w->ctl, s, n) != n)
+ threadprint(2,"write error to ctl file: %r");
+}
+
+int
+wdel(Win *w)
+{
+ if(write(w->ctl, "del\n", 4) != 4)
+ return False;
+ wdormant(w);
+ close(w->ctl);
+ w->ctl = -1;
+ close(w->event);
+ w->event = -1;
+ return True;
+}
+
+void
+wname(Win *w, char *s)
+{
+ char buf[128];
+
+ sprint(buf, "name %s\n", s);
+ ctlwrite(w, buf);
+}
+
+void
+wclean(Win *w)
+{
+ if(w->body)
+ Bflush(w->body);
+ ctlwrite(w, "clean\n");
+}
+
+void
+wdormant(Win *w)
+{
+ if(w->addr >= 0){
+ close(w->addr);
+ w->addr = -1;
+ }
+ if(w->body != nil){
+ Bterm(w->body);
+ w->body = nil;
+ }
+ if(w->data >= 0){
+ close(w->data);
+ w->data = -1;
+ }
+}
+
+int
+getec(Win *w)
+{
+ if(w->nbuf == 0){
+ w->nbuf = read(w->event, w->buf, sizeof w->buf);
+ if(w->nbuf <= 0)
+ threadprint(2,"event read error: %r");
+ w->bufp = w->buf;
+ }
+ w->nbuf--;
+ return *w->bufp++;
+}
+
+int
+geten(Win *w)
+{
+ int n, c;
+
+ n = 0;
+ while('0'<=(c=getec(w)) && c<='9')
+ n = n*10+(c-'0');
+ if(c != ' ')
+ threadprint(2, "event number syntax");
+ return n;
+}
+
+int
+geter(Win *w, char *buf, int *nb)
+{
+ Rune r;
+ int n;
+
+ r = getec(w);
+ buf[0] = r;
+ n = 1;
+ if(r < Runeself)
+ goto Return;
+ while(!fullrune(buf, n))
+ buf[n++] = getec(w);
+ chartorune(&r, buf);
+ Return:
+ *nb = n;
+ return r;
+}
+
+
+void
+wevent(Win *w, Event *e)
+{
+ int i, nb;
+
+ e->c1 = getec(w);
+ e->c2 = getec(w);
+ e->q0 = geten(w);
+ e->q1 = geten(w);
+ e->flag = geten(w);
+ e->nr = geten(w);
+ if(e->nr > EVENTSIZE)
+ threadprint(2, "wevent: event string too long");
+ e->nb = 0;
+ for(i=0; i<e->nr; i++){
+ e->r[i] = geter(w, e->b+e->nb, &nb);
+ e->nb += nb;
+ }
+ e->r[e->nr] = 0;
+ e->b[e->nb] = 0;
+ if(getec(w) != '\n')
+ threadprint(2, "wevent: event syntax 2");
+}
+
+void
+wslave(Win *w, Channel *ce)
+{
+ Event e;
+
+ while(recv(ce, &e) >= 0)
+ wevent(w, &e);
+}
+
+void
+wwriteevent(Win *w, Event *e)
+{
+ threadprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1);
+}
+
+int
+wreadall(Win *w, char **sp)
+{
+ char *s;
+ int m, na, n;
+
+ if(w->body != nil)
+ Bterm(w->body);
+ openbody(w, OREAD);
+ s = nil;
+ na = 0;
+ n = 0;
+ for(;;){
+ if(na < n+512){
+ na += 1024;
+ s = erealloc(s, na+1);
+ }
+ m = Bread(w->body, s+n, na-n);
+ if(m <= 0)
+ break;
+ n += m;
+ }
+ s[n] = 0;
+ Bterm(w->body);
+ w->body = nil;
+ *sp = s;
+ return n;
+}
diff --git a/acme/bin/source/adict/adict.c b/acme/bin/source/adict/adict.c
new file mode 100644
index 000000000..7ce53e5c2
--- /dev/null
+++ b/acme/bin/source/adict/adict.c
@@ -0,0 +1,591 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include "win.h"
+#include "adict.h"
+
+enum
+{
+ STACK = 8192,
+};
+
+char *prog = "adict";
+char *lprog = "/bin/adict";
+char *xprog = "/bin/dict";
+char *dict, *pattern, *curaddr[MAXMATCH], *curone, *args[6], buffer[80];
+char abuffer[80], fbuffer[80], pbuffer[80];
+int curindex, count, Eopen, Mopen;
+Win Mwin, Ewin, Dwin;
+
+void openwin(char*, char*, Win*, int);
+void handle(Win*, int);
+void rexec(void*);
+void pexec(void*);
+int getaddr(char*);
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-d dictname] [pattern]\n", argv0);
+ threadexitsall(nil);
+}
+
+int mainstacksize = STACK;
+
+void
+threadmain(int argc, char** argv)
+{
+ ARGBEGIN{
+ case 'd':
+ dict = strdup(ARGF());
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ /* if running as other name, note that fact */
+ if(access(argv0, AEXIST) == 0)
+ lprog = argv0;
+
+ switch(argc){
+ case 1:
+ pattern = pbuffer;
+ strcpy(pattern,argv[0]);
+ if(dict == nil)
+ dict = "pgw";
+ break;
+ case 0:
+ break;
+ default:
+ usage();
+ }
+
+ if ((dict == nil) && (pattern == nil))
+ openwin(prog,"", &Dwin, Dictwin);
+ if (pattern == nil)
+ openwin(prog,"",&Ewin, Entrywin);
+ if ((count = getaddr(pattern)) <= 1)
+ openwin(prog,"Prev Next", &Ewin, Entrywin);
+ else
+ openwin(prog, "", &Mwin, Matchwin);
+}
+
+static int
+procrexec(char *xprog, ...)
+{
+ int fpipe[2];
+ void *rexarg[4];
+ Channel *c;
+ va_list va;
+ int i;
+ char *p;
+
+ pipe(fpipe);
+ va_start(va, xprog);
+ p = xprog;
+ for(i=0; p && i+1<nelem(args); i++){
+ args[i] = p;
+ p = va_arg(va, char*);
+ }
+ args[i] = nil;
+
+ c = chancreate(sizeof(ulong), 0);
+ rexarg[0] = xprog;
+ rexarg[1] = args;
+ rexarg[2] = fpipe;
+ rexarg[3] = c;
+
+ proccreate(rexec, rexarg, STACK);
+ recvul(c);
+ chanfree(c);
+ close(fpipe[1]);
+ return fpipe[0];
+}
+
+int
+getaddr(char *pattern)
+{
+ /* Get char offset into dictionary of matches. */
+
+ int fd, i;
+ Biobuf inbuf;
+ char *bufptr;
+char *obuf;
+
+ if (pattern == nil) {
+ curone = nil;
+ curindex = 0;
+ curaddr[curindex] = nil;
+ return 0;
+ }
+
+ sprint(buffer,"/%s/A", pattern);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ Binit(&inbuf, fd, OREAD);
+ i = 0;
+ curindex = 0;
+ while ((bufptr = Brdline(&inbuf, '\n')) != nil && (i < (MAXMATCH-1))) {
+ bufptr[Blinelen(&inbuf)-1] = 0;
+obuf=bufptr;
+ while (bufptr[0] != '#' && bufptr[0] != 0) bufptr++;
+if(bufptr[0] == 0)
+ print("whoops buf «%s»\n", obuf);
+ curaddr[i] = malloc(strlen(bufptr));
+ strcpy(curaddr[i], bufptr);
+ i++;
+ }
+ curaddr[i] = nil;
+ if (i == MAXMATCH)
+ fprint(2, "Too many matches!\n");
+ Bterm(&inbuf);
+ close(fd);
+
+ curone = curaddr[curindex];
+ return(i);
+}
+
+char*
+getpattern(char *addr)
+{
+ /* Get the pattern corresponding to an absolute address.*/
+ int fd;
+ char *res, *t;
+
+ res = nil;
+ sprint(buffer,"%sh", addr);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ if (read(fd, pbuffer, 80) > 80)
+ fprint(2, "Error in getting addres from dict.\n");
+ else {
+ t = pbuffer;
+ /* remove trailing whitespace, newline */
+ if (t != nil){
+ while(*t != 0 && *t != '\n')
+ t++;
+ if(t == 0 && t > pbuffer)
+ t--;
+ while(t >= pbuffer && (*t==' ' || *t=='\n' || *t=='\t' || *t=='\r'))
+ *t-- = 0;
+ }
+ res = pbuffer;
+ }
+ close(fd);
+ return(res);
+}
+
+char*
+chgaddr(int dir)
+{
+ /* Increment or decrement the current address (curone). */
+
+ int fd;
+ char *res, *t;
+
+ res = nil;
+ if (dir < 0)
+ sprint(buffer,"%s-a", curone);
+ else
+ sprint(buffer,"%s+a", curone);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ if (read(fd, abuffer, 80) > 80)
+ fprint(2, "Error in getting addres from dict.\n");
+ else {
+ res = abuffer;
+ while (*res != '#') res++;
+ t = res;
+ while ((*t != '\n') && (t != nil)) t++;
+ if (t != nil) *t = 0;
+ }
+ close(fd);
+ return(res);
+}
+
+void
+dispdicts(Win *cwin)
+{
+ /* Display available dictionaries in window. */
+
+ int fd, nb, i;
+ char buf[1024], *t;
+
+ fd = procrexec(xprog, "-d", "?", nil);
+ wreplace(cwin, "0,$","",0); /* Clear window */
+ while ((nb = read(fd, buf, 1024)) > 0) {
+ t = buf;
+ i = 0;
+ if (strncmp("Usage", buf, 5) == 0) { /* Remove first line. */
+ while (t[0] != '\n') {
+ t++;
+ i++;
+ }
+ t++;
+ i++;
+ }
+ wwritebody(cwin, t, nb-i);
+ }
+ close(fd);
+ wclean(cwin);
+}
+
+void
+dispentry(Win *cwin)
+{
+ /* Display the current selection in window. */
+
+ int fd, nb;
+ char buf[BUFSIZE];
+
+ if (curone == nil) {
+ if (pattern != nil) {
+ sprint(buf,"Pattern not found.\n");
+ wwritebody(cwin, buf, 19);
+ wclean(cwin);
+ }
+ return;
+ }
+ sprint(buffer,"%sp", curone);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ wreplace(cwin, "0,$","",0); /* Clear window */
+ while ((nb = read(fd, buf, BUFSIZE)) > 0) {
+ wwritebody(cwin, buf, nb);
+ }
+ close(fd);
+ wclean(cwin);
+}
+
+void
+dispmatches(Win *cwin)
+{
+ /* Display the current matches. */
+
+ int fd, nb;
+ char buf[BUFSIZE];
+
+ sprint(buffer,"/%s/H", pattern);
+ fd = procrexec(xprog, "-d", dict, "-c", buffer, nil);
+ while ((nb = read(fd, buf, BUFSIZE)) > 0)
+ wwritebody(cwin, buf, nb);
+ close(fd);
+ wclean(cwin);
+}
+
+char*
+format(char *s)
+{
+ /* Format a string to be written in window tag. Acme doesn't like */
+ /* non alpha-num's in the tag line. */
+
+ char *t, *h;
+
+ t = fbuffer;
+ if (s == nil) {
+ *t = 0;
+ return t;
+ }
+ strcpy(t, s);
+ h = t;
+ while (*t != 0) {
+ if (!(((*t >= 'a') && (*t <= 'z')) ||
+ ((*t >= 'A') && (*t <= 'Z')) ||
+ ((*t >= '0') && (*t <= '9'))))
+ *t = '_';
+ t++;
+ }
+ if (strlen(h) > MAXTAG)
+ h[MAXTAG] = 0;
+ if (strcmp(s,h) == 0) return s;
+ return h;
+}
+
+void
+openwin(char *name, char *buttons, Win *twin, int wintype)
+{
+ char buf[80];
+
+ wnew(twin);
+ if (wintype == Dictwin)
+ sprint(buf,"%s",name);
+ else
+ if ((wintype == Entrywin) && (count > 1))
+ sprint(buf,"%s/%s/%s/%d",name, dict, format(pattern), curindex+1);
+ else
+ sprint(buf,"%s/%s/%s",name, dict, format(pattern));
+ wname(twin, buf);
+ wtagwrite(twin, buttons, strlen(buttons));
+ wclean(twin);
+ wdormant(twin);
+ if (wintype == Dictwin)
+ dispdicts(twin);
+ if (wintype == Matchwin) {
+ Mopen = True;
+ dispmatches(twin);
+ }
+ if (wintype == Entrywin) {
+ Eopen = True;
+ dispentry(twin);
+ }
+ handle(twin, wintype);
+}
+
+void
+vopenwin(void *v)
+{
+ void **arg;
+ char *name, *buttons;
+ Win *twin;
+ int wintype;
+
+ arg = v;
+ name = arg[0];
+ buttons = arg[1];
+ twin = arg[2];
+ wintype = (int)arg[3];
+ sendul(arg[4], 0);
+
+ openwin(name, buttons, twin, wintype);
+ threadexits(nil);
+}
+
+void
+procopenwin(char *name, char *buttons, Win *twin, int wintype)
+{
+ void *arg[5];
+ Channel *c;
+
+ c = chancreate(sizeof(ulong), 0);
+ arg[0] = name;
+ arg[1] = buttons;
+ arg[2] = twin;
+ arg[3] = (void*)wintype;
+ arg[4] = c;
+ proccreate(vopenwin, arg, STACK);
+ recvul(c);
+ chanfree(c);
+}
+
+void
+rexec(void *v)
+{
+ void **arg;
+ char *prog;
+ char **args;
+ int *fd;
+ Channel *c;
+
+ arg = v;
+ prog = arg[0];
+ args = arg[1];
+ fd = arg[2];
+ c = arg[3];
+
+ rfork(RFENVG|RFFDG);
+ dup(fd[1], 1);
+ close(fd[1]);
+ close(fd[0]);
+ procexec(c, prog, args);
+ fprint(2, "Remote pipe execution failed: %s %r\n", prog);
+abort();
+ threadexits(nil);
+}
+
+void
+pexec(void *v)
+{
+ void **arg;
+ char *prog;
+ char **args;
+ Channel *c;
+
+ arg = v;
+ prog = arg[0];
+ args = arg[1];
+ c = arg[2];
+
+ procexec(c, prog, args);
+ fprint(2, "Remote execution failed: %s %r\n", prog);
+abort();
+ threadexits(nil);
+}
+
+void
+procpexec(char *prog, char **args)
+{
+ void *rexarg[4];
+ Channel *c;
+
+ c = chancreate(sizeof(ulong), 0);
+ rexarg[0] = prog;
+ rexarg[1] = args;
+ rexarg[2] = c;
+
+ proccreate(pexec, rexarg, STACK);
+ recvul(c);
+ chanfree(c);
+}
+
+void
+kill(void)
+{
+ /* Kill all processes related to this one. */
+ int fd;
+
+ sprint(buffer, "/proc/%d/notepg", getpid());
+ fd = open(buffer, OWRITE);
+ rfork(RFNOTEG);
+ write(fd, "kill", 4);
+}
+
+int
+command(char *com, Win *w, int wintype)
+{
+ char *buf;
+
+ if (strncmp(com, "Del", 3) == 0) {
+ switch(wintype){
+ case Entrywin:
+ if (wdel(w)) {
+ Eopen = False;
+ threadexits(nil);
+ }
+ break;
+ case Dictwin:
+ if (wdel(w))
+ threadexits(nil);
+ break;
+ case Matchwin:
+ kill();
+ if (Eopen)
+ if (~wdel(&Ewin)) /* Remove the entry window */
+ wdel(&Ewin);
+ if (!wdel(w))
+ wdel(w);
+ threadexits(nil);
+ break;
+ }
+ return True;
+ }
+ if (strncmp(com, "Next", 4) == 0){
+ if (curone != nil) {
+ curone = chgaddr(1);
+ buf = getpattern(curone);
+ sprint(buffer,"%s/%s/%s", prog, dict, format(buf));
+ wname(w, buffer);
+ dispentry(w);
+ }
+ return True;
+ }
+ if (strncmp(com, "Prev",4) == 0){
+ if (curone != nil) {
+ curone = chgaddr(-1);
+ buf = getpattern(curone);
+ sprint(buffer,"%s/%s/%s", prog, dict, format(buf));
+ wname(w, buffer);
+ dispentry(w);
+ }
+ return True;
+ }
+ if (strncmp(com, "Nmatch",6) == 0){
+ if (curaddr[++curindex] == nil)
+ curindex = 0;
+ curone = curaddr[curindex];
+ if (curone != nil) {
+ sprint(buffer,"%s/%s/%s/%d",prog,dict,format(pattern),curindex+1);
+ wname(w, buffer);
+ dispentry(w);
+ }
+ return True;
+ }
+ return False;
+}
+
+void
+handle(Win *w, int wintype)
+{
+ Event e, e2, ea, etoss;
+ char *s, *t, buf[80];
+ int tmp, na;
+
+ while (True) {
+ wevent(w, &e);
+ switch(e.c2){
+ default:
+ /* fprint(2,"unknown message %c%c\n", e.c1, e.c2); */
+ break;
+ case 'i':
+ /* fprint(2,"'%s' inserted in tag at %d\n", e.b, e.q0);*/
+ break;
+ case 'I':
+ /* fprint(2,"'%s' inserted in body at %d\n", e.b, e.q0);*/
+ break;
+ case 'd':
+ /* fprint(2, "'%s' deleted in tag at %d\n", e.b, e.q0);*/
+ break;
+ case 'D':
+ /* fprint(2, "'%s' deleted in body at %d\n", e.b, e.q0);*/
+ break;
+ case 'x':
+ case 'X': /* Execute command. */
+ if (e.flag & 2)
+ wevent(w, &e2);
+ if(e.flag & 8){
+ wevent(w, &ea);
+ wevent(w, &etoss);
+ na = ea.nb;
+ } else
+ na = 0;
+ s = e.b;
+ if ((e.flag & 2) && e.nb == 0)
+ s = e2.b;
+ if(na){
+ t = malloc(strlen(s)+1+na+1);
+ snprint(t, strlen(s)+1+na+1, "%s %s", s, ea.b);
+ s = t;
+ }
+ /* if it's a long message, it can't be for us anyway */
+ if(!command(s, w, wintype)) /* send it back */
+ wwriteevent(w, &e);
+ if(na)
+ free(s);
+ break;
+ case 'l':
+ case 'L': /* Look for something. */
+ if (e.flag & 2)
+ wevent(w, &e);
+ wclean(w); /* Set clean bit. */
+ if (wintype == Dictwin) {
+ strcpy(buf, e.b);
+ args[0] = lprog;
+ args[1] = "-d";
+ args[2] = buf;
+ args[3] = nil;
+ procpexec(lprog, args); /* New adict with chosen dict. */
+ }
+ if (wintype == Entrywin) {
+ strcpy(buf, e.b);
+ args[0] = lprog;
+ args[1] = "-d";
+ args[2] = dict;
+ args[3] = buf;
+ args[4] = nil;
+ procpexec(lprog, args); /* New adict with chosen pattern. */
+ }
+ if (wintype == Matchwin) {
+ tmp = atoi(e.b) - 1;
+ if ((tmp >= 0) && (tmp < MAXMATCH) && (curaddr[tmp] != nil)) {
+ curindex = tmp;
+ curone = curaddr[curindex];
+ /* Display selected match. */
+ if (Eopen) {
+ sprint(buf,"%s/%s/%s/%d",prog,dict,format(pattern),curindex+1);
+ wname(&Ewin, buf);
+ dispentry(&Ewin);
+ }
+ else
+ procopenwin(prog,"Nmatch Prev Next", &Ewin, Entrywin);
+ }
+ }
+ break;
+ }
+ }
+}
diff --git a/acme/bin/source/adict/adict.h b/acme/bin/source/adict/adict.h
new file mode 100644
index 000000000..fffaa5454
--- /dev/null
+++ b/acme/bin/source/adict/adict.h
@@ -0,0 +1,10 @@
+enum
+{
+ Matchwin,
+ Entrywin,
+ Dictwin
+};
+
+#define MAXTAG 20
+#define MAXMATCH 100
+#define BUFSIZE 4096
diff --git a/acme/bin/source/adict/man b/acme/bin/source/adict/man
new file mode 100644
index 000000000..ad15bf079
--- /dev/null
+++ b/acme/bin/source/adict/man
@@ -0,0 +1,26 @@
+adict [-d dictionary] [pattern]
+
+ adict with no arguments opens a window that displays all the currently
+available dictionaries. To select a dictionary, click the right mouse button on
+its name.
+
+-d dictionary Opens a window that interfaces to the specified dictionary. To
+ look up a word, enter it in the window, and click the right mouse button on it.
+
+[pattern] If no dictionary is specified, adict looks up the pattern in "oed" (Oxford
+ English Dictionary). If more than one entry is found, adict opens a window
+ displaying the headers of the matching entries. To display a particular entry
+ click the right mouse button on the number to its left.
+
+Quit Exit and remove all windows associated with this one.
+
+Nmatch Display the next matching entry.
+
+Next Display the next entry in the dictionary.
+
+Prev Display the previous entry in the dictionary.
+
+ Nmatch works independently of Prev and Next.
+
+ Any word in the window displaying an entry can be looked up in the selected
+dictionary by clicking the right mouse button on that word. \ No newline at end of file
diff --git a/acme/bin/source/adict/mkfile b/acme/bin/source/adict/mkfile
new file mode 100644
index 000000000..14a079874
--- /dev/null
+++ b/acme/bin/source/adict/mkfile
@@ -0,0 +1,11 @@
+</$objtype/mkfile
+
+TARG=adict
+
+HFILES=win.h
+
+OFILES=adict.$O\
+ win.$O\
+
+BIN= /acme/bin/$objtype
+</sys/src/cmd/mkone
diff --git a/acme/bin/source/adict/win.c b/acme/bin/source/adict/win.c
new file mode 100644
index 000000000..e8b2ee257
--- /dev/null
+++ b/acme/bin/source/adict/win.c
@@ -0,0 +1,315 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include "win.h"
+
+void*
+erealloc(void *p, uint n)
+{
+ p = realloc(p, n);
+ if(p == nil)
+ fprint(2, "realloc failed: %r");
+ return p;
+}
+
+void
+wnew(Win *w)
+{
+ char buf[12];
+
+ w->ctl = open("/mnt/acme/new/ctl", ORDWR);
+ if(w->ctl<0 || read(w->ctl, buf, 12)!=12)
+ fprint (2, "can't open window ctl file: %r");
+ ctlwrite(w, "noscroll\n");
+ w->winid = atoi(buf);
+ w->event = openfile(w, "event");
+ w->addr = -1; /* will be opened when needed */
+ w->body = nil;
+ w->data = -1;
+}
+
+int
+openfile(Win *w, char *f)
+{
+ char buf[64];
+ int fd;
+
+ sprint(buf, "/mnt/acme/%d/%s", w->winid, f);
+ fd = open(buf, ORDWR|OCEXEC);
+ if(fd < 0)
+ fprint (2,"can't open window %s file: %r", f);
+ return fd;
+}
+
+void
+openbody(Win *w, int mode)
+{
+ char buf[64];
+
+ sprint(buf, "/mnt/acme/%d/body", w->winid);
+ w->body = Bopen(buf, mode|OCEXEC);
+ if(w->body == nil)
+ fprint(2,"can't open window body file: %r");
+}
+
+void
+wwritebody(Win *w, char *s, int n)
+{
+ if(w->body == nil)
+ openbody(w, OWRITE);
+ if(Bwrite(w->body, s, n) != n)
+ fprint(2,"write error to window: %r");
+ Bflush(w->body);
+}
+
+void
+wreplace(Win *w, char *addr, char *repl, int nrepl)
+{
+ if(w->addr < 0)
+ w->addr = openfile(w, "addr");
+ if(w->data < 0)
+ w->data = openfile(w, "data");
+ if(write(w->addr, addr, strlen(addr)) < 0){
+ fprint(2, "mail: warning: badd address %s:%r\n", addr);
+ return;
+ }
+ if(write(w->data, repl, nrepl) != nrepl)
+ fprint(2, "writing data: %r");
+}
+
+static int
+nrunes(char *s, int nb)
+{
+ int i, n;
+ Rune r;
+
+ n = 0;
+ for(i=0; i<nb; n++)
+ i += chartorune(&r, s+i);
+ return n;
+}
+
+void
+wread(Win *w, uint q0, uint q1, char *data)
+{
+ int m, n, nr;
+ char buf[256];
+
+ if(w->addr < 0)
+ w->addr = openfile(w, "addr");
+ if(w->data < 0)
+ w->data = openfile(w, "data");
+ m = q0;
+ while(m < q1){
+ n = sprint(buf, "#%d", m);
+ if(write(w->addr, buf, n) != n)
+ fprint(2,"writing addr: %r");
+ n = read(w->data, buf, sizeof buf);
+ if(n <= 0)
+ fprint(2,"reading data: %r");
+ nr = nrunes(buf, n);
+ while(m+nr >q1){
+ do; while(n>0 && (buf[--n]&0xC0)==0x80);
+ --nr;
+ }
+ if(n == 0)
+ break;
+ memmove(data, buf, n);
+ data += n;
+ *data = 0;
+ m += nr;
+ }
+}
+
+void
+wselect(Win *w, char *addr)
+{
+ if(w->addr < 0)
+ w->addr = openfile(w, "addr");
+ if(write(w->addr, addr, strlen(addr)) < 0)
+ fprint(2,"writing addr");
+ ctlwrite(w, "dot=addr\n");
+}
+
+void
+wtagwrite(Win *w, char *s, int n)
+{
+ int fd;
+
+ fd = openfile(w, "tag");
+ if(write(fd, s, n) != n)
+ fprint(2,"tag write: %r");
+ close(fd);
+}
+
+void
+ctlwrite(Win *w, char *s)
+{
+ int n;
+
+ n = strlen(s);
+ if(write(w->ctl, s, n) != n)
+ fprint(2,"write error to ctl file: %r");
+}
+
+int
+wdel(Win *w)
+{
+ if(write(w->ctl, "del\n", 4) != 4)
+ return False;
+ wdormant(w);
+ close(w->ctl);
+ w->ctl = -1;
+ close(w->event);
+ w->event = -1;
+ return True;
+}
+
+void
+wname(Win *w, char *s)
+{
+ char buf[128];
+
+ sprint(buf, "name %s\n", s);
+ ctlwrite(w, buf);
+}
+
+void
+wclean(Win *w)
+{
+ if(w->body)
+ Bflush(w->body);
+ ctlwrite(w, "clean\n");
+}
+
+void
+wdormant(Win *w)
+{
+ if(w->addr >= 0){
+ close(w->addr);
+ w->addr = -1;
+ }
+ if(w->body != nil){
+ Bterm(w->body);
+ w->body = nil;
+ }
+ if(w->data >= 0){
+ close(w->data);
+ w->data = -1;
+ }
+}
+
+int
+getec(Win *w)
+{
+ if(w->nbuf == 0){
+ w->nbuf = read(w->event, w->buf, sizeof w->buf);
+ if(w->nbuf <= 0)
+ fprint(2,"event read error: %r");
+ w->bufp = w->buf;
+ }
+ w->nbuf--;
+ return *w->bufp++;
+}
+
+int
+geten(Win *w)
+{
+ int n, c;
+
+ n = 0;
+ while('0'<=(c=getec(w)) && c<='9')
+ n = n*10+(c-'0');
+ if(c != ' ')
+ fprint(2, "event number syntax");
+ return n;
+}
+
+int
+geter(Win *w, char *buf, int *nb)
+{
+ Rune r;
+ int n;
+
+ r = getec(w);
+ buf[0] = r;
+ n = 1;
+ if(r < Runeself)
+ goto Return;
+ while(!fullrune(buf, n))
+ buf[n++] = getec(w);
+ chartorune(&r, buf);
+ Return:
+ *nb = n;
+ return r;
+}
+
+
+void
+wevent(Win *w, Event *e)
+{
+ int i, nb;
+
+ e->c1 = getec(w);
+ e->c2 = getec(w);
+ e->q0 = geten(w);
+ e->q1 = geten(w);
+ e->flag = geten(w);
+ e->nr = geten(w);
+ if(e->nr > EVENTSIZE)
+ fprint(2, "wevent: event string too long");
+ e->nb = 0;
+ for(i=0; i<e->nr; i++){
+ e->r[i] = geter(w, e->b+e->nb, &nb);
+ e->nb += nb;
+ }
+ e->r[e->nr] = 0;
+ e->b[e->nb] = 0;
+ if(getec(w) != '\n')
+ fprint(2, "wevent: event syntax 2");
+}
+
+void
+wslave(Win *w, Channel *ce)
+{
+ Event e;
+
+ while(recv(ce, &e) >= 0)
+ wevent(w, &e);
+}
+
+void
+wwriteevent(Win *w, Event *e)
+{
+ fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1);
+}
+
+int
+wreadall(Win *w, char **sp)
+{
+ char *s;
+ int m, na, n;
+
+ if(w->body != nil)
+ Bterm(w->body);
+ openbody(w, OREAD);
+ s = nil;
+ na = 0;
+ n = 0;
+ for(;;){
+ if(na < n+512){
+ na += 1024;
+ s = erealloc(s, na+1);
+ }
+ m = Bread(w->body, s+n, na-n);
+ if(m <= 0)
+ break;
+ n += m;
+ }
+ s[n] = 0;
+ Bterm(w->body);
+ w->body = nil;
+ *sp = s;
+ return n;
+}
diff --git a/acme/bin/source/adict/win.h b/acme/bin/source/adict/win.h
new file mode 100644
index 000000000..8e1698aa1
--- /dev/null
+++ b/acme/bin/source/adict/win.h
@@ -0,0 +1,59 @@
+enum
+{
+ False,
+ True,
+ EVENTSIZE=256,
+};
+
+
+typedef struct Event Event;
+struct Event
+{
+ int c1;
+ int c2;
+ int q0;
+ int q1;
+ int flag;
+ int nb;
+ int nr;
+ char b[EVENTSIZE*UTFmax+1];
+ Rune r[EVENTSIZE+1];
+};
+
+
+typedef struct Win Win;
+struct Win
+{
+ int winid;
+ int addr;
+ Biobuf *body;
+ int ctl;
+ int data;
+ int event;
+ char buf[512];
+ char *bufp;
+ int nbuf;
+};
+
+int dead(Win*);
+void wnew(Win*);
+void wwritebody(Win*, char *s, int n);
+void wread(Win*, uint, uint, char*);
+void wclean(Win*);
+void wname(Win*, char*);
+void wdormant(Win*);
+void wevent(Win*, Event*);
+void wtagwrite(Win*, char*, int);
+void wwriteevent(Win*, Event*);
+void wslave(Win*, Channel*); /* chan(Event) */
+void wreplace(Win*, char*, char*, int);
+void wselect(Win*, char*);
+int wdel(Win*);
+int wreadall(Win*, char**);
+
+void ctlwrite(Win*, char*);
+int getec(Win*);
+int geten(Win*);
+int geter(Win*, char*, int*);
+int openfile(Win*, char*);
+void openbody(Win*, int);