|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <thread.h> | 
|  | #include <9pclient.h> | 
|  | #include <acme.h> | 
|  |  | 
|  | static CFsys *acmefs; | 
|  | static Win *windows; | 
|  | static Win *last; | 
|  |  | 
|  | static void | 
|  | mountacme(void) | 
|  | { | 
|  | if(acmefs == nil){ | 
|  | acmefs = nsmount("acme", nil); | 
|  | if(acmefs == nil) | 
|  | sysfatal("cannot mount acme: %r"); | 
|  | } | 
|  | } | 
|  |  | 
|  | Win* | 
|  | newwin(void) | 
|  | { | 
|  | CFid *fid; | 
|  | char buf[100]; | 
|  | int id, n; | 
|  |  | 
|  | mountacme(); | 
|  | fid = fsopen(acmefs, "new/ctl", ORDWR); | 
|  | if(fid == nil) | 
|  | sysfatal("open new/ctl: %r"); | 
|  | n = fsread(fid, buf, sizeof buf-1); | 
|  | if(n <= 0) | 
|  | sysfatal("read new/ctl: %r"); | 
|  | buf[n] = 0; | 
|  | id = atoi(buf); | 
|  | if(id == 0) | 
|  | sysfatal("read new/ctl: malformed message: %s", buf); | 
|  |  | 
|  | return openwin(id, fid); | 
|  | } | 
|  |  | 
|  | Win* | 
|  | openwin(int id, CFid *ctl) | 
|  | { | 
|  | char buf[100]; | 
|  | Win *w; | 
|  |  | 
|  | mountacme(); | 
|  | if(ctl == nil){ | 
|  | snprint(buf, sizeof buf, "%d/ctl", id); | 
|  | if((ctl = fsopen(acmefs, buf, ORDWR)) == nil) | 
|  | sysfatal("open %s: %r", buf); | 
|  | } | 
|  | w = emalloc(sizeof *w); | 
|  | w->id = id; | 
|  | w->ctl = ctl; | 
|  | w->next = nil; | 
|  | w->prev = last; | 
|  | if(last) | 
|  | last->next = w; | 
|  | else | 
|  | windows = w; | 
|  | last = w; | 
|  | return w; | 
|  | } | 
|  |  | 
|  | void | 
|  | winclosefiles(Win *w) | 
|  | { | 
|  | if(w->ctl){ | 
|  | fsclose(w->ctl); | 
|  | w->ctl = nil; | 
|  | } | 
|  | if(w->body){ | 
|  | fsclose(w->body); | 
|  | w->body = nil; | 
|  | } | 
|  | if(w->addr){ | 
|  | fsclose(w->addr); | 
|  | w->addr = nil; | 
|  | } | 
|  | if(w->tag){ | 
|  | fsclose(w->tag); | 
|  | w->tag = nil; | 
|  | } | 
|  | if(w->event){ | 
|  | fsclose(w->event); | 
|  | w->event = nil; | 
|  | } | 
|  | if(w->data){ | 
|  | fsclose(w->data); | 
|  | w->data = nil; | 
|  | } | 
|  | if(w->xdata){ | 
|  | fsclose(w->xdata); | 
|  | w->xdata = nil; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | winfree(Win *w) | 
|  | { | 
|  | winclosefiles(w); | 
|  | if(w->c){ | 
|  | chanfree(w->c); | 
|  | w->c = nil; | 
|  | } | 
|  | if(w->next) | 
|  | w->next->prev = w->prev; | 
|  | else | 
|  | last = w->prev; | 
|  | if(w->prev) | 
|  | w->prev->next = w->next; | 
|  | else | 
|  | windows = w->next; | 
|  | free(w); | 
|  | } | 
|  |  | 
|  | void | 
|  | windeleteall(void) | 
|  | { | 
|  | Win *w, *next; | 
|  |  | 
|  | for(w=windows; w; w=next){ | 
|  | next = w->next; | 
|  | winctl(w, "delete"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static CFid* | 
|  | wfid(Win *w, char *name) | 
|  | { | 
|  | char buf[100]; | 
|  | CFid **fid; | 
|  |  | 
|  | if(strcmp(name, "ctl") == 0) | 
|  | fid = &w->ctl; | 
|  | else if(strcmp(name, "body") == 0) | 
|  | fid = &w->body; | 
|  | else if(strcmp(name, "addr") == 0) | 
|  | fid = &w->addr; | 
|  | else if(strcmp(name, "tag") == 0) | 
|  | fid = &w->tag; | 
|  | else if(strcmp(name, "event") == 0) | 
|  | fid = &w->event; | 
|  | else if(strcmp(name, "data") == 0) | 
|  | fid = &w->data; | 
|  | else if(strcmp(name, "xdata") == 0) | 
|  | fid = &w->xdata; | 
|  | else{ | 
|  | fid = 0; | 
|  | sysfatal("bad window file name %s", name); | 
|  | } | 
|  |  | 
|  | if(*fid == nil){ | 
|  | snprint(buf, sizeof buf, "acme/%d/%s", w->id, name); | 
|  | *fid = fsopen(acmefs, buf, ORDWR); | 
|  | if(*fid == nil) | 
|  | sysfatal("open %s: %r", buf); | 
|  | } | 
|  | return *fid; | 
|  | } | 
|  |  | 
|  | int | 
|  | winopenfd(Win *w, char *name, int mode) | 
|  | { | 
|  | char buf[100]; | 
|  |  | 
|  | snprint(buf, sizeof buf, "%d/%s", w->id, name); | 
|  | return fsopenfd(acmefs, buf, mode); | 
|  | } | 
|  |  | 
|  | int | 
|  | winctl(Win *w, char *fmt, ...) | 
|  | { | 
|  | char *s; | 
|  | va_list arg; | 
|  | CFid *fid; | 
|  | int n; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | s = evsmprint(fmt, arg); | 
|  | va_end(arg); | 
|  |  | 
|  | fid = wfid(w, "ctl"); | 
|  | n = fspwrite(fid, s, strlen(s), 0); | 
|  | free(s); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int | 
|  | winname(Win *w, char *fmt, ...) | 
|  | { | 
|  | char *s; | 
|  | va_list arg; | 
|  | int n; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | s = evsmprint(fmt, arg); | 
|  | va_end(arg); | 
|  |  | 
|  | n = winctl(w, "name %s\n", s); | 
|  | free(s); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int | 
|  | winprint(Win *w, char *name, char *fmt, ...) | 
|  | { | 
|  | char *s; | 
|  | va_list arg; | 
|  | int n; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | s = evsmprint(fmt, arg); | 
|  | va_end(arg); | 
|  |  | 
|  | n = fswrite(wfid(w, name), s, strlen(s)); | 
|  | free(s); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int | 
|  | winaddr(Win *w, char *fmt, ...) | 
|  | { | 
|  | char *s; | 
|  | va_list arg; | 
|  | int n; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | s = evsmprint(fmt, arg); | 
|  | va_end(arg); | 
|  |  | 
|  | n = fswrite(wfid(w, "addr"), s, strlen(s)); | 
|  | free(s); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int | 
|  | winreadaddr(Win *w, uint *q1) | 
|  | { | 
|  | char buf[40], *p; | 
|  | uint q0; | 
|  | int n; | 
|  |  | 
|  | n = fspread(wfid(w, "addr"), buf, sizeof buf-1, 0); | 
|  | if(n <= 0) | 
|  | return -1; | 
|  | buf[n] = 0; | 
|  | q0 = strtoul(buf, &p, 10); | 
|  | if(q1) | 
|  | *q1 = strtoul(p, nil, 10); | 
|  | return q0; | 
|  | } | 
|  |  | 
|  | int | 
|  | winread(Win *w, char *file, void *a, int n) | 
|  | { | 
|  | return fsread(wfid(w, file), a, n); | 
|  | } | 
|  |  | 
|  | int | 
|  | winwrite(Win *w, char *file, void *a, int n) | 
|  | { | 
|  | return fswrite(wfid(w, file), a, n); | 
|  | } | 
|  |  | 
|  | char* | 
|  | winmread(Win *w, char *file) | 
|  | { | 
|  | char *buf; | 
|  | int n, tot, m; | 
|  |  | 
|  | m = 128; | 
|  | buf = emalloc(m+1); | 
|  | tot = 0; | 
|  | while((n = fsread(wfid(w, file), buf+tot, m-tot)) > 0){ | 
|  | tot += n; | 
|  | if(tot >= m){ | 
|  | m += 128; | 
|  | buf = erealloc(buf, m+1); | 
|  | } | 
|  | } | 
|  | if(n < 0){ | 
|  | free(buf); | 
|  | return nil; | 
|  | } | 
|  | buf[tot] = 0; | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | int | 
|  | winseek(Win *w, char *file, int n, int off) | 
|  | { | 
|  | return fsseek(wfid(w, file), n, off); | 
|  | } | 
|  |  | 
|  | int | 
|  | winwriteevent(Win *w, Event *e) | 
|  | { | 
|  | char buf[100]; | 
|  |  | 
|  | snprint(buf, sizeof buf, "%c%c%d %d \n", e->c1, e->c2, e->q0, e->q1); | 
|  | return fswrite(wfid(w, "event"), buf, strlen(buf)); | 
|  | } | 
|  |  | 
|  | int | 
|  | windel(Win *w, int sure) | 
|  | { | 
|  | return winctl(w, sure ? "delete" : "del"); | 
|  | } | 
|  |  | 
|  | int | 
|  | winfd(Win *w, char *name, int mode) | 
|  | { | 
|  | char buf[100]; | 
|  |  | 
|  | snprint(buf, sizeof buf, "acme/%d/%s", w->id, name); | 
|  | return fsopenfd(acmefs, buf, mode); | 
|  | } | 
|  |  | 
|  | static void | 
|  | error(Win *w, char *msg) | 
|  | { | 
|  | if(msg == nil) | 
|  | longjmp(w->jmp, 1); | 
|  | fprint(2, "%s: win%d: %s\n", argv0, w->id, msg); | 
|  | longjmp(w->jmp, 2); | 
|  | } | 
|  |  | 
|  | static int | 
|  | getec(Win *w, CFid *efd) | 
|  | { | 
|  | if(w->nbuf <= 0){ | 
|  | w->nbuf = fsread(efd, w->buf, sizeof w->buf); | 
|  | if(w->nbuf <= 0) | 
|  | error(w, nil); | 
|  | w->bufp = w->buf; | 
|  | } | 
|  | --w->nbuf; | 
|  | return *w->bufp++; | 
|  | } | 
|  |  | 
|  | static int | 
|  | geten(Win *w, CFid *efd) | 
|  | { | 
|  | int n, c; | 
|  |  | 
|  | n = 0; | 
|  | while('0'<=(c=getec(w,efd)) && c<='9') | 
|  | n = n*10+(c-'0'); | 
|  | if(c != ' ') | 
|  | error(w, "event number syntax"); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static int | 
|  | geter(Win *w, CFid *efd, char *buf, int *nb) | 
|  | { | 
|  | Rune r; | 
|  | int n; | 
|  |  | 
|  | r = getec(w, efd); | 
|  | buf[0] = r; | 
|  | n = 1; | 
|  | if(r < Runeself) | 
|  | goto Return; | 
|  | while(!fullrune(buf, n)) | 
|  | buf[n++] = getec(w, efd); | 
|  | chartorune(&r, buf); | 
|  | Return: | 
|  | *nb = n; | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static void | 
|  | gete(Win *w, CFid *efd, Event *e) | 
|  | { | 
|  | int i, nb; | 
|  |  | 
|  | e->c1 = getec(w, efd); | 
|  | e->c2 = getec(w, efd); | 
|  | e->q0 = geten(w, efd); | 
|  | e->q1 = geten(w, efd); | 
|  | e->flag = geten(w, efd); | 
|  | e->nr = geten(w, efd); | 
|  | if(e->nr > EVENTSIZE) | 
|  | error(w, "event string too long"); | 
|  | e->nb = 0; | 
|  | for(i=0; i<e->nr; i++){ | 
|  | /* e->r[i] = */ geter(w, efd, e->text+e->nb, &nb); | 
|  | e->nb += nb; | 
|  | } | 
|  | /* 	e->r[e->nr] = 0; */ | 
|  | e->text[e->nb] = 0; | 
|  | if(getec(w, efd) != '\n') | 
|  | error(w, "event syntax 2"); | 
|  | } | 
|  |  | 
|  | int | 
|  | winreadevent(Win *w, Event *e) | 
|  | { | 
|  | CFid *efd; | 
|  | int r; | 
|  |  | 
|  | if((r = setjmp(w->jmp)) != 0){ | 
|  | if(r == 1) | 
|  | return 0; | 
|  | return -1; | 
|  | } | 
|  | efd = wfid(w, "event"); | 
|  | gete(w, efd, e); | 
|  | e->oq0 = e->q0; | 
|  | e->oq1 = e->q1; | 
|  |  | 
|  | /* expansion */ | 
|  | if(e->flag&2){ | 
|  | gete(w, efd, &w->e2); | 
|  | if(e->q0==e->q1){ | 
|  | w->e2.oq0 = e->q0; | 
|  | w->e2.oq1 = e->q1; | 
|  | w->e2.flag = e->flag; | 
|  | *e = w->e2; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* chorded argument */ | 
|  | if(e->flag&8){ | 
|  | gete(w, efd, &w->e3);	/* arg */ | 
|  | gete(w, efd, &w->e4);	/* location */ | 
|  | strcpy(e->arg, w->e3.text); | 
|  | strcpy(e->loc, w->e4.text); | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | eventfmt(Fmt *fmt) | 
|  | { | 
|  | Event *e; | 
|  |  | 
|  | e = va_arg(fmt->args, Event*); | 
|  | return fmtprint(fmt, "%c%c %d %d %d %d %q", e->c1, e->c2, e->q0, e->q1, e->flag, e->nr, e->text); | 
|  | } | 
|  |  | 
|  | void* | 
|  | emalloc(uint n) | 
|  | { | 
|  | void *v; | 
|  |  | 
|  | v = mallocz(n, 1); | 
|  | if(v == nil) | 
|  | sysfatal("out of memory"); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | void* | 
|  | erealloc(void *v, uint n) | 
|  | { | 
|  | v = realloc(v, n); | 
|  | if(v == nil) | 
|  | sysfatal("out of memory"); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | char* | 
|  | estrdup(char *s) | 
|  | { | 
|  | s = strdup(s); | 
|  | if(s == nil) | 
|  | sysfatal("out of memory"); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | char* | 
|  | evsmprint(char *s, va_list v) | 
|  | { | 
|  | s = vsmprint(s, v); | 
|  | if(s == nil) | 
|  | sysfatal("out of memory"); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | int | 
|  | pipewinto(Win *w, char *name, int errto, char *cmd, ...) | 
|  | { | 
|  | va_list arg; | 
|  | char *p; | 
|  | int fd[3], pid; | 
|  |  | 
|  | va_start(arg, cmd); | 
|  | p = evsmprint(cmd, arg); | 
|  | va_end(arg); | 
|  | fd[0] = winfd(w, name, OREAD); | 
|  | fd[1] = dup(errto, -1); | 
|  | fd[2] = dup(errto, -1); | 
|  | pid = threadspawnl(fd, "rc", "rc", "-c", p, 0); | 
|  | free(p); | 
|  | return pid; | 
|  | } | 
|  |  | 
|  | int | 
|  | pipetowin(Win *w, char *name, int errto, char *cmd, ...) | 
|  | { | 
|  | va_list arg; | 
|  | char *p; | 
|  | int fd[3], pid; | 
|  |  | 
|  | va_start(arg, cmd); | 
|  | p = evsmprint(cmd, arg); | 
|  | va_end(arg); | 
|  | fd[0] = open("/dev/null", OREAD); | 
|  | fd[1] = winfd(w, name, OWRITE); | 
|  | if(errto == 0) | 
|  | fd[2] = dup(fd[1], -1); | 
|  | else | 
|  | fd[2] = dup(errto, -1); | 
|  | pid = threadspawnl(fd, "rc", "rc", "-c", p, 0); | 
|  | free(p); | 
|  | return pid; | 
|  | } | 
|  |  | 
|  | char* | 
|  | sysrun(char *fmt, ...) | 
|  | { | 
|  | static char buf[1025]; | 
|  | char *cmd; | 
|  | va_list arg; | 
|  | int n, fd[3], p[2], tot; | 
|  |  | 
|  | #undef pipe | 
|  | if(pipe(p) < 0) | 
|  | sysfatal("pipe: %r"); | 
|  | fd[0] = open("/dev/null", OREAD); | 
|  | fd[1] = p[1]; | 
|  | fd[2] = dup(p[1], -1); | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | cmd = evsmprint(fmt, arg); | 
|  | va_end(arg); | 
|  | threadspawnl(fd, "rc", "rc", "-Ic", cmd, 0); | 
|  |  | 
|  | tot = 0; | 
|  | while((n = read(p[0], buf+tot, sizeof buf-tot)) > 0) | 
|  | tot += n; | 
|  | close(p[0]); | 
|  | if(n < 0) | 
|  | return nil; | 
|  | free(cmd); | 
|  | if(tot == sizeof buf) | 
|  | tot--; | 
|  | buf[tot] = 0; | 
|  | while(tot > 0 && isspace(buf[tot-1])) | 
|  | tot--; | 
|  | buf[tot] = 0; | 
|  | if(tot == 0){ | 
|  | werrstr("no output"); | 
|  | return nil; | 
|  | } | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static void | 
|  | eventreader(void *v) | 
|  | { | 
|  | Event e[2]; | 
|  | Win *w; | 
|  | int i; | 
|  |  | 
|  | w = v; | 
|  | i = 0; | 
|  | for(;;){ | 
|  | if(winreadevent(w, &e[i]) <= 0) | 
|  | break; | 
|  | sendp(w->c, &e[i]); | 
|  | i = 1-i;	/* toggle */ | 
|  | } | 
|  | sendp(w->c, nil); | 
|  | threadexits(nil); | 
|  | } | 
|  |  | 
|  | Channel* | 
|  | wineventchan(Win *w) | 
|  | { | 
|  | if(w->c == nil){ | 
|  | w->c = chancreate(sizeof(Event*), 0); | 
|  | threadcreate(eventreader, w, 32*1024); | 
|  | } | 
|  | return w->c; | 
|  | } |