diff options
Diffstat (limited to 'acme/bin/source/win')
-rw-r--r-- | acme/bin/source/win/_fs.c | 146 | ||||
-rw-r--r-- | acme/bin/source/win/_main.c | 651 | ||||
-rw-r--r-- | acme/bin/source/win/dat.h | 95 | ||||
-rw-r--r-- | acme/bin/source/win/fs.c | 147 | ||||
-rw-r--r-- | acme/bin/source/win/main.c | 646 | ||||
-rw-r--r-- | acme/bin/source/win/mkfile | 25 | ||||
-rw-r--r-- | acme/bin/source/win/pipe.c | 175 | ||||
-rw-r--r-- | acme/bin/source/win/util.c | 90 | ||||
-rw-r--r-- | acme/bin/source/win/win.c | 264 |
9 files changed, 2239 insertions, 0 deletions
diff --git a/acme/bin/source/win/_fs.c b/acme/bin/source/win/_fs.c new file mode 100644 index 000000000..6791a21f2 --- /dev/null +++ b/acme/bin/source/win/_fs.c @@ -0,0 +1,146 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include "dat.h" + +Channel *fschan; +Channel *writechan; + +static File *devcons, *devnew; + +static void +fsread(Req *r) +{ + Fsevent e; + + if(r->fid->file == devnew){ + if(r->fid->aux==nil){ + respond(r, "phase error"); + return; + } + readstr(r, r->fid->aux); + respond(r, nil); + return; + } + + assert(r->fid->file == devcons); + e.type = 'r'; + e.r = r; + send(fschan, &e); +} + +static void +fsflush(Req *r) +{ + Fsevent e; + + e.type = 'f'; + e.r = r; + send(fschan, &e); +} + +static void +fswrite(Req *r) +{ + static Event *e[4]; + Event *ep; + int i, j, nb, wid, pid; + Rune rune; + char *s; + char tmp[UTFmax], *t; + static int n, partial; + + if(r->fid->file == devnew){ + if(r->fid->aux){ + respond(r, "already created a window"); + return; + } + s = emalloc(r->ifcall.count+1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = 0; + pid = strtol(s, &t, 0); + if(*t==' ') + t++; + i = newpipewin(pid, t); + free(s); + s = emalloc(32); + sprint(s, "%lud", (ulong)i); + r->fid->aux = s; + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + } + + assert(r->fid->file == devcons); + + if(e[0] == nil){ + for(i=0; i<nelem(e); i++){ + e[i] = emalloc(sizeof(Event)); + e[i]->c1 = 'S'; + } + } + + ep = e[n]; + n = (n+1)%nelem(e); + assert(r->ifcall.count <= 8192); /* is this guaranteed by lib9p? */ + nb = r->ifcall.count; + memmove(ep->b+partial, r->ifcall.data, nb); + nb += partial; + ep->b[nb] = '\0'; + if(strlen(ep->b) < nb){ /* nulls in data */ + t = ep->b; + for(i=j=0; i<nb; i++) + if(ep->b[i] != '\0') + t[j++] = ep->b[i]; + nb = j; + t[j] = '\0'; + } + /* process bytes into runes, transferring terminal partial runes into next buffer */ + for(i=j=0; i<nb && fullrune(ep->b+i, nb-i); i+=wid,j++) + wid = chartorune(&rune, ep->b+i); + memmove(tmp, ep->b+i, nb-i); + partial = nb-i; + ep->nb = i; + ep->nr = j; + ep->b[i] = '\0'; + if(i != 0){ + sendp(win->cevent, ep); + recvp(writechan); + } + partial = nb-i; + memmove(e[n]->b, tmp, partial); + r->ofcall.count = r->ifcall.count; + respond(r, nil); +} + +void +fsdestroyfid(Fid *fid) +{ + if(fid->aux) + free(fid->aux); +} + +Srv fs = { +.read= fsread, +.write= fswrite, +.flush= fsflush, +.destroyfid= fsdestroyfid, +.leavefdsopen= 1, +}; + +void +mountcons(void) +{ + fschan = chancreate(sizeof(Fsevent), 0); + writechan = chancreate(sizeof(void*), 0); + fs.tree = alloctree("win", "win", DMDIR|0555, nil); + devcons = createfile(fs.tree->root, "cons", "win", 0666, nil); + if(devcons == nil) + sysfatal("creating /dev/cons: %r"); + devnew = createfile(fs.tree->root, "wnew", "win", 0666, nil); + if(devnew == nil) + sysfatal("creating /dev/wnew: %r"); + threadpostmountsrv(&fs, nil, "/dev", MBEFORE); +} diff --git a/acme/bin/source/win/_main.c b/acme/bin/source/win/_main.c new file mode 100644 index 000000000..4ac9c19d5 --- /dev/null +++ b/acme/bin/source/win/_main.c @@ -0,0 +1,651 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <fcall.h> +#include <9p.h> +#include <ctype.h> +#include "dat.h" + +void mainctl(void*); +void startcmd(char *[], int*); +void stdout2body(void*); + +int debug; +int notepg; +int eraseinput; +int dirty = 0; + +Window *win; /* the main window */ + +void +usage(void) +{ + fprint(2, "usage: win [command]\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + int i, j; + char *dir, *tag, *name; + char buf[1024], **av; + + quotefmtinstall(); + rfork(RFNAMEG); + ARGBEGIN{ + case 'd': + debug = 1; + chatty9p++; + break; + case 'e': + eraseinput = 1; + break; + case 'D': +{extern int _threaddebuglevel; + _threaddebuglevel = 1<<20; +} + }ARGEND + + if(argc == 0){ + av = emalloc(3*sizeof(char*)); + av[0] = "rc"; + av[1] = "-i"; + name = getenv("sysname"); + }else{ + av = argv; + name = utfrrune(av[0], '/'); + if(name) + name++; + else + name = av[0]; + } + + if(getwd(buf, sizeof buf) == 0) + dir = "/"; + else + dir = buf; + dir = estrdup(dir); + tag = estrdup(dir); + tag = eappend(estrdup(tag), "/-", name); + win = newwindow(); + snprint(buf, sizeof buf, "%d", win->id); + putenv("winid", buf); + winname(win, tag); + wintagwrite(win, "Send Noscroll", 5+8); + threadcreate(mainctl, win, STACK); + mountcons(); + threadcreate(fsloop, nil, STACK); + startpipe(); + startcmd(av, ¬epg); + + strcpy(buf, "win"); + j = 3; + for(i=0; i<argc && j+1+strlen(argv[i])+1<sizeof buf; i++){ + strcpy(buf+j, " "); + strcpy(buf+j+1, argv[i]); + j += 1+strlen(argv[i]); + } + + ctlprint(win->ctl, "scroll"); + winsetdump(win, dir, buf); +} + +int +EQUAL(char *s, char *t) +{ + while(tolower(*s) == tolower(*t++)) + if(*s++ == '\0') + return 1; + return 0; +} + +int +command(Window *w, char *s) +{ + while(*s==' ' || *s=='\t' || *s=='\n') + s++; + if(strcmp(s, "Delete")==0){ + windel(w, 1); + threadexitsall(nil); + return 1; + } + if(strcmp(s, "Del")==0){ + if(windel(w, 0)) + threadexitsall(nil); + return 1; + } + if(EQUAL(s, "scroll")){ + ctlprint(w->ctl, "scroll\nshow"); + return 1; + } + if(EQUAL(s, "noscroll")){ + ctlprint(w->ctl, "noscroll"); + return 1; + } + return 0; +} + +static long +utfncpy(char *to, char *from, int n) +{ + char *end, *e; + + e = to+n; + if(to >= e) + return 0; + end = memccpy(to, from, '\0', e - to); + if(end == nil){ + end = e; + if(end[-1]&0x80){ + if(end-2>=to && (end[-2]&0xE0)==0xC0) + return end-to; + if(end-3>=to && (end[-3]&0xF0)==0xE0) + return end-to; + while(end>to && (*--end&0xC0)==0x80) + ; + } + }else + end--; + return end - to; +} + +/* sendinput and fsloop run in the same proc (can't interrupt each other). */ +static Req *q; +static Req **eq; +static int +__sendinput(Window *w, ulong q0, ulong q1) +{ + char *s, *t; + int n, nb, eofchar; + static int partial; + static char tmp[UTFmax]; + Req *r; + Rune rune; + + if(!q) + return 0; + + r = q; + n = 0; + if(partial){ + Partial: + nb = partial; + if(nb > r->ifcall.count) + nb = r->ifcall.count; + memmove(r->ofcall.data, tmp, nb); + if(nb!=partial) + memmove(tmp, tmp+nb, partial-nb); + partial -= nb; + q = r->aux; + if(q == nil) + eq = &q; + r->aux = nil; + r->ofcall.count = nb; + if(debug) + fprint(2, "satisfy read with partial\n"); + respond(r, nil); + return n; + } + if(q0==q1) + return 0; + s = emalloc((q1-q0)*UTFmax+1); + n = winread(w, q0, q1, s); + s[n] = '\0'; + t = strpbrk(s, "\n\004"); + if(t == nil){ + free(s); + return 0; + } + r = q; + eofchar = 0; + if(*t == '\004'){ + eofchar = 1; + *t = '\0'; + }else + *++t = '\0'; + nb = utfncpy((char*)r->ofcall.data, s, r->ifcall.count); + if(nb==0 && s<t && r->ifcall.count > 0){ + partial = utfncpy(tmp, s, UTFmax); + assert(partial > 0); + chartorune(&rune, tmp); + partial = runelen(rune); + free(s); + n = 1; + goto Partial; + } + n = utfnlen(r->ofcall.data, nb); + if(nb==strlen(s) && eofchar) + n++; + r->ofcall.count = nb; + q = r->aux; + if(q == nil) + eq = &q; + r->aux = nil; + if(debug) + fprint(2, "read returns %lud-%lud: %.*q\n", q0, q0+n, n, r->ofcall.data); + respond(r, nil); + return n; +} + +static int +_sendinput(Window *w, ulong q0, ulong *q1) +{ + char buf[32]; + int n; + + n = __sendinput(w, q0, *q1); + if(!n || !eraseinput) + return n; + /* erase q0 to q0+n */ + sprint(buf, "#%lud,#%lud", q0, q0+n); + winsetaddr(w, buf, 0); + write(w->data, buf, 0); + *q1 -= n; + return 0; +} + +int +sendinput(Window *w, ulong q0, ulong *q1) +{ + ulong n; + Req *oq; + + n = 0; + do { + oq = q; + n += _sendinput(w, q0+n, q1); + } while(q != oq); + return n; +} + +Event esendinput; +void +fsloop(void*) +{ + Fsevent e; + Req **l, *r; + + eq = &q; + memset(&esendinput, 0, sizeof esendinput); + esendinput.c1 = 'C'; + for(;;){ + while(recv(fschan, &e) == -1) + ; + r = e.r; + switch(e.type){ + case 'r': + *eq = r; + r->aux = nil; + eq = &r->aux; + /* call sendinput with hostpt and endpt */ + sendp(win->cevent, &esendinput); + break; + case 'f': + for(l=&q; *l; l=&(*l)->aux){ + if(*l == r->oldreq){ + *l = (*l)->aux; + if(*l == nil) + eq = l; + respond(r->oldreq, "interrupted"); + break; + } + } + respond(r, nil); + break; + } + } +} + +void +sendit(char *s) +{ +// char tmp[32]; + + write(win->body, s, strlen(s)); +/* + * RSC: The problem here is that other procs can call sendit, + * so we lose our single-threadedness if we call sendinput. + * In fact, we don't even have the right queue memory, + * I think that we'll get a write event from the body write above, + * and we can do the sendinput then, from our single thread. + * + * I still need to figure out how to test this assertion for + * programs that use /srv/win* + * + winselect(win, "$", 0); + seek(win->addr, 0UL, 0); + if(read(win->addr, tmp, 2*12) == 2*12) + hostpt += sendinput(win, hostpt, atol(tmp), ); + */ +} + +void +execevent(Window *w, Event *e, int (*command)(Window*, char*)) +{ + Event *ea, *e2; + int n, na, len, needfree; + char *s, *t; + + ea = nil; + e2 = nil; + if(e->flag & 2) + e2 = recvp(w->cevent); + if(e->flag & 8){ + ea = recvp(w->cevent); + na = ea->nb; + recvp(w->cevent); + }else + na = 0; + + needfree = 0; + s = e->b; + if(e->nb==0 && (e->flag&2)){ + s = e2->b; + e->q0 = e2->q0; + e->q1 = e2->q1; + e->nb = e2->nb; + } + if(e->nb==0 && e->q0<e->q1){ + /* fetch data from window */ + s = emalloc((e->q1-e->q0)*UTFmax+2); + n = winread(w, e->q0, e->q1, s); + s[n] = '\0'; + needfree = 1; + }else + if(na){ + t = emalloc(strlen(s)+1+na+2); + sprint(t, "%s %s", s, ea->b); + if(needfree) + free(s); + s = t; + needfree = 1; + } + + /* if it's a known command, do it */ + /* if it's a long message, it can't be for us anyway */ + if(!command(w, s) && s[0]!='\0'){ /* send it as typed text */ + /* if it's a built-in from the tag, send it back */ + if(e->flag & 1) + fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1); + else{ /* send text to main window */ + len = strlen(s); + if(len>0 && s[len-1]!='\n' && s[len-1]!='\004'){ + if(!needfree){ + /* if(needfree), we left room for a newline before */ + t = emalloc(len+2); + strcpy(t, s); + s = t; + needfree = 1; + } + s[len++] = '\n'; + s[len] = '\0'; + } + sendit(s); + } + } + if(needfree) + free(s); +} + +int +hasboundary(Rune *r, int nr) +{ + int i; + + for(i=0; i<nr; i++) + if(r[i]=='\n' || r[i]=='\004') + return 1; + return 0; +} + +void +mainctl(void *v) +{ + Window *w; + Event *e; + int delta, pendingS, pendingK; + ulong hostpt, endpt; + char tmp[32]; + + w = v; + proccreate(wineventproc, w, STACK); + + hostpt = 0; + endpt = 0; + winsetaddr(w, "0", 0); + pendingS = 0; + pendingK = 0; + for(;;){ + if(debug) + fprint(2, "input range %lud-%lud\n", hostpt, endpt); + e = recvp(w->cevent); + if(debug) + fprint(2, "msg: %C %C %d %d %d %d %q\n", + e->c1 ? e->c1 : ' ', e->c2 ? e->c2 : ' ', e->q0, e->q1, e->flag, e->nb, e->b); + switch(e->c1){ + default: + Unknown: + fprint(2, "unknown message %c%c\n", e->c1, e->c2); + break; + + case 'C': /* input needed for /dev/cons */ + if(pendingS) + pendingK = 1; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + + case 'S': /* output to stdout */ + sprint(tmp, "#%lud", hostpt); + winsetaddr(w, tmp, 0); + write(w->data, e->b, e->nb); + pendingS += utfnlen(e->b, e->nb); + break; + + case 'E': /* write to tag or body; body happens due to sendit */ + delta = e->q1-e->q0; + if(e->c2=='I'){ + endpt += delta; + if(e->q0 < hostpt) + hostpt += delta; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + } + if(!islower(e->c2)) + fprint(2, "win msg: %C %C %d %d %d %d %q\n", + e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b); + break; + + case 'F': /* generated by our actions (specifically case 'S' above) */ + delta = e->q1-e->q0; + if(e->c2=='D'){ + /* we know about the delete by _sendinput */ + break; + } + if(e->c2=='I'){ + pendingS -= e->q1 - e->q0; + if(pendingS < 0) + fprint(2, "win: pendingS = %d\n", pendingS); + if(e->q0 != hostpt) + fprint(2, "win: insert at %d expected %lud\n", e->q0, hostpt); + endpt += delta; + hostpt += delta; + sendp(writechan, nil); + if(pendingS == 0 && pendingK){ + pendingK = 0; + hostpt += sendinput(w, hostpt, &endpt); + } + break; + } + if(!islower(e->c2)) + fprint(2, "win msg: %C %C %d %d %d %d %q\n", + e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b); + break; + + case 'K': + delta = e->q1-e->q0; + switch(e->c2){ + case 'D': + endpt -= delta; + if(e->q1 < hostpt) + hostpt -= delta; + else if(e->q0 < hostpt) + hostpt = e->q0; + break; + case 'I': + delta = e->q1 - e->q0; + endpt += delta; + if(endpt < e->q1) /* just in case */ + endpt = e->q1; + if(e->q0 < hostpt) + hostpt += delta; + if(e->nr>0 && e->r[e->nr-1]==0x7F){ + write(notepg, "interrupt", 9); + hostpt = endpt; + break; + } + if(e->q0 >= hostpt + && hasboundary(e->r, e->nr)){ + /* + * If we are between the S message (which + * we processed by inserting text in the + * window) and the F message notifying us + * that the text has been inserted, then our + * impression of the hostpt and acme's + * may be different. This could be seen if you + * hit enter a bunch of times in a con + * session. To work around the unreliability, + * only send input if we don't have an S pending. + * The same race occurs between when a character + * is typed and when we get notice of it, but + * since characters tend to be typed at the end + * of the buffer, we don't run into it. There's + * no workaround possible for this typing race, + * since we can't tell when the user has typed + * something but we just haven't been notified. + */ + if(pendingS) + pendingK = 1; + else + hostpt += sendinput(w, hostpt, &endpt); + } + break; + } + break; + + case 'M': /* mouse */ + delta = e->q1-e->q0; + switch(e->c2){ + case 'x': + case 'X': + execevent(w, e, command); + break; + + case 'l': /* reflect all searches back to acme */ + case 'L': + if(e->flag & 2) + recvp(w->cevent); + winwriteevent(w, e); + break; + + case 'I': + endpt += delta; + if(e->q0 < hostpt) + hostpt += delta; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + + case 'D': + endpt -= delta; + if(e->q1 < hostpt) + hostpt -= delta; + else if(e->q0 < hostpt) + hostpt = e->q0; + break; + case 'd': /* modify away; we don't care */ + case 'i': + break; + + default: + goto Unknown; + } + } + } +} + +enum +{ + NARGS = 100, + NARGCHAR = 8*1024, + EXECSTACK = STACK+(NARGS+1)*sizeof(char*)+NARGCHAR +}; + +struct Exec +{ + char **argv; + Channel *cpid; +}; + +int +lookinbin(char *s) +{ + if(s[0] == '/') + return 0; + if(s[0]=='.' && s[1]=='/') + return 0; + if(s[0]=='.' && s[1]=='.' && s[2]=='/') + return 0; + return 1; +} + +/* adapted from mail. not entirely free of details from that environment */ +void +execproc(void *v) +{ + struct Exec *e; + char *cmd, **av; + Channel *cpid; + + e = v; + rfork(RFCFDG|RFNOTEG); + av = e->argv; + close(0); + open("/dev/cons", OREAD); + close(1); + open("/dev/cons", OWRITE); + dup(1, 2); + cpid = e->cpid; + free(e); + procexec(cpid, av[0], av); + if(lookinbin(av[0])){ + cmd = estrstrdup("/bin/", av[0]); + procexec(cpid, cmd, av); + } + error("can't exec %s: %r", av[0]); +} + +void +startcmd(char *argv[], int *notepg) +{ + struct Exec *e; + Channel *cpid; + char buf[64]; + int pid; + + e = emalloc(sizeof(struct Exec)); + e->argv = argv; + cpid = chancreate(sizeof(ulong), 0); + e->cpid = cpid; + sprint(buf, "/mnt/wsys/%d", win->id); + bind(buf, "/dev/acme", MREPL); + proccreate(execproc, e, EXECSTACK); + do + pid = recvul(cpid); + while(pid == -1); + sprint(buf, "/proc/%d/notepg", pid); + *notepg = open(buf, OWRITE); +} diff --git a/acme/bin/source/win/dat.h b/acme/bin/source/win/dat.h new file mode 100644 index 000000000..38d31dcf0 --- /dev/null +++ b/acme/bin/source/win/dat.h @@ -0,0 +1,95 @@ +typedef struct Fsevent Fsevent; +typedef struct Event Event; +typedef struct Message Message; +typedef struct Window Window; + +enum +{ + STACK = 8192, + NPIPEDATA = 8000, + NPIPE = NPIPEDATA+32, + /* EVENTSIZE is really 256 in acme, but we use events internally and want bigger buffers */ + EVENTSIZE = 8192, + NEVENT = 5, +}; + +struct Fsevent +{ + int type; + void *r; +}; + +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]; +}; + +struct Window +{ + /* file descriptors */ + int ctl; + int event; + int addr; + int data; + int body; + + /* event input */ + char buf[512]; + char *bufp; + int nbuf; + Event e[NEVENT]; + + int id; + int open; + Channel *cevent; +}; + +extern Window* newwindow(void); +extern int winopenfile(Window*, char*); +extern void wintagwrite(Window*, char*, int); +extern void winname(Window*, char*); +extern void winwriteevent(Window*, Event*); +extern int winread(Window*, uint, uint, char*); +extern int windel(Window*, int); +extern void wingetevent(Window*, Event*); +extern void wineventproc(void*); +extern void winclean(Window*); +extern int winselect(Window*, char*, int); +extern int winsetaddr(Window*, char*, int); +extern void windormant(Window*); +extern void winsetdump(Window*, char*, char*); + +extern void ctlprint(int, char*, ...); +extern void* emalloc(uint); +extern char* estrdup(char*); +extern char* estrstrdup(char*, char*); +extern char* egrow(char*, char*, char*); +extern char* eappend(char*, char*, char*); +extern void error(char*, ...); + +extern void startpipe(void); +extern void sendit(char*); +extern void execevent(Window *w, Event *e, int (*)(Window*, char*)); + +extern void mountcons(void); +extern void fsloop(void*); + +extern int newpipewin(int, char*); +extern void startpipe(void); +extern int pipecommand(Window*, char*); +extern void pipectl(void*); + +#pragma varargck argpos error 1 +#pragma varargck argpos ctlprint 2 + +extern Window *win; +extern Channel *fschan, *writechan; + diff --git a/acme/bin/source/win/fs.c b/acme/bin/source/win/fs.c new file mode 100644 index 000000000..6c41eb072 --- /dev/null +++ b/acme/bin/source/win/fs.c @@ -0,0 +1,147 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include "dat.h" + +Channel *fschan; +Channel *writechan; + +static File *devcons, *devnew; + +static void +fsread(Req *r) +{ + Fsevent e; + + if(r->fid->file == devnew){ + if(r->fid->aux==nil){ + respond(r, "phase error"); + return; + } + readstr(r, r->fid->aux); + respond(r, nil); + return; + } + + assert(r->fid->file == devcons); + e.type = 'r'; + e.r = r; + send(fschan, &e); +} + +static void +fsflush(Req *r) +{ + Fsevent e; + + e.type = 'f'; + e.r = r; + send(fschan, &e); +} + +static void +fswrite(Req *r) +{ + static Event *e[4]; + Event *ep; + int i, j, ei, nb, wid, pid; + Rune rune; + char *s; + char tmp[UTFmax], *t; + static int n, partial; + + if(r->fid->file == devnew){ + if(r->fid->aux){ + respond(r, "already created a window"); + return; + } + s = emalloc(r->ifcall.count+1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = 0; + pid = strtol(s, &t, 0); + if(*t==' ') + t++; + i = newpipewin(pid, t); + free(s); + s = emalloc(32); + sprint(s, "%lud", (ulong)i); + r->fid->aux = s; + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + } + + assert(r->fid->file == devcons); + + if(e[0] == nil){ + for(i=0; i<nelem(e); i++){ + e[i] = emalloc(sizeof(Event)); + e[i]->c1 = 'S'; + } + } + + ep = e[n]; + n = (n+1)%nelem(e); + assert(r->ifcall.count <= 8192); /* is this guaranteed by lib9p? */ + nb = r->ifcall.count; + memmove(ep->b+partial, r->ifcall.data, nb); + nb += partial; + ep->b[nb] = '\0'; + if(strlen(ep->b) < nb){ /* nulls in data */ + t = ep->b; + for(i=j=0; i<nb; i++) + if(ep->b[i] != '\0') + t[j++] = ep->b[i]; + nb = j; + t[j] = '\0'; + } + ei = nb>8192? 8192 : nb; + /* process bytes into runes, transferring terminal partial runes into next buffer */ + for(i=j=0; i<ei && fullrune(ep->b+i, ei-i); i+=wid,j++) + wid = chartorune(&rune, ep->b+i); + memmove(tmp, ep->b+i, nb-i); + partial = nb-i; + ep->nb = i; + ep->nr = j; + ep->b[i] = '\0'; + if(i != 0){ + sendp(win->cevent, ep); + recvp(writechan); + } + partial = nb-i; + memmove(e[n]->b, tmp, partial); + r->ofcall.count = r->ifcall.count; + respond(r, nil); +} + +void +fsdestroyfid(Fid *fid) +{ + if(fid->aux) + free(fid->aux); +} + +Srv fs = { +.read= fsread, +.write= fswrite, +.flush= fsflush, +.destroyfid= fsdestroyfid, +.leavefdsopen= 1, +}; + +void +mountcons(void) +{ + fschan = chancreate(sizeof(Fsevent), 0); + writechan = chancreate(sizeof(void*), 0); + fs.tree = alloctree("win", "win", DMDIR|0555, nil); + devcons = createfile(fs.tree->root, "cons", "win", 0666, nil); + if(devcons == nil) + sysfatal("creating /dev/cons: %r"); + devnew = createfile(fs.tree->root, "wnew", "win", 0666, nil); + if(devnew == nil) + sysfatal("creating /dev/wnew: %r"); + threadpostmountsrv(&fs, nil, "/dev", MBEFORE); +} diff --git a/acme/bin/source/win/main.c b/acme/bin/source/win/main.c new file mode 100644 index 000000000..916f2b05f --- /dev/null +++ b/acme/bin/source/win/main.c @@ -0,0 +1,646 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <fcall.h> +#include <9p.h> +#include <ctype.h> +#include "dat.h" + +void mainctl(void*); +void startcmd(char *[], int*); +void stdout2body(void*); + +int debug; +int notepg; +int eraseinput; +int dirty = 0; + +Window *win; /* the main window */ + +void +usage(void) +{ + fprint(2, "usage: win [command]\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + int i, j; + char *dir, *tag, *name; + char buf[1024], **av; + + quotefmtinstall(); + rfork(RFNAMEG); + ARGBEGIN{ + case 'd': + debug = 1; + chatty9p++; + break; + case 'e': + eraseinput = 1; + break; + case 'D': +{extern int _threaddebuglevel; + _threaddebuglevel = 1<<20; +} + }ARGEND + + if(argc == 0){ + av = emalloc(3*sizeof(char*)); + av[0] = "rc"; + av[1] = "-i"; + name = getenv("sysname"); + }else{ + av = argv; + name = utfrrune(av[0], '/'); + if(name) + name++; + else + name = av[0]; + } + + if(getwd(buf, sizeof buf) == 0) + dir = "/"; + else + dir = buf; + dir = estrdup(dir); + tag = estrdup(dir); + tag = eappend(estrdup(tag), "/-", name); + win = newwindow(); + snprint(buf, sizeof buf, "%d", win->id); + putenv("winid", buf); + winname(win, tag); + wintagwrite(win, "Send Noscroll", 5+8); + threadcreate(mainctl, win, STACK); + mountcons(); + threadcreate(fsloop, nil, STACK); + startpipe(); + startcmd(av, ¬epg); + + strcpy(buf, "win"); + j = 3; + for(i=0; i<argc && j+1+strlen(argv[i])+1<sizeof buf; i++){ + strcpy(buf+j, " "); + strcpy(buf+j+1, argv[i]); + j += 1+strlen(argv[i]); + } + + ctlprint(win->ctl, "scroll"); + winsetdump(win, dir, buf); +} + +int +EQUAL(char *s, char *t) +{ + while(tolower(*s) == tolower(*t++)) + if(*s++ == '\0') + return 1; + return 0; +} + +int +command(Window *w, char *s) +{ + while(*s==' ' || *s=='\t' || *s=='\n') + s++; + if(strcmp(s, "Delete")==0 || strcmp(s, "Del")==0){ + windel(w, 1); + threadexitsall(nil); + return 1; + } + if(EQUAL(s, "scroll")){ + ctlprint(w->ctl, "scroll\nshow"); + return 1; + } + if(EQUAL(s, "noscroll")){ + ctlprint(w->ctl, "noscroll"); + return 1; + } + return 0; +} + +static long +utfncpy(char *to, char *from, int n) +{ + char *end, *e; + + e = to+n; + if(to >= e) + return 0; + end = memccpy(to, from, '\0', e - to); + if(end == nil){ + end = e; + if(end[-1]&0x80){ + if(end-2>=to && (end[-2]&0xE0)==0xC0) + return end-to; + if(end-3>=to && (end[-3]&0xF0)==0xE0) + return end-to; + while(end>to && (*--end&0xC0)==0x80) + ; + } + }else + end--; + return end - to; +} + +/* sendinput and fsloop run in the same proc (can't interrupt each other). */ +static Req *q; +static Req **eq; +static int +__sendinput(Window *w, ulong q0, ulong q1) +{ + char *s, *t; + int n, nb, eofchar; + static int partial; + static char tmp[UTFmax]; + Req *r; + Rune rune; + + if(!q) + return 0; + + r = q; + n = 0; + if(partial){ + Partial: + nb = partial; + if(nb > r->ifcall.count) + nb = r->ifcall.count; + memmove(r->ofcall.data, tmp, nb); + if(nb!=partial) + memmove(tmp, tmp+nb, partial-nb); + partial -= nb; + q = r->aux; + if(q == nil) + eq = &q; + r->aux = nil; + r->ofcall.count = nb; + if(debug) + fprint(2, "satisfy read with partial\n"); + respond(r, nil); + return n; + } + if(q0==q1) + return 0; + s = emalloc((q1-q0)*UTFmax+1); + n = winread(w, q0, q1, s); + s[n] = '\0'; + t = strpbrk(s, "\n\004"); + if(t == nil){ + free(s); + return 0; + } + r = q; + eofchar = 0; + if(*t == '\004'){ + eofchar = 1; + *t = '\0'; + }else + *++t = '\0'; + nb = utfncpy((char*)r->ofcall.data, s, r->ifcall.count); + if(nb==0 && s<t && r->ifcall.count > 0){ + partial = utfncpy(tmp, s, UTFmax); + assert(partial > 0); + chartorune(&rune, tmp); + partial = runelen(rune); + free(s); + n = 1; + goto Partial; + } + n = utfnlen(r->ofcall.data, nb); + if(nb==strlen(s) && eofchar) + n++; + r->ofcall.count = nb; + q = r->aux; + if(q == nil) + eq = &q; + r->aux = nil; + if(debug) + fprint(2, "read returns %lud-%lud: %.*q\n", q0, q0+n, n, r->ofcall.data); + respond(r, nil); + return n; +} + +static int +_sendinput(Window *w, ulong q0, ulong *q1) +{ + char buf[32]; + int n; + + n = __sendinput(w, q0, *q1); + if(!n || !eraseinput) + return n; + /* erase q0 to q0+n */ + sprint(buf, "#%lud,#%lud", q0, q0+n); + winsetaddr(w, buf, 0); + write(w->data, buf, 0); + *q1 -= n; + return 0; +} + +int +sendinput(Window *w, ulong q0, ulong *q1) +{ + ulong n; + Req *oq; + + n = 0; + do { + oq = q; + n += _sendinput(w, q0+n, q1); + } while(q != oq); + return n; +} + +Event esendinput; +void +fsloop(void*) +{ + Fsevent e; + Req **l, *r; + + eq = &q; + memset(&esendinput, 0, sizeof esendinput); + esendinput.c1 = 'C'; + for(;;){ + while(recv(fschan, &e) == -1) + ; + r = e.r; + switch(e.type){ + case 'r': + *eq = r; + r->aux = nil; + eq = &r->aux; + /* call sendinput with hostpt and endpt */ + sendp(win->cevent, &esendinput); + break; + case 'f': + for(l=&q; *l; l=&(*l)->aux){ + if(*l == r->oldreq){ + *l = (*l)->aux; + if(*l == nil) + eq = l; + respond(r->oldreq, "interrupted"); + break; + } + } + respond(r, nil); + break; + } + } +} + +void +sendit(char *s) +{ +// char tmp[32]; + + write(win->body, s, strlen(s)); +/* + * RSC: The problem here is that other procs can call sendit, + * so we lose our single-threadedness if we call sendinput. + * In fact, we don't even have the right queue memory, + * I think that we'll get a write event from the body write above, + * and we can do the sendinput then, from our single thread. + * + * I still need to figure out how to test this assertion for + * programs that use /srv/win* + * + winselect(win, "$", 0); + seek(win->addr, 0UL, 0); + if(read(win->addr, tmp, 2*12) == 2*12) + hostpt += sendinput(win, hostpt, atol(tmp), ); + */ +} + +void +execevent(Window *w, Event *e, int (*command)(Window*, char*)) +{ + Event *ea, *e2; + int n, na, len, needfree; + char *s, *t; + + ea = nil; + e2 = nil; + if(e->flag & 2) + e2 = recvp(w->cevent); + if(e->flag & 8){ + ea = recvp(w->cevent); + na = ea->nb; + recvp(w->cevent); + }else + na = 0; + + needfree = 0; + s = e->b; + if(e->nb==0 && (e->flag&2)){ + s = e2->b; + e->q0 = e2->q0; + e->q1 = e2->q1; + e->nb = e2->nb; + } + if(e->nb==0 && e->q0<e->q1){ + /* fetch data from window */ + s = emalloc((e->q1-e->q0)*UTFmax+2); + n = winread(w, e->q0, e->q1, s); + s[n] = '\0'; + needfree = 1; + }else + if(na){ + t = emalloc(strlen(s)+1+na+2); + sprint(t, "%s %s", s, ea->b); + if(needfree) + free(s); + s = t; + needfree = 1; + } + + /* if it's a known command, do it */ + /* if it's a long message, it can't be for us anyway */ + if(!command(w, s) && s[0]!='\0'){ /* send it as typed text */ + /* if it's a built-in from the tag, send it back */ + if(e->flag & 1) + fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1); + else{ /* send text to main window */ + len = strlen(s); + if(len>0 && s[len-1]!='\n' && s[len-1]!='\004'){ + if(!needfree){ + /* if(needfree), we left room for a newline before */ + t = emalloc(len+2); + strcpy(t, s); + s = t; + needfree = 1; + } + s[len++] = '\n'; + s[len] = '\0'; + } + sendit(s); + } + } + if(needfree) + free(s); +} + +int +hasboundary(Rune *r, int nr) +{ + int i; + + for(i=0; i<nr; i++) + if(r[i]=='\n' || r[i]=='\004') + return 1; + return 0; +} + +void +mainctl(void *v) +{ + Window *w; + Event *e; + int delta, pendingS, pendingK; + ulong hostpt, endpt; + char tmp[32]; + + w = v; + proccreate(wineventproc, w, STACK); + + hostpt = 0; + endpt = 0; + winsetaddr(w, "0", 0); + pendingS = 0; + pendingK = 0; + for(;;){ + if(debug) + fprint(2, "input range %lud-%lud\n", hostpt, endpt); + e = recvp(w->cevent); + if(debug) + fprint(2, "msg: %C %C %d %d %d %d %q\n", + e->c1 ? e->c1 : ' ', e->c2 ? e->c2 : ' ', e->q0, e->q1, e->flag, e->nb, e->b); + switch(e->c1){ + default: + Unknown: + fprint(2, "unknown message %c%c\n", e->c1, e->c2); + break; + + case 'C': /* input needed for /dev/cons */ + if(pendingS) + pendingK = 1; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + + case 'S': /* output to stdout */ + sprint(tmp, "#%lud", hostpt); + winsetaddr(w, tmp, 0); + write(w->data, e->b, e->nb); + pendingS += e->nr; + break; + + case 'E': /* write to tag or body; body happens due to sendit */ + delta = e->q1-e->q0; + if(e->c2=='I'){ + endpt += delta; + if(e->q0 < hostpt) + hostpt += delta; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + } + if(!islower(e->c2)) + fprint(2, "win msg: %C %C %d %d %d %d %q\n", + e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b); + break; + + case 'F': /* generated by our actions (specifically case 'S' above) */ + delta = e->q1-e->q0; + if(e->c2=='D'){ + /* we know about the delete by _sendinput */ + break; + } + if(e->c2=='I'){ + pendingS -= e->q1 - e->q0; + if(pendingS < 0) + fprint(2, "win: pendingS = %d\n", pendingS); + if(e->q0 != hostpt) + fprint(2, "win: insert at %d expected %lud\n", e->q0, hostpt); + endpt += delta; + hostpt += delta; + sendp(writechan, nil); + if(pendingS == 0 && pendingK){ + pendingK = 0; + hostpt += sendinput(w, hostpt, &endpt); + } + break; + } + if(!islower(e->c2)) + fprint(2, "win msg: %C %C %d %d %d %d %q\n", + e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b); + break; + + case 'K': + delta = e->q1-e->q0; + switch(e->c2){ + case 'D': + endpt -= delta; + if(e->q1 < hostpt) + hostpt -= delta; + else if(e->q0 < hostpt) + hostpt = e->q0; + break; + case 'I': + delta = e->q1 - e->q0; + endpt += delta; + if(endpt < e->q1) /* just in case */ + endpt = e->q1; + if(e->q0 < hostpt) + hostpt += delta; + if(e->nr>0 && e->r[e->nr-1]==0x7F){ + write(notepg, "interrupt", 9); + hostpt = endpt; + break; + } + if(e->q0 >= hostpt + && hasboundary(e->r, e->nr)){ + /* + * If we are between the S message (which + * we processed by inserting text in the + * window) and the F message notifying us + * that the text has been inserted, then our + * impression of the hostpt and acme's + * may be different. This could be seen if you + * hit enter a bunch of times in a con + * session. To work around the unreliability, + * only send input if we don't have an S pending. + * The same race occurs between when a character + * is typed and when we get notice of it, but + * since characters tend to be typed at the end + * of the buffer, we don't run into it. There's + * no workaround possible for this typing race, + * since we can't tell when the user has typed + * something but we just haven't been notified. + */ + if(pendingS) + pendingK = 1; + else + hostpt += sendinput(w, hostpt, &endpt); + } + break; + } + break; + + case 'M': /* mouse */ + delta = e->q1-e->q0; + switch(e->c2){ + case 'x': + case 'X': + execevent(w, e, command); + break; + + case 'l': /* reflect all searches back to acme */ + case 'L': + if(e->flag & 2) + recvp(w->cevent); + winwriteevent(w, e); + break; + + case 'I': + endpt += delta; + if(e->q0 < hostpt) + hostpt += delta; + else + hostpt += sendinput(w, hostpt, &endpt); + break; + + case 'D': + endpt -= delta; + if(e->q1 < hostpt) + hostpt -= delta; + else if(e->q0 < hostpt) + hostpt = e->q0; + break; + case 'd': /* modify away; we don't care */ + case 'i': + break; + + default: + goto Unknown; + } + } + } +} + +enum +{ + NARGS = 100, + NARGCHAR = 8*1024, + EXECSTACK = STACK+(NARGS+1)*sizeof(char*)+NARGCHAR +}; + +struct Exec +{ + char **argv; + Channel *cpid; +}; + +int +lookinbin(char *s) +{ + if(s[0] == '/') + return 0; + if(s[0]=='.' && s[1]=='/') + return 0; + if(s[0]=='.' && s[1]=='.' && s[2]=='/') + return 0; + return 1; +} + +/* adapted from mail. not entirely free of details from that environment */ +void +execproc(void *v) +{ + struct Exec *e; + char *cmd, **av; + Channel *cpid; + + e = v; + rfork(RFCFDG|RFNOTEG); + av = e->argv; + close(0); + open("/dev/cons", OREAD); + close(1); + open("/dev/cons", OWRITE); + dup(1, 2); + cpid = e->cpid; + free(e); + procexec(cpid, av[0], av); + if(lookinbin(av[0])){ + cmd = estrstrdup("/bin/", av[0]); + procexec(cpid, cmd, av); + } + error("can't exec %s: %r", av[0]); +} + +void +startcmd(char *argv[], int *notepg) +{ + struct Exec *e; + Channel *cpid; + char buf[64]; + int pid; + + e = emalloc(sizeof(struct Exec)); + e->argv = argv; + cpid = chancreate(sizeof(ulong), 0); + e->cpid = cpid; + sprint(buf, "/mnt/wsys/%d", win->id); + bind(buf, "/dev/acme", MREPL); + proccreate(execproc, e, EXECSTACK); + do + pid = recvul(cpid); + while(pid == -1); + sprint(buf, "/proc/%d/notepg", pid); + *notepg = open(buf, OWRITE); +} diff --git a/acme/bin/source/win/mkfile b/acme/bin/source/win/mkfile new file mode 100644 index 000000000..608f95b86 --- /dev/null +++ b/acme/bin/source/win/mkfile @@ -0,0 +1,25 @@ +</$objtype/mkfile + +TARG=win +OFILES=\ + fs.$O\ + main.$O\ + pipe.$O\ + util.$O\ + win.$O + +HFILES=dat.h +LIB=/$objtype/lib/lib9p.a + +BIN=/acme/bin/$objtype +</sys/src/cmd/mkone + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + ${TARG:%=/acme/bin/$objtype/%}\ + +syms:V: + 8c -a main.c >syms + 8c -aa util.c win.c >>syms diff --git a/acme/bin/source/win/pipe.c b/acme/bin/source/win/pipe.c new file mode 100644 index 000000000..280f726c9 --- /dev/null +++ b/acme/bin/source/win/pipe.c @@ -0,0 +1,175 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include "dat.h" + +typedef struct Wpid Wpid; +struct Wpid +{ + int pid; + Window *w; + Wpid *next; +}; + +void pipectl(void*); + +int pipefd; +Wpid *wpid; +int snarffd; +Channel *newpipechan; + +int +newpipewin(int pid, char *p) +{ + int id; + Window *w; + Wpid *wp; + + w = newwindow(); + winname(w, p); + wintagwrite(w, "Send ", 5); + wp = emalloc(sizeof(Wpid)); + wp->pid = pid; + wp->w = w; + wp->next = wpid; /* BUG: this happens in fsread proc (we don't use wpid, so it's okay) */ + wpid = wp; + id = w->id; + sendp(newpipechan, w); + return id; +} + +int +pipecommand(Window *w, char *s) +{ + ulong q0, q1; + char tmp[32], *t; + int n, k; + + while(*s==' ' || *s=='\t' || *s=='\n') + s++; + if(strcmp(s, "Delete")==0){ + windel(w, 1); + threadexits(nil); + return 1; + } + if(strcmp(s, "Del")==0){ + if(windel(w, 0)) + threadexits(nil); + return 1; + } + if(strcmp(s, "Send") == 0){ + if(w->addr < 0) + w->addr = winopenfile(w, "addr"); + ctlprint(w->ctl, "addr=dot\n"); + seek(w->addr, 0UL, 0); + if(read(w->addr, tmp, 2*12) == 2*12){ + q0 = atol(tmp+0*12); + q1 = atol(tmp+1*12); + if(q0 == q1){ + t = nil; + k = 0; + if(snarffd > 0){ + seek(0, snarffd, 0); + for(;;){ + t = realloc(t, k+8192+2); + if(t == nil) + error("alloc failed: %r\n"); + n = read(snarffd, t+k, 8192); + if(n <= 0) + break; + k += n; + } + t[k] = 0; + } + }else{ + t = emalloc((q1-q0)*UTFmax+2); + winread(w, q0, q1, t); + k = strlen(t); + } + if(t!=nil && t[0]!='\0'){ + if(t[k-1]!='\n' && t[k-1]!='\004'){ + t[k++] = '\n'; + t[k] = '\0'; + } + sendit(t); + } + free(t); + } + return 1; + } + return 0; +} + +void +pipectl(void *v) +{ + Window *w; + Event *e; + + w = v; + proccreate(wineventproc, w, STACK); + + windormant(w); + winsetaddr(w, "0", 0); + for(;;){ + e = recvp(w->cevent); + switch(e->c1){ + default: + Unknown: + fprint(2, "unknown message %c%c\n", e->c1, e->c2); + break; + + case 'E': /* write to body; can't affect us */ + break; + + case 'F': /* generated by our actions; ignore */ + break; + + case 'K': /* ignore */ + break; + + case 'M': + switch(e->c2){ + case 'x': + case 'X': + execevent(w, e, pipecommand); + break; + + case 'l': /* reflect all searches back to acme */ + case 'L': + if(e->flag & 2) + recvp(w->cevent); + winwriteevent(w, e); + break; + + case 'I': /* modify away; we don't care */ + case 'i': + case 'D': + case 'd': + break; + + default: + goto Unknown; + } + } + } +} + +void +newpipethread(void*) +{ + Window *w; + + while(w = recvp(newpipechan)) + threadcreate(pipectl, w, STACK); +} + +void +startpipe(void) +{ + newpipechan = chancreate(sizeof(Window*), 0); + threadcreate(newpipethread, nil, STACK); + snarffd = open("/dev/snarf", OREAD|OCEXEC); +} diff --git a/acme/bin/source/win/util.c b/acme/bin/source/win/util.c new file mode 100644 index 000000000..ec8bd0c99 --- /dev/null +++ b/acme/bin/source/win/util.c @@ -0,0 +1,90 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include "dat.h" + +void* +emalloc(uint n) +{ + void *p; + + p = malloc(n); + if(p == nil) + error("can't malloc: %r"); + memset(p, 0, n); + return p; +} + +char* +estrdup(char *s) +{ + char *t; + + t = emalloc(strlen(s)+1); + strcpy(t, s); + return t; +} + +char* +estrstrdup(char *s, char *t) +{ + char *u; + + u = emalloc(strlen(s)+strlen(t)+1); + sprint(u, "%s%s", s, t); + return u; +} + +char* +eappend(char *s, char *sep, char *t) +{ + char *u; + + if(t == nil) + u = estrstrdup(s, sep); + else{ + u = emalloc(strlen(s)+strlen(sep)+strlen(t)+1); + sprint(u, "%s%s%s", s, sep, t); + } + free(s); + return u; +} + +char* +egrow(char *s, char *sep, char *t) +{ + s = eappend(s, sep, t); + free(t); + return s; +} + +void +error(char *fmt, ...) +{ + Fmt f; + char buf[64]; + va_list arg; + + fmtfdinit(&f, 2, buf, sizeof buf); + fmtprint(&f, "win: "); + va_start(arg, fmt); + fmtvprint(&f, fmt, arg); + va_end(arg); + fmtprint(&f, "\n"); + fmtfdflush(&f); + threadexitsall(fmt); +} + +void +ctlprint(int fd, char *fmt, ...) +{ + int n; + va_list arg; + + va_start(arg, fmt); + n = vfprint(fd, fmt, arg); + va_end(arg); + if(n <= 0) + error("control file write error: %r"); +} diff --git a/acme/bin/source/win/win.c b/acme/bin/source/win/win.c new file mode 100644 index 000000000..2e7f883ed --- /dev/null +++ b/acme/bin/source/win/win.c @@ -0,0 +1,264 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include "dat.h" + +Window* +newwindow(void) +{ + char buf[12]; + Window *w; + + w = emalloc(sizeof(Window)); + w->ctl = open("/mnt/wsys/new/ctl", ORDWR|OCEXEC); + if(w->ctl<0 || read(w->ctl, buf, 12)!=12) + error("can't open window ctl file: %r"); + ctlprint(w->ctl, "noscroll\n"); + w->id = atoi(buf); + w->event = winopenfile(w, "event"); + w->addr = winopenfile(w, "addr"); + w->body = winopenfile(w, "body"); + w->data = winopenfile(w, "data"); + w->cevent = chancreate(sizeof(Event*), 0); + return w; +} + +void +winsetdump(Window *w, char *dir, char *cmd) +{ + if(dir != nil) + ctlprint(w->ctl, "dumpdir %s\n", dir); + if(cmd != nil) + ctlprint(w->ctl, "dump %s\n", cmd); +} + +void +wineventproc(void *v) +{ + Window *w; + int i; + + w = v; + for(i=0; ; i++){ + if(i >= NEVENT) + i = 0; + wingetevent(w, &w->e[i]); + sendp(w->cevent, &w->e[i]); + } +} + +int +winopenfile(Window *w, char *f) +{ + char buf[64]; + int fd; + + sprint(buf, "/mnt/wsys/%d/%s", w->id, f); + fd = open(buf, ORDWR|OCEXEC); + if(fd < 0) + error("can't open window file %s: %r", f); + return fd; +} + +void +wintagwrite(Window *w, char *s, int n) +{ + int fd; + + fd = winopenfile(w, "tag"); + if(write(fd, s, n) != n) + error("tag write: %r"); + close(fd); +} + +void +winname(Window *w, char *s) +{ + ctlprint(w->ctl, "name %s\n", s); +} + +int +wingetec(Window *w) +{ + if(w->nbuf == 0){ + w->nbuf = read(w->event, w->buf, sizeof w->buf); + if(w->nbuf <= 0){ + /* probably because window has exited, and only called by wineventproc, so just shut down */ + threadexits(nil); + } + w->bufp = w->buf; + } + w->nbuf--; + return *w->bufp++; +} + +int +wingeten(Window *w) +{ + int n, c; + + n = 0; + while('0'<=(c=wingetec(w)) && c<='9') + n = n*10+(c-'0'); + if(c != ' ') + error("event number syntax"); + return n; +} + +int +wingeter(Window *w, char *buf, int *nb) +{ + Rune r; + int n; + + r = wingetec(w); + buf[0] = r; + n = 1; + if(r >= Runeself) { + while(!fullrune(buf, n)) + buf[n++] = wingetec(w); + chartorune(&r, buf); + } + *nb = n; + return r; +} + +void +wingetevent(Window *w, Event *e) +{ + int i, nb; + + e->c1 = wingetec(w); + e->c2 = wingetec(w); + e->q0 = wingeten(w); + e->q1 = wingeten(w); + e->flag = wingeten(w); + e->nr = wingeten(w); + if(e->nr > EVENTSIZE) + error("event string too long"); + e->nb = 0; + for(i=0; i<e->nr; i++){ + e->r[i] = wingeter(w, e->b+e->nb, &nb); + e->nb += nb; + } + e->r[e->nr] = 0; + e->b[e->nb] = 0; + if(wingetec(w) != '\n') + error("event syntax error"); +} + +void +winwriteevent(Window *w, Event *e) +{ + fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1); +} + +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; +} + +int +winread(Window *w, uint q0, uint q1, char *data) +{ + int m, n, nr, nb; + char buf[256]; + + if(w->addr < 0) + w->addr = winopenfile(w, "addr"); + if(w->data < 0) + w->data = winopenfile(w, "data"); + m = q0; + nb = 0; + while(m < q1){ + n = sprint(buf, "#%d", m); + if(write(w->addr, buf, n) != n) + error("error writing addr: %r"); + n = read(w->data, buf, sizeof buf); + if(n < 0) + error("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); + nb += n; + data += n; + *data = 0; + m += nr; + } + return nb; +} + +void +windormant(Window *w) +{ + if(w->addr >= 0){ + close(w->addr); + w->addr = -1; + } + if(w->body >= 0){ + close(w->body); + w->body = -1; + } + if(w->data >= 0){ + close(w->data); + w->data = -1; + } +} + +int +windel(Window *w, int sure) +{ + if(sure) + write(w->ctl, "delete\n", 7); + else if(write(w->ctl, "del\n", 4) != 4) + return 0; + /* event proc will die due to read error from event file */ + windormant(w); + close(w->ctl); + w->ctl = -1; + close(w->event); + w->event = -1; + return 1; +} + +void +winclean(Window *w) +{ + ctlprint(w->ctl, "clean\n"); +} + +int +winsetaddr(Window *w, char *addr, int errok) +{ + if(w->addr < 0) + w->addr = winopenfile(w, "addr"); + if(write(w->addr, addr, strlen(addr)) < 0){ + if(!errok) + error("error writing addr(%s): %r", addr); + return 0; + } + return 1; +} + +int +winselect(Window *w, char *addr, int errok) +{ + if(winsetaddr(w, addr, errok)){ + ctlprint(w->ctl, "dot=addr\n"); + return 1; + } + return 0; +} |