|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <thread.h> | 
|  | #include <fcall.h> | 
|  | #include <9pclient.h> | 
|  | #include "term.h" | 
|  |  | 
|  |  | 
|  | #define	EVENTSIZE	256 | 
|  | #define	STACK	32768 | 
|  |  | 
|  | typedef struct Event Event; | 
|  | typedef struct Q Q; | 
|  |  | 
|  | 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]; | 
|  | }; | 
|  |  | 
|  | Event blank = { | 
|  | 'M', | 
|  | 'X', | 
|  | 0, 0, 0, 1, 1, | 
|  | { ' ', 0 }, | 
|  | { ' ', 0 } | 
|  | }; | 
|  |  | 
|  | struct Q | 
|  | { | 
|  | QLock	lk; | 
|  | int		p; | 
|  | int		k; | 
|  | }; | 
|  |  | 
|  | Q	q; | 
|  |  | 
|  | CFid *eventfd; | 
|  | CFid *addrfd; | 
|  | CFid *datafd; | 
|  | CFid *ctlfd; | 
|  | /* int bodyfd; */ | 
|  |  | 
|  | char	*typing; | 
|  | int	ntypeb; | 
|  | int	ntyper; | 
|  | int	ntypebreak; | 
|  | int	debug; | 
|  | int	rcfd; | 
|  | int	cook = 1; | 
|  | int	password; | 
|  | int	israw(int); | 
|  |  | 
|  | char *name; | 
|  |  | 
|  | char **prog; | 
|  | Channel *cwait; | 
|  | int pid = -1; | 
|  |  | 
|  | int	label(char*, int); | 
|  | void	error(char*, ...); | 
|  | void	stdinproc(void*); | 
|  | void	stdoutproc(void*); | 
|  | void	type(Event*, int, CFid*, CFid*); | 
|  | void	sende(Event*, int, CFid*, CFid*, CFid*, int); | 
|  | char	*onestring(int, char**); | 
|  | int	delete(Event*); | 
|  | void	deltype(uint, uint); | 
|  | void	sendbs(int, int); | 
|  | void	runproc(void*); | 
|  |  | 
|  | int | 
|  | fsfidprint(CFid *fid, char *fmt, ...) | 
|  | { | 
|  | char buf[256]; | 
|  | va_list arg; | 
|  | int n; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | n = vsnprint(buf, sizeof buf, fmt, arg); | 
|  | va_end(arg); | 
|  | return fswrite(fid, buf, n); | 
|  | } | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprint(2, "usage: win cmd args...\n"); | 
|  | threadexitsall("usage"); | 
|  | } | 
|  |  | 
|  | void | 
|  | waitthread(void *v) | 
|  | { | 
|  | recvp(cwait); | 
|  | threadexitsall(nil); | 
|  | } | 
|  |  | 
|  | void | 
|  | hangupnote(void *a, char *msg) | 
|  | { | 
|  | if(strcmp(msg, "hangup") == 0 && pid != 0){ | 
|  | postnote(PNGROUP, pid, "hangup"); | 
|  | noted(NDFLT); | 
|  | } | 
|  | if(strstr(msg, "child")){ | 
|  | char buf[128]; | 
|  | int n; | 
|  |  | 
|  | n = awaitnohang(buf, sizeof buf-1); | 
|  | if(n > 0){ | 
|  | buf[n] = 0; | 
|  | if(atoi(buf) == pid) | 
|  | threadexitsall(0); | 
|  | } | 
|  | noted(NCONT); | 
|  | } | 
|  | noted(NDFLT); | 
|  | } | 
|  |  | 
|  | void | 
|  | threadmain(int argc, char **argv) | 
|  | { | 
|  | int fd, id; | 
|  | char buf[256]; | 
|  | char buf1[128]; | 
|  | CFsys *fs; | 
|  | char *dump; | 
|  |  | 
|  | dump = onestring(argc, argv); | 
|  |  | 
|  | ARGBEGIN{ | 
|  | case 'd': | 
|  | debug = 1; | 
|  | break; | 
|  | case 'n': | 
|  | name = EARGF(usage()); | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | }ARGEND | 
|  |  | 
|  | prog = argv; | 
|  |  | 
|  | if(name == nil){ | 
|  | if(argc > 0) | 
|  | name = argv[0]; | 
|  | else{ | 
|  | name = sysname(); | 
|  | if(name == nil) | 
|  | name = "gnot"; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * notedisable("sys: write on closed pipe"); | 
|  | * not okay to disable the note, because that | 
|  | * gets inherited by the subshell, so that something | 
|  | * as simple as "yes | sed 10q" never exits. | 
|  | * call notifyoff instead.  (is notedisable ever safe?) | 
|  | */ | 
|  | notifyoff("sys: write on closed pipe"); | 
|  |  | 
|  | noteenable("sys: child"); | 
|  | notify(hangupnote); | 
|  |  | 
|  | if((fs = nsmount("acme", "")) == 0) | 
|  | sysfatal("nsmount acme: %r"); | 
|  | ctlfd = fsopen(fs, "new/ctl", ORDWR|OCEXEC); | 
|  | if(ctlfd == 0 || fsread(ctlfd, buf, 12) != 12) | 
|  | sysfatal("ctl: %r"); | 
|  | id = atoi(buf); | 
|  | snprint(buf, sizeof buf, "%d", id); | 
|  | putenv("winid", buf); | 
|  | sprint(buf, "%d/tag", id); | 
|  | fd = fsopenfd(fs, buf, OWRITE|OCEXEC); | 
|  | write(fd, " Send", 1+4); | 
|  | close(fd); | 
|  | sprint(buf, "%d/event", id); | 
|  | eventfd = fsopen(fs, buf, ORDWR|OCEXEC); | 
|  | sprint(buf, "%d/addr", id); | 
|  | addrfd = fsopen(fs, buf, ORDWR|OCEXEC); | 
|  | sprint(buf, "%d/data", id); | 
|  | datafd = fsopen(fs, buf, ORDWR|OCEXEC); | 
|  | sprint(buf, "%d/body", id); | 
|  | /*	bodyfd = fsopenfd(fs, buf, ORDWR|OCEXEC); */ | 
|  | if(eventfd==nil || addrfd==nil || datafd==nil) | 
|  | sysfatal("data files: %r"); | 
|  | /* | 
|  | if(eventfd<0 || addrfd<0 || datafd<0 || bodyfd<0) | 
|  | sysfatal("data files: %r"); | 
|  | */ | 
|  | fsunmount(fs); | 
|  |  | 
|  | cwait = threadwaitchan(); | 
|  | threadcreate(waitthread, nil, STACK); | 
|  | pid = rcstart(argc, argv, &rcfd, nil); | 
|  | if(pid == -1) | 
|  | sysfatal("exec failed"); | 
|  |  | 
|  | getwd(buf1, sizeof buf1); | 
|  | sprint(buf, "name %s/-%s\n0\n", buf1, name); | 
|  | fswrite(ctlfd, buf, strlen(buf)); | 
|  | sprint(buf, "dumpdir %s/\n", buf1); | 
|  | fswrite(ctlfd, buf, strlen(buf)); | 
|  | sprint(buf, "dump %s\n", dump); | 
|  | fswrite(ctlfd, buf, strlen(buf)); | 
|  | sprint(buf, "scroll"); | 
|  | fswrite(ctlfd, buf, strlen(buf)); | 
|  |  | 
|  | updatewinsize(25, 80, 0, 0); | 
|  | proccreate(stdoutproc, nil, STACK); | 
|  | stdinproc(nil); | 
|  | } | 
|  |  | 
|  | void | 
|  | error(char *s, ...) | 
|  | { | 
|  | va_list arg; | 
|  |  | 
|  | if(s){ | 
|  | va_start(arg, s); | 
|  | s = vsmprint(s, arg); | 
|  | va_end(arg); | 
|  | fprint(2, "win: %s: %r\n", s); | 
|  | } | 
|  | if(pid != -1) | 
|  | postnote(PNGROUP, pid, "hangup"); | 
|  | threadexitsall(s); | 
|  | } | 
|  |  | 
|  | char* | 
|  | onestring(int argc, char **argv) | 
|  | { | 
|  | char *p; | 
|  | int i, n; | 
|  | static char buf[1024]; | 
|  |  | 
|  | if(argc == 0) | 
|  | return ""; | 
|  | p = buf; | 
|  | for(i=0; i<argc; i++){ | 
|  | n = strlen(argv[i]); | 
|  | if(p+n+1 >= buf+sizeof buf) | 
|  | break; | 
|  | memmove(p, argv[i], n); | 
|  | p += n; | 
|  | *p++ = ' '; | 
|  | } | 
|  | p[-1] = 0; | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | int | 
|  | getec(CFid *efd) | 
|  | { | 
|  | static char buf[8192]; | 
|  | static char *bufp; | 
|  | static int nbuf; | 
|  |  | 
|  | if(nbuf == 0){ | 
|  | nbuf = fsread(efd, buf, sizeof buf); | 
|  | if(nbuf <= 0) | 
|  | error(nil); | 
|  | bufp = buf; | 
|  | } | 
|  | --nbuf; | 
|  | return *bufp++; | 
|  | } | 
|  |  | 
|  | int | 
|  | geten(CFid *efd) | 
|  | { | 
|  | int n, c; | 
|  |  | 
|  | n = 0; | 
|  | while('0'<=(c=getec(efd)) && c<='9') | 
|  | n = n*10+(c-'0'); | 
|  | if(c != ' ') | 
|  | error("event number syntax"); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int | 
|  | geter(CFid *efd, char *buf, int *nb) | 
|  | { | 
|  | Rune r; | 
|  | int n; | 
|  |  | 
|  | r = getec(efd); | 
|  | buf[0] = r; | 
|  | n = 1; | 
|  | if(r < Runeself) | 
|  | goto Return; | 
|  | while(!fullrune(buf, n)) | 
|  | buf[n++] = getec(efd); | 
|  | chartorune(&r, buf); | 
|  | Return: | 
|  | *nb = n; | 
|  | return r; | 
|  | } | 
|  |  | 
|  | void | 
|  | gete(CFid *efd, Event *e) | 
|  | { | 
|  | int i, nb; | 
|  |  | 
|  | e->c1 = getec(efd); | 
|  | e->c2 = getec(efd); | 
|  | e->q0 = geten(efd); | 
|  | e->q1 = geten(efd); | 
|  | e->flag = geten(efd); | 
|  | e->nr = geten(efd); | 
|  | if(e->nr > EVENTSIZE) | 
|  | error("event string too long"); | 
|  | e->nb = 0; | 
|  | for(i=0; i<e->nr; i++){ | 
|  | e->r[i] = geter(efd, e->b+e->nb, &nb); | 
|  | e->nb += nb; | 
|  | } | 
|  | e->r[e->nr] = 0; | 
|  | e->b[e->nb] = 0; | 
|  | if(getec(efd) != '\n') | 
|  | error("event syntax 2"); | 
|  | } | 
|  |  | 
|  | 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 | 
|  | stdinproc(void *v) | 
|  | { | 
|  | CFid *cfd = ctlfd; | 
|  | CFid *efd = eventfd; | 
|  | CFid *dfd = datafd; | 
|  | CFid *afd = addrfd; | 
|  | int fd0 = rcfd; | 
|  | Event e, e2, e3, e4; | 
|  | int n; | 
|  |  | 
|  | USED(v); | 
|  |  | 
|  | for(;;){ | 
|  | if(debug) | 
|  | fprint(2, "typing[%d,%d)\n", q.p, q.p+ntyper); | 
|  | gete(efd, &e); | 
|  | if(debug) | 
|  | fprint(2, "msg %c%c q[%d,%d)... ", e.c1, e.c2, e.q0, e.q1); | 
|  | qlock(&q.lk); | 
|  | switch(e.c1){ | 
|  | default: | 
|  | Unknown: | 
|  | print("unknown message %c%c\n", e.c1, e.c2); | 
|  | break; | 
|  |  | 
|  | case 'E':	/* write to body or tag; can't affect us */ | 
|  | switch(e.c2){ | 
|  | case 'I': | 
|  | case 'D':		/* body */ | 
|  | if(debug) | 
|  | fprint(2, "shift typing %d... ", e.q1-e.q0); | 
|  | q.p += e.q1-e.q0; | 
|  | break; | 
|  |  | 
|  | case 'i': | 
|  | case 'd':		/* tag */ | 
|  | break; | 
|  |  | 
|  | default: | 
|  | goto Unknown; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'F':	/* generated by our actions; ignore */ | 
|  | break; | 
|  |  | 
|  | case 'K': | 
|  | case 'M': | 
|  | switch(e.c2){ | 
|  | case 'I': | 
|  | if(e.nr == 1 && e.r[0] == 0x7F) { | 
|  | char buf[1]; | 
|  | fsprint(addrfd, "#%ud,#%ud", e.q0, e.q1); | 
|  | fswrite(datafd, "", 0); | 
|  | buf[0] = 0x7F; | 
|  | write(fd0, buf, 1); | 
|  | break; | 
|  | } | 
|  | if(e.q0 < q.p){ | 
|  | if(debug) | 
|  | fprint(2, "shift typing %d... ", e.q1-e.q0); | 
|  | q.p += e.q1-e.q0; | 
|  | } | 
|  | else if(e.q0 <= q.p+ntyper){ | 
|  | if(debug) | 
|  | fprint(2, "type... "); | 
|  | type(&e, fd0, afd, dfd); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'D': | 
|  | n = delete(&e); | 
|  | q.p -= n; | 
|  | if(israw(fd0) && e.q1 >= q.p+n) | 
|  | sendbs(fd0, n); | 
|  | break; | 
|  |  | 
|  | case 'x': | 
|  | case 'X': | 
|  | if(e.flag & 2) | 
|  | gete(efd, &e2); | 
|  | if(e.flag & 8){ | 
|  | gete(efd, &e3); | 
|  | gete(efd, &e4); | 
|  | } | 
|  | if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){ | 
|  | /* send it straight back */ | 
|  | fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1); | 
|  | break; | 
|  | } | 
|  | if(e.q0==e.q1 && (e.flag&2)){ | 
|  | e2.flag = e.flag; | 
|  | e = e2; | 
|  | } | 
|  | char buf[100]; | 
|  | snprint(buf, sizeof buf, "%.*S", e.nr, e.r); | 
|  | if(cistrcmp(buf, "cook") == 0) { | 
|  | cook = 1; | 
|  | break; | 
|  | } | 
|  | if(cistrcmp(buf, "nocook") == 0) { | 
|  | cook = 0; | 
|  | break; | 
|  | } | 
|  | if(e.flag & 8){ | 
|  | if(e.q1 != e.q0){ | 
|  | sende(&e, fd0, cfd, afd, dfd, 0); | 
|  | sende(&blank, fd0, cfd, afd, dfd, 0); | 
|  | } | 
|  | sende(&e3, fd0, cfd, afd, dfd, 1); | 
|  | }else	 if(e.q1 != e.q0) | 
|  | sende(&e, fd0, cfd, afd, dfd, 1); | 
|  | break; | 
|  |  | 
|  | case 'l': | 
|  | case 'L': | 
|  | /* just send it back */ | 
|  | if(e.flag & 2) | 
|  | gete(efd, &e2); | 
|  | fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1); | 
|  | break; | 
|  |  | 
|  | case 'd': | 
|  | case 'i': | 
|  | break; | 
|  |  | 
|  | default: | 
|  | goto Unknown; | 
|  | } | 
|  | } | 
|  | qunlock(&q.lk); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | stdoutproc(void *v) | 
|  | { | 
|  | int fd1 = rcfd; | 
|  | CFid *afd = addrfd; | 
|  | CFid *dfd = datafd; | 
|  | int n, m, w, npart; | 
|  | char *buf, *s, *t; | 
|  | Rune r; | 
|  | char x[16], hold[UTFmax]; | 
|  |  | 
|  | USED(v); | 
|  | buf = malloc(8192+UTFmax+1); | 
|  | npart = 0; | 
|  | for(;;){ | 
|  | /* Let typing have a go -- maybe there's a rubout waiting. */ | 
|  | yield(); | 
|  | n = read(fd1, buf+npart, 8192); | 
|  | if(n <= 0) | 
|  | error(nil); | 
|  |  | 
|  | n = echocancel(buf+npart, n); | 
|  | if(n == 0) | 
|  | continue; | 
|  |  | 
|  | n = dropcrnl(buf+npart, n); | 
|  | if(n == 0) | 
|  | continue; | 
|  |  | 
|  | /* squash NULs */ | 
|  | s = memchr(buf+npart, 0, n); | 
|  | if(s){ | 
|  | for(t=s; s<buf+npart+n; s++) | 
|  | if(*t = *s)	/* assign = */ | 
|  | t++; | 
|  | n = t-(buf+npart); | 
|  | } | 
|  |  | 
|  | n += npart; | 
|  |  | 
|  | /* hold on to final partial rune */ | 
|  | npart = 0; | 
|  | while(n>0 && (buf[n-1]&0xC0)){ | 
|  | --n; | 
|  | npart++; | 
|  | if((buf[n]&0xC0)!=0x80){ | 
|  | if(fullrune(buf+n, npart)){ | 
|  | w = chartorune(&r, buf+n); | 
|  | n += w; | 
|  | npart -= w; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | if(n > 0){ | 
|  | memmove(hold, buf+n, npart); | 
|  | buf[n] = 0; | 
|  | n = label(buf, n); | 
|  | buf[n] = 0; | 
|  |  | 
|  | // clumsy but effective: notice password | 
|  | // prompts so we can disable echo. | 
|  | password = 0; | 
|  | if(cistrstr(buf, "password") || cistrstr(buf, "passphrase")) { | 
|  | int i; | 
|  |  | 
|  | i = n; | 
|  | while(i > 0 && buf[i-1] == ' ') | 
|  | i--; | 
|  | password = i > 0 && buf[i-1] == ':'; | 
|  | } | 
|  |  | 
|  | qlock(&q.lk); | 
|  | m = sprint(x, "#%d", q.p); | 
|  | if(fswrite(afd, x, m) != m){ | 
|  | fprint(2, "stdout writing address %s: %r; resetting\n", x); | 
|  | if(fswrite(afd, "$", 1) < 0) | 
|  | fprint(2, "reset: %r\n"); | 
|  | fsseek(afd, 0, 0); | 
|  | m = fsread(afd, x, sizeof x-1); | 
|  | if(m >= 0){ | 
|  | x[m] = 0; | 
|  | q.p = atoi(x); | 
|  | } | 
|  | } | 
|  | if(fswrite(dfd, buf, n) != n) | 
|  | error("stdout writing body"); | 
|  | /* Make sure acme scrolls to the end of the above write. */ | 
|  | if(fswrite(dfd, nil, 0) != 0) | 
|  | error("stdout flushing body"); | 
|  | q.p += nrunes(buf, n); | 
|  | qunlock(&q.lk); | 
|  | memmove(buf, hold, npart); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | char wdir[512]; | 
|  | int | 
|  | label(char *sr, int n) | 
|  | { | 
|  | char *sl, *el, *er, *r, *p; | 
|  |  | 
|  | er = sr+n; | 
|  | for(r=er-1; r>=sr; r--) | 
|  | if(*r == '\007') | 
|  | break; | 
|  | if(r < sr) | 
|  | return n; | 
|  |  | 
|  | el = r+1; | 
|  | if(el-sr > sizeof wdir - strlen(name) - 20) | 
|  | sr = el - sizeof wdir - strlen(name) - 20; | 
|  | for(sl=el-3; sl>=sr; sl--) | 
|  | if(sl[0]=='\033' && sl[1]==']' && sl[2]==';') | 
|  | break; | 
|  | if(sl < sr) | 
|  | return n; | 
|  |  | 
|  | *r = 0; | 
|  | if(strcmp(sl+3, "*9term-hold+") != 0) { | 
|  | /* | 
|  | * add /-sysname if not present | 
|  | */ | 
|  | snprint(wdir, sizeof wdir, "name %s", sl+3); | 
|  | p = strrchr(wdir, '/'); | 
|  | if(p==nil || *(p+1) != '-'){ | 
|  | p = wdir+strlen(wdir); | 
|  | if(*(p-1) != '/') | 
|  | *p++ = '/'; | 
|  | *p++ = '-'; | 
|  | strcpy(p, name); | 
|  | } | 
|  | strcat(wdir, "\n0\n"); | 
|  | fswrite(ctlfd, wdir, strlen(wdir)); | 
|  | } | 
|  |  | 
|  | memmove(sl, el, er-el); | 
|  | n -= (el-sl); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int | 
|  | delete(Event *e) | 
|  | { | 
|  | uint q0, q1; | 
|  | int deltap; | 
|  |  | 
|  | q0 = e->q0; | 
|  | q1 = e->q1; | 
|  | if(q1 <= q.p) | 
|  | return e->q1-e->q0; | 
|  | if(q0 >= q.p+ntyper) | 
|  | return 0; | 
|  | deltap = 0; | 
|  | if(q0 < q.p){ | 
|  | deltap = q.p-q0; | 
|  | q0 = 0; | 
|  | }else | 
|  | q0 -= q.p; | 
|  | if(q1 > q.p+ntyper) | 
|  | q1 = ntyper; | 
|  | else | 
|  | q1 -= q.p; | 
|  | deltype(q0, q1); | 
|  | return deltap; | 
|  | } | 
|  |  | 
|  | void | 
|  | addtype(int c, uint p0, char *b, int nb, int nr) | 
|  | { | 
|  | int i, w; | 
|  | Rune r; | 
|  | uint p; | 
|  | char *b0; | 
|  |  | 
|  | for(i=0; i<nb; i+=w){ | 
|  | w = chartorune(&r, b+i); | 
|  | if((r==0x7F||r==3) && c=='K'){ | 
|  | write(rcfd, "\x7F", 1); | 
|  | /* toss all typing */ | 
|  | q.p += ntyper+nr; | 
|  | ntypebreak = 0; | 
|  | ntypeb = 0; | 
|  | ntyper = 0; | 
|  | /* buglet:  more than one delete ignored */ | 
|  | return; | 
|  | } | 
|  | if(r=='\n' || r==0x04) | 
|  | ntypebreak++; | 
|  | } | 
|  | typing = realloc(typing, ntypeb+nb); | 
|  | if(typing == nil) | 
|  | error("realloc"); | 
|  | if(p0 == ntyper) | 
|  | memmove(typing+ntypeb, b, nb); | 
|  | else{ | 
|  | b0 = typing; | 
|  | for(p=0; p<p0 && b0<typing+ntypeb; p++){ | 
|  | w = chartorune(&r, b0+i); | 
|  | b0 += w; | 
|  | } | 
|  | if(p != p0) | 
|  | error("typing: findrune"); | 
|  | memmove(b0+nb, b0, (typing+ntypeb)-b0); | 
|  | memmove(b0, b, nb); | 
|  | } | 
|  | ntypeb += nb; | 
|  | ntyper += nr; | 
|  | } | 
|  |  | 
|  | int | 
|  | israw(int fd0) | 
|  | { | 
|  | return (!cook || password) && !isecho(fd0); | 
|  | } | 
|  |  | 
|  | void | 
|  | sendtype(int fd0) | 
|  | { | 
|  | int i, n, nr, raw; | 
|  |  | 
|  | raw = israw(fd0); | 
|  | while(ntypebreak || (raw && ntypeb > 0)){ | 
|  | for(i=0; i<ntypeb; i++) | 
|  | if(typing[i]=='\n' || typing[i]==0x04 || (i==ntypeb-1 && raw)){ | 
|  | if((typing[i] == '\n' || typing[i] == 0x04) && ntypebreak > 0) | 
|  | ntypebreak--; | 
|  | n = i+1; | 
|  | i++; | 
|  | if(!raw) | 
|  | echoed(typing, n); | 
|  | if(write(fd0, typing, n) != n) | 
|  | error("sending to program"); | 
|  | nr = nrunes(typing, i); | 
|  | q.p += nr; | 
|  | ntyper -= nr; | 
|  | ntypeb -= i; | 
|  | memmove(typing, typing+i, ntypeb); | 
|  | goto cont2; | 
|  | } | 
|  | print("no breakchar\n"); | 
|  | ntypebreak = 0; | 
|  | cont2:; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | sendbs(int fd0, int n) | 
|  | { | 
|  | char buf[128]; | 
|  | int m; | 
|  |  | 
|  | memset(buf, 0x08, sizeof buf); | 
|  | while(n > 0) { | 
|  | m = sizeof buf; | 
|  | if(m > n) | 
|  | m = n; | 
|  | n -= m; | 
|  | write(fd0, buf, m); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | deltype(uint p0, uint p1) | 
|  | { | 
|  | int w; | 
|  | uint p, b0, b1; | 
|  | Rune r; | 
|  |  | 
|  | /* advance to p0 */ | 
|  | b0 = 0; | 
|  | for(p=0; p<p0 && b0<ntypeb; p++){ | 
|  | w = chartorune(&r, typing+b0); | 
|  | b0 += w; | 
|  | } | 
|  | if(p != p0) | 
|  | error("deltype 1"); | 
|  | /* advance to p1 */ | 
|  | b1 = b0; | 
|  | for(; p<p1 && b1<ntypeb; p++){ | 
|  | w = chartorune(&r, typing+b1); | 
|  | b1 += w; | 
|  | if(r=='\n' || r==0x04) | 
|  | ntypebreak--; | 
|  | } | 
|  | if(p != p1) | 
|  | error("deltype 2"); | 
|  | memmove(typing+b0, typing+b1, ntypeb-b1); | 
|  | ntypeb -= b1-b0; | 
|  | ntyper -= p1-p0; | 
|  | } | 
|  |  | 
|  | void | 
|  | type(Event *e, int fd0, CFid *afd, CFid *dfd) | 
|  | { | 
|  | int m, n, nr; | 
|  | char buf[128]; | 
|  |  | 
|  | if(e->nr > 0) | 
|  | addtype(e->c1, e->q0-q.p, e->b, e->nb, e->nr); | 
|  | else{ | 
|  | m = e->q0; | 
|  | while(m < e->q1){ | 
|  | n = sprint(buf, "#%d", m); | 
|  | fswrite(afd, buf, n); | 
|  | n = fsread(dfd, buf, sizeof buf); | 
|  | nr = nrunes(buf, n); | 
|  | while(m+nr > e->q1){ | 
|  | do; while(n>0 && (buf[--n]&0xC0)==0x80); | 
|  | --nr; | 
|  | } | 
|  | if(n == 0) | 
|  | break; | 
|  | addtype(e->c1, m-q.p, buf, n, nr); | 
|  | m += nr; | 
|  | } | 
|  | } | 
|  | if(israw(fd0)) { | 
|  | n = sprint(buf, "#%d,#%d", e->q0, e->q1); | 
|  | fswrite(afd, buf, n); | 
|  | fswrite(dfd, "", 0); | 
|  | q.p -= e->q1 - e->q0; | 
|  | } | 
|  | sendtype(fd0); | 
|  | if(e->nb > 0 && e->b[e->nb-1] == '\n') | 
|  | cook = 1; | 
|  | } | 
|  |  | 
|  | void | 
|  | sende(Event *e, int fd0, CFid *cfd, CFid *afd, CFid *dfd, int donl) | 
|  | { | 
|  | int l, m, n, nr, lastc, end; | 
|  | char abuf[16], buf[128]; | 
|  |  | 
|  | end = q.p+ntyper; | 
|  | l = sprint(abuf, "#%d", end); | 
|  | fswrite(afd, abuf, l); | 
|  | if(e->nr > 0){ | 
|  | fswrite(dfd, e->b, e->nb); | 
|  | addtype(e->c1, ntyper, e->b, e->nb, e->nr); | 
|  | lastc = e->r[e->nr-1]; | 
|  | }else{ | 
|  | m = e->q0; | 
|  | lastc = 0; | 
|  | while(m < e->q1){ | 
|  | n = sprint(buf, "#%d", m); | 
|  | fswrite(afd, buf, n); | 
|  | n = fsread(dfd, buf, sizeof buf); | 
|  | nr = nrunes(buf, n); | 
|  | while(m+nr > e->q1){ | 
|  | do; while(n>0 && (buf[--n]&0xC0)==0x80); | 
|  | --nr; | 
|  | } | 
|  | if(n == 0) | 
|  | break; | 
|  | l = sprint(abuf, "#%d", end); | 
|  | fswrite(afd, abuf, l); | 
|  | fswrite(dfd, buf, n); | 
|  | addtype(e->c1, ntyper, buf, n, nr); | 
|  | lastc = buf[n-1]; | 
|  | m += nr; | 
|  | end += nr; | 
|  | } | 
|  | } | 
|  | if(donl && lastc!='\n'){ | 
|  | fswrite(dfd, "\n", 1); | 
|  | addtype(e->c1, ntyper, "\n", 1, 1); | 
|  | } | 
|  | fswrite(cfd, "dot=addr", 8); | 
|  | sendtype(fd0); | 
|  | } |