#include #include #include #include <9pclient.h> #include 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; inr; 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; }