| #include <u.h> | 
 | #include <libc.h> | 
 | #include <bio.h> | 
 | #include <thread.h> | 
 | #include <plumb.h> | 
 | #include <9pclient.h> | 
 | #include "dat.h" | 
 |  | 
 | Window* | 
 | newwindow(void) | 
 | { | 
 | 	char buf[12]; | 
 | 	Window *w; | 
 |  | 
 | 	w = emalloc(sizeof(Window)); | 
 | 	w->ctl = fsopen(acmefs, "new/ctl", ORDWR|OCEXEC); | 
 | 	if(w->ctl == nil || fsread(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 = nil;	/* will be opened when needed */ | 
 | 	w->body = nil; | 
 | 	w->data = nil; | 
 | 	w->cevent = chancreate(sizeof(Event*), 0); | 
 | 	w->ref = 1; | 
 | 	return w; | 
 | } | 
 |  | 
 | void | 
 | winincref(Window *w) | 
 | { | 
 | 	qlock(&w->lk); | 
 | 	++w->ref; | 
 | 	qunlock(&w->lk); | 
 | } | 
 |  | 
 | void | 
 | windecref(Window *w) | 
 | { | 
 | 	qlock(&w->lk); | 
 | 	if(--w->ref > 0){ | 
 | 		qunlock(&w->lk); | 
 | 		return; | 
 | 	} | 
 | 	fsclose(w->event); | 
 | 	chanfree(w->cevent); | 
 | 	free(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]); | 
 | 	} | 
 | } | 
 |  | 
 | static CFid* | 
 | winopenfile1(Window *w, char *f, int m) | 
 | { | 
 | 	char buf[64]; | 
 | 	CFid* fd; | 
 |  | 
 | 	sprint(buf, "%d/%s", w->id, f); | 
 | 	fd = fsopen(acmefs, buf, m|OCEXEC); | 
 | 	if(fd == nil) | 
 | 		error("can't open window file %s: %r", f); | 
 | 	return fd; | 
 | } | 
 |  | 
 | CFid* | 
 | winopenfile(Window *w, char *f) | 
 | { | 
 | 	return winopenfile1(w, f, ORDWR); | 
 | } | 
 |  | 
 | void | 
 | wintagwrite(Window *w, char *s, int n) | 
 | { | 
 | 	CFid* fid; | 
 |  | 
 | 	fid = winopenfile(w, "tag"); | 
 | 	if(fswrite(fid, s, n) != n) | 
 | 		error("tag write: %r"); | 
 | 	fsclose(fid); | 
 | } | 
 |  | 
 | void | 
 | winname(Window *w, char *s) | 
 | { | 
 | 	ctlprint(w->ctl, "name %s\n", s); | 
 | } | 
 |  | 
 | void | 
 | winopenbody(Window *w, int mode) | 
 | { | 
 | 	char buf[256]; | 
 | 	CFid* fid; | 
 |  | 
 | 	sprint(buf, "%d/body", w->id); | 
 | 	fid = fsopen(acmefs, buf, mode|OCEXEC); | 
 | 	w->body = fid; | 
 | 	if(w->body == nil) | 
 | 		error("can't open window body file: %r"); | 
 | } | 
 |  | 
 | void | 
 | winclosebody(Window *w) | 
 | { | 
 | 	if(w->body != nil){ | 
 | 		fsclose(w->body); | 
 | 		w->body = nil; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | winwritebody(Window *w, char *s, int n) | 
 | { | 
 | 	if(w->body == nil) | 
 | 		winopenbody(w, OWRITE); | 
 | 	if(fswrite(w->body, s, n) != n) | 
 | 		error("write error to window: %r"); | 
 | } | 
 |  | 
 | int | 
 | wingetec(Window *w) | 
 | { | 
 | 	if(w->nbuf == 0){ | 
 | 		w->nbuf = fsread(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 */ | 
 | 			windecref(w); | 
 | 			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) | 
 | { | 
 | 	fsprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1); | 
 | } | 
 |  | 
 | void | 
 | winread(Window *w, uint q0, uint q1, char *data) | 
 | { | 
 | 	int m, n, nr; | 
 | 	char buf[256]; | 
 |  | 
 | 	if(w->addr == nil) | 
 | 		w->addr = winopenfile(w, "addr"); | 
 | 	if(w->data == nil) | 
 | 		w->data = winopenfile(w, "data"); | 
 | 	m = q0; | 
 | 	while(m < q1){ | 
 | 		n = sprint(buf, "#%d", m); | 
 | 		if(fswrite(w->addr, buf, n) != n) | 
 | 			error("error writing addr: %r"); | 
 | 		n = fsread(w->data, buf, sizeof buf); | 
 | 		if(n <= 0) | 
 | 			error("reading data: %r"); | 
 | 		nr = utfnlen(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 | 
 | windormant(Window *w) | 
 | { | 
 | 	if(w->addr != nil){ | 
 | 		fsclose(w->addr); | 
 | 		w->addr = nil; | 
 | 	} | 
 | 	if(w->body != nil){ | 
 | 		fsclose(w->body); | 
 | 		w->body = nil; | 
 | 	} | 
 | 	if(w->data != nil){ | 
 | 		fsclose(w->data); | 
 | 		w->data = nil; | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | int | 
 | windel(Window *w, int sure) | 
 | { | 
 | 	if(sure) | 
 | 		fswrite(w->ctl, "delete\n", 7); | 
 | 	else if(fswrite(w->ctl, "del\n", 4) != 4) | 
 | 		return 0; | 
 | 	/* event proc will die due to read error from event file */ | 
 | 	windormant(w); | 
 | 	fsclose(w->ctl); | 
 | 	w->ctl = nil; | 
 | 	return 1; | 
 | } | 
 |  | 
 | void | 
 | winclean(Window *w) | 
 | { | 
 | 	ctlprint(w->ctl, "clean\n"); | 
 | } | 
 |  | 
 | int | 
 | winsetaddr(Window *w, char *addr, int errok) | 
 | { | 
 | 	if(w->addr == nil) | 
 | 		w->addr = winopenfile(w, "addr"); | 
 | 	if(fswrite(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; | 
 | } | 
 |  | 
 | char* | 
 | winreadbody(Window *w, int *np)	/* can't use readfile because acme doesn't report the length */ | 
 | { | 
 | 	char *s; | 
 | 	int m, na, n; | 
 |  | 
 | 	if(w->body != nil) | 
 | 		winclosebody(w); | 
 | 	winopenbody(w, OREAD); | 
 | 	s = nil; | 
 | 	na = 0; | 
 | 	n = 0; | 
 | 	for(;;){ | 
 | 		if(na < n+512){ | 
 | 			na += 1024; | 
 | 			s = realloc(s, na+1); | 
 | 		} | 
 | 		m = fsread(w->body, s+n, na-n); | 
 | 		if(m <= 0) | 
 | 			break; | 
 | 		n += m; | 
 | 	} | 
 | 	s[n] = 0; | 
 | 	winclosebody(w); | 
 | 	*np = n; | 
 | 	return s; | 
 | } | 
 |  | 
 | char* | 
 | winselection(Window *w) | 
 | { | 
 | 	int m, n; | 
 | 	char *buf; | 
 | 	char tmp[256]; | 
 | 	CFid* fid; | 
 |  | 
 | 	fid = winopenfile1(w, "rdsel", OREAD); | 
 | 	if(fid == nil) | 
 | 		error("can't open rdsel: %r"); | 
 | 	n = 0; | 
 | 	buf = nil; | 
 | 	for(;;){ | 
 | 		m = fsread(fid, tmp, sizeof tmp); | 
 | 		if(m <= 0) | 
 | 			break; | 
 | 		buf = erealloc(buf, n+m+1); | 
 | 		memmove(buf+n, tmp, m); | 
 | 		n += m; | 
 | 		buf[n] = '\0'; | 
 | 	} | 
 | 	fsclose(fid); | 
 | 	return buf; | 
 | } |