plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

win.c (15339B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <thread.h>
      4 #include <fcall.h>
      5 #include <9pclient.h>
      6 #include "term.h"
      7 
      8 const char *termprog = "win";
      9 
     10 #define	EVENTSIZE	256
     11 #define	STACK	32768
     12 
     13 typedef struct Event Event;
     14 typedef struct Q Q;
     15 
     16 struct Event
     17 {
     18 	int	c1;
     19 	int	c2;
     20 	int	q0;
     21 	int	q1;
     22 	int	flag;
     23 	int	nb;
     24 	int	nr;
     25 	char	b[EVENTSIZE*UTFmax+1];
     26 	Rune	r[EVENTSIZE+1];
     27 };
     28 
     29 Event blank = {
     30 	'M',
     31 	'X',
     32 	0, 0, 0, 1, 1,
     33 	{ ' ', 0 },
     34 	{ ' ', 0 }
     35 };
     36 
     37 struct Q
     38 {
     39 	QLock	lk;
     40 	int		p;
     41 	int		k;
     42 };
     43 
     44 Q	q;
     45 
     46 CFid *eventfd;
     47 CFid *addrfd;
     48 CFid *datafd;
     49 CFid *ctlfd;
     50 /* int bodyfd; */
     51 
     52 char	*typing;
     53 int	ntypeb;
     54 int	ntyper;
     55 int	ntypebreak;
     56 int	debug;
     57 int	rcfd;
     58 int	cook = 1;
     59 int	password;
     60 int	israw(int);
     61 
     62 char *name;
     63 
     64 char **prog;
     65 Channel *cwait;
     66 int pid = -1;
     67 
     68 int	label(char*, int);
     69 void	error(char*, ...);
     70 void	stdinproc(void*);
     71 void	stdoutproc(void*);
     72 void	type(Event*, int, CFid*, CFid*);
     73 void	sende(Event*, int, CFid*, CFid*, CFid*, int);
     74 char	*onestring(int, char**);
     75 int	delete(Event*);
     76 void	deltype(uint, uint);
     77 void	sendbs(int, int);
     78 void	runproc(void*);
     79 
     80 int
     81 fsfidprint(CFid *fid, char *fmt, ...)
     82 {
     83 	char buf[256];
     84 	va_list arg;
     85 	int n;
     86 
     87 	va_start(arg, fmt);
     88 	n = vsnprint(buf, sizeof buf, fmt, arg);
     89 	va_end(arg);
     90 	return fswrite(fid, buf, n);
     91 }
     92 
     93 void
     94 usage(void)
     95 {
     96 	fprint(2, "usage: win cmd args...\n");
     97 	threadexitsall("usage");
     98 }
     99 
    100 void
    101 waitthread(void *v)
    102 {
    103 	recvp(cwait);
    104 	threadexitsall(nil);
    105 }
    106 
    107 void
    108 hangupnote(void *a, char *msg)
    109 {
    110 	if(strcmp(msg, "hangup") == 0 && pid != 0){
    111 		postnote(PNGROUP, pid, "hangup");
    112 		noted(NDFLT);
    113 	}
    114 	if(strstr(msg, "child")){
    115 		char buf[128];
    116 		int n;
    117 
    118 		n = awaitnohang(buf, sizeof buf-1);
    119 		if(n > 0){
    120 			buf[n] = 0;
    121 			if(atoi(buf) == pid)
    122 				threadexitsall(0);
    123 		}
    124 		noted(NCONT);
    125 	}
    126 	noted(NDFLT);
    127 }
    128 
    129 void
    130 threadmain(int argc, char **argv)
    131 {
    132 	int fd, id;
    133 	char buf[256];
    134 	char buf1[128];
    135 	CFsys *fs;
    136 	char *dump;
    137 
    138 	dump = onestring(argc, argv);
    139 
    140 	ARGBEGIN{
    141 	case 'd':
    142 		debug = 1;
    143 		break;
    144 	case 'n':
    145 		name = EARGF(usage());
    146 		break;
    147 	default:
    148 		usage();
    149 	}ARGEND
    150 
    151 	prog = argv;
    152 
    153 	if(name == nil){
    154 		if(argc > 0)
    155 			name = argv[0];
    156 		else{
    157 			name = sysname();
    158 			if(name == nil)
    159 				name = "gnot";
    160 		}
    161 	}
    162 
    163 	/*
    164 	 * notedisable("sys: write on closed pipe");
    165 	 * not okay to disable the note, because that
    166 	 * gets inherited by the subshell, so that something
    167 	 * as simple as "yes | sed 10q" never exits.
    168 	 * call notifyoff instead.  (is notedisable ever safe?)
    169 	 */
    170 	notifyoff("sys: write on closed pipe");
    171 
    172 	noteenable("sys: child");
    173 	notify(hangupnote);
    174 
    175 	if((fs = nsmount("acme", "")) == 0)
    176 		sysfatal("nsmount acme: %r");
    177 	ctlfd = fsopen(fs, "new/ctl", ORDWR|OCEXEC);
    178 	if(ctlfd == 0 || fsread(ctlfd, buf, 12) != 12)
    179 		sysfatal("ctl: %r");
    180 	id = atoi(buf);
    181 	snprint(buf, sizeof buf, "%d", id);
    182 	putenv("winid", buf);
    183 	sprint(buf, "%d/tag", id);
    184 	fd = fsopenfd(fs, buf, OWRITE|OCEXEC);
    185 	write(fd, " Send", 1+4);
    186 	close(fd);
    187 	sprint(buf, "%d/event", id);
    188 	eventfd = fsopen(fs, buf, ORDWR|OCEXEC);
    189 	sprint(buf, "%d/addr", id);
    190 	addrfd = fsopen(fs, buf, ORDWR|OCEXEC);
    191 	sprint(buf, "%d/data", id);
    192 	datafd = fsopen(fs, buf, ORDWR|OCEXEC);
    193 	sprint(buf, "%d/body", id);
    194 /*	bodyfd = fsopenfd(fs, buf, ORDWR|OCEXEC); */
    195 	if(eventfd==nil || addrfd==nil || datafd==nil)
    196 		sysfatal("data files: %r");
    197 /*
    198 	if(eventfd<0 || addrfd<0 || datafd<0 || bodyfd<0)
    199 		sysfatal("data files: %r");
    200 */
    201 	fsunmount(fs);
    202 
    203 	cwait = threadwaitchan();
    204 	threadcreate(waitthread, nil, STACK);
    205 	pid = rcstart(argc, argv, &rcfd, nil);
    206 	if(pid == -1)
    207 		sysfatal("exec failed");
    208 
    209 	getwd(buf1, sizeof buf1);
    210 	sprint(buf, "name %s/-%s\n0\n", buf1, name);
    211 	fswrite(ctlfd, buf, strlen(buf));
    212 	sprint(buf, "dumpdir %s/\n", buf1);
    213 	fswrite(ctlfd, buf, strlen(buf));
    214 	sprint(buf, "dump %s\n", dump);
    215 	fswrite(ctlfd, buf, strlen(buf));
    216 	sprint(buf, "scroll");
    217 	fswrite(ctlfd, buf, strlen(buf));
    218 
    219 	updatewinsize(25, 80, 0, 0);
    220 	proccreate(stdoutproc, nil, STACK);
    221 	stdinproc(nil);
    222 }
    223 
    224 void
    225 error(char *s, ...)
    226 {
    227 	va_list arg;
    228 
    229 	if(s){
    230 		va_start(arg, s);
    231 		s = vsmprint(s, arg);
    232 		va_end(arg);
    233 		fprint(2, "win: %s: %r\n", s);
    234 	}
    235 	if(pid != -1)
    236 		postnote(PNGROUP, pid, "hangup");
    237 	threadexitsall(s);
    238 }
    239 
    240 char*
    241 onestring(int argc, char **argv)
    242 {
    243 	char *p;
    244 	int i, n;
    245 	static char buf[1024];
    246 
    247 	if(argc == 0)
    248 		return "";
    249 	p = buf;
    250 	for(i=0; i<argc; i++){
    251 		n = strlen(argv[i]);
    252 		if(p+n+1 >= buf+sizeof buf)
    253 			break;
    254 		memmove(p, argv[i], n);
    255 		p += n;
    256 		*p++ = ' ';
    257 	}
    258 	p[-1] = 0;
    259 	return buf;
    260 }
    261 
    262 int
    263 getec(CFid *efd)
    264 {
    265 	static char buf[8192];
    266 	static char *bufp;
    267 	static int nbuf;
    268 
    269 	if(nbuf == 0){
    270 		nbuf = fsread(efd, buf, sizeof buf);
    271 		if(nbuf <= 0)
    272 			error(nil);
    273 		bufp = buf;
    274 	}
    275 	--nbuf;
    276 	return *bufp++;
    277 }
    278 
    279 int
    280 geten(CFid *efd)
    281 {
    282 	int n, c;
    283 
    284 	n = 0;
    285 	while('0'<=(c=getec(efd)) && c<='9')
    286 		n = n*10+(c-'0');
    287 	if(c != ' ')
    288 		error("event number syntax");
    289 	return n;
    290 }
    291 
    292 int
    293 geter(CFid *efd, char *buf, int *nb)
    294 {
    295 	Rune r;
    296 	int n;
    297 
    298 	r = getec(efd);
    299 	buf[0] = r;
    300 	n = 1;
    301 	if(r < Runeself)
    302 		goto Return;
    303 	while(!fullrune(buf, n))
    304 		buf[n++] = getec(efd);
    305 	chartorune(&r, buf);
    306     Return:
    307 	*nb = n;
    308 	return r;
    309 }
    310 
    311 void
    312 gete(CFid *efd, Event *e)
    313 {
    314 	int i, nb;
    315 
    316 	e->c1 = getec(efd);
    317 	e->c2 = getec(efd);
    318 	e->q0 = geten(efd);
    319 	e->q1 = geten(efd);
    320 	e->flag = geten(efd);
    321 	e->nr = geten(efd);
    322 	if(e->nr > EVENTSIZE)
    323 		error("event string too long");
    324 	e->nb = 0;
    325 	for(i=0; i<e->nr; i++){
    326 		e->r[i] = geter(efd, e->b+e->nb, &nb);
    327 		e->nb += nb;
    328 	}
    329 	e->r[e->nr] = 0;
    330 	e->b[e->nb] = 0;
    331 	if(getec(efd) != '\n')
    332 		error("event syntax 2");
    333 }
    334 
    335 int
    336 nrunes(char *s, int nb)
    337 {
    338 	int i, n;
    339 	Rune r;
    340 
    341 	n = 0;
    342 	for(i=0; i<nb; n++)
    343 		i += chartorune(&r, s+i);
    344 	return n;
    345 }
    346 
    347 void
    348 stdinproc(void *v)
    349 {
    350 	CFid *cfd = ctlfd;
    351 	CFid *efd = eventfd;
    352 	CFid *dfd = datafd;
    353 	CFid *afd = addrfd;
    354 	int fd0 = rcfd;
    355 	Event e, e2, e3, e4;
    356 	int n;
    357 
    358 	USED(v);
    359 
    360 	for(;;){
    361 		if(debug)
    362 			fprint(2, "typing[%d,%d)\n", q.p, q.p+ntyper);
    363 		gete(efd, &e);
    364 		if(debug)
    365 			fprint(2, "msg %c%c q[%d,%d)... ", e.c1, e.c2, e.q0, e.q1);
    366 		qlock(&q.lk);
    367 		switch(e.c1){
    368 		default:
    369 		Unknown:
    370 			print("unknown message %c%c\n", e.c1, e.c2);
    371 			break;
    372 
    373 		case 'E':	/* write to body or tag; can't affect us */
    374 			switch(e.c2){
    375 			case 'I':
    376 			case 'D':		/* body */
    377 				if(debug)
    378 					fprint(2, "shift typing %d... ", e.q1-e.q0);
    379 				q.p += e.q1-e.q0;
    380 				break;
    381 
    382 			case 'i':
    383 			case 'd':		/* tag */
    384 				break;
    385 
    386 			default:
    387 				goto Unknown;
    388 			}
    389 			break;
    390 
    391 		case 'F':	/* generated by our actions; ignore */
    392 			break;
    393 
    394 		case 'K':
    395 		case 'M':
    396 			switch(e.c2){
    397 			case 'I':
    398 				if(e.nr == 1 && e.r[0] == 0x7F) {
    399 					char buf[1];
    400 					fsprint(addrfd, "#%ud,#%ud", e.q0, e.q1);
    401 					fswrite(datafd, "", 0);
    402 					buf[0] = 0x7F;
    403 					write(fd0, buf, 1);
    404 					break;
    405 				}
    406 				if(e.q0 < q.p){
    407 					if(debug)
    408 						fprint(2, "shift typing %d... ", e.q1-e.q0);
    409 					q.p += e.q1-e.q0;
    410 				}
    411 				else if(e.q0 <= q.p+ntyper){
    412 					if(debug)
    413 						fprint(2, "type... ");
    414 					type(&e, fd0, afd, dfd);
    415 				}
    416 				break;
    417 
    418 			case 'D':
    419 				n = delete(&e);
    420 				q.p -= n;
    421 				if(israw(fd0) && e.q1 >= q.p+n)
    422 					sendbs(fd0, n);
    423 				break;
    424 
    425 			case 'x':
    426 			case 'X':
    427 				if(e.flag & 2)
    428 					gete(efd, &e2);
    429 				if(e.flag & 8){
    430 					gete(efd, &e3);
    431 					gete(efd, &e4);
    432 				}
    433 				if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){
    434 					/* send it straight back */
    435 					fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
    436 					break;
    437 				}
    438 				if(e.q0==e.q1 && (e.flag&2)){
    439 					e2.flag = e.flag;
    440 					e = e2;
    441 				}
    442 				char buf[100];
    443 				snprint(buf, sizeof buf, "%.*S", e.nr, e.r);
    444 				if(cistrcmp(buf, "cook") == 0) {
    445 					cook = 1;
    446 					break;
    447 				}
    448 				if(cistrcmp(buf, "nocook") == 0) {
    449 					cook = 0;
    450 					break;
    451 				}
    452 				if(e.flag & 8){
    453 					if(e.q1 != e.q0){
    454 						sende(&e, fd0, cfd, afd, dfd, 0);
    455 						sende(&blank, fd0, cfd, afd, dfd, 0);
    456 					}
    457 					sende(&e3, fd0, cfd, afd, dfd, 1);
    458 				}else	 if(e.q1 != e.q0)
    459 					sende(&e, fd0, cfd, afd, dfd, 1);
    460 				break;
    461 
    462 			case 'l':
    463 			case 'L':
    464 				/* just send it back */
    465 				if(e.flag & 2)
    466 					gete(efd, &e2);
    467 				fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
    468 				break;
    469 
    470 			case 'd':
    471 			case 'i':
    472 				break;
    473 
    474 			default:
    475 				goto Unknown;
    476 			}
    477 		}
    478 		qunlock(&q.lk);
    479 	}
    480 }
    481 
    482 int
    483 dropcr(char *p, int n)
    484 {
    485 	int i;
    486 	char *w, *r, *q;
    487 
    488 	r = p;
    489 	w = p;
    490 	for(i=0; i<n; i++) {
    491 		switch(*r) {
    492 		case '\b':
    493 			if(w > p)
    494 				w--;
    495 			break;
    496 		case '\r':
    497 			while(i<n-1 && *(r+1) == '\r') {
    498 				r++;
    499 				i++;
    500 			}
    501 			if(i<n && *(r+1) != '\n') {
    502 				q = r;
    503 				while(q>p && *(q-1) != '\n')
    504 					q--;
    505 				if(q > p) {
    506 					w = q;
    507 					break;
    508 				}
    509 			}
    510 			*w++ = '\n';
    511 			break;
    512 		default:
    513 			*w++ = *r;
    514 			break;
    515 		}
    516 		r++;
    517 	}
    518 	return w-p;
    519 }
    520 
    521 void
    522 stdoutproc(void *v)
    523 {
    524 	int fd1 = rcfd;
    525 	CFid *afd = addrfd;
    526 	CFid *dfd = datafd;
    527 	int n, m, w, npart;
    528 	char *buf, *s, *t;
    529 	Rune r;
    530 	char x[16], hold[UTFmax];
    531 
    532 	USED(v);
    533 	buf = malloc(8192+UTFmax+1);
    534 	npart = 0;
    535 	for(;;){
    536 		/* Let typing have a go -- maybe there's a rubout waiting. */
    537 		yield();
    538 		n = read(fd1, buf+npart, 8192);
    539 		if(n <= 0)
    540 			error(nil);
    541 
    542 		n = echocancel(buf+npart, n);
    543 		if(n == 0)
    544 			continue;
    545 
    546 		n = dropcrnl(buf+npart, n);
    547 		if(n == 0)
    548 			continue;
    549 
    550 		n = dropcr(buf+npart, n);
    551 		if(n == 0)
    552 			continue;
    553 
    554 		/* squash NULs */
    555 		s = memchr(buf+npart, 0, n);
    556 		if(s){
    557 			for(t=s; s<buf+npart+n; s++)
    558 				if(*t = *s)	/* assign = */
    559 					t++;
    560 			n = t-(buf+npart);
    561 		}
    562 
    563 		n += npart;
    564 
    565 		/* hold on to final partial rune */
    566 		npart = 0;
    567 		while(n>0 && (buf[n-1]&0xC0)){
    568 			--n;
    569 			npart++;
    570 			if((buf[n]&0xC0)!=0x80){
    571 				if(fullrune(buf+n, npart)){
    572 					w = chartorune(&r, buf+n);
    573 					n += w;
    574 					npart -= w;
    575 				}
    576 				break;
    577 			}
    578 		}
    579 		if(n > 0){
    580 			memmove(hold, buf+n, npart);
    581 			buf[n] = 0;
    582 			n = label(buf, n);
    583 			buf[n] = 0;
    584 
    585 			// clumsy but effective: notice password
    586 			// prompts so we can disable echo.
    587 			password = 0;
    588 			if(cistrstr(buf, "password") || cistrstr(buf, "passphrase")) {
    589 				int i;
    590 
    591 				i = n;
    592 				while(i > 0 && buf[i-1] == ' ')
    593 					i--;
    594 				password = i > 0 && buf[i-1] == ':';
    595 			}
    596 
    597 			qlock(&q.lk);
    598 			m = sprint(x, "#%d", q.p);
    599 			if(fswrite(afd, x, m) != m){
    600 				fprint(2, "stdout writing address %s: %r; resetting\n", x);
    601 				if(fswrite(afd, "$", 1) < 0)
    602 					fprint(2, "reset: %r\n");
    603 				fsseek(afd, 0, 0);
    604 				m = fsread(afd, x, sizeof x-1);
    605 				if(m >= 0){
    606 					x[m] = 0;
    607 					q.p = atoi(x);
    608 				}
    609 			}
    610 			if(fswrite(dfd, buf, n) != n)
    611 				error("stdout writing body");
    612 			/* Make sure acme scrolls to the end of the above write. */
    613 			if(fswrite(dfd, nil, 0) != 0)
    614 				error("stdout flushing body");
    615 			q.p += nrunes(buf, n);
    616 			qunlock(&q.lk);
    617 			memmove(buf, hold, npart);
    618 		}
    619 	}
    620 }
    621 
    622 char wdir[512];
    623 int
    624 label(char *sr, int n)
    625 {
    626 	char *sl, *el, *er, *r, *p;
    627 
    628 	er = sr+n;
    629 	for(r=er-1; r>=sr; r--)
    630 		if(*r == '\007')
    631 			break;
    632 	if(r < sr)
    633 		return n;
    634 
    635 	el = r+1;
    636 	if(el-sr > sizeof wdir - strlen(name) - 20)
    637 		sr = el - (sizeof wdir - strlen(name) - 20);
    638 	for(sl=el-3; sl>=sr; sl--)
    639 		if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
    640 			break;
    641 	if(sl < sr)
    642 		return n;
    643 
    644 	*r = 0;
    645 	if(strcmp(sl+3, "*9term-hold+") != 0) {
    646 		/*
    647 		 * add /-sysname if not present
    648 		 */
    649 		snprint(wdir, sizeof wdir, "name %s", sl+3);
    650 		p = strrchr(wdir, '/');
    651 		if(p==nil || *(p+1) != '-'){
    652 			p = wdir+strlen(wdir);
    653 			if(*(p-1) != '/')
    654 				*p++ = '/';
    655 			*p++ = '-';
    656 			strcpy(p, name);
    657 		}
    658 		strcat(wdir, "\n0\n");
    659 		fswrite(ctlfd, wdir, strlen(wdir));
    660 	}
    661 
    662 	memmove(sl, el, er-el);
    663 	n -= (el-sl);
    664 	return n;
    665 }
    666 
    667 int
    668 delete(Event *e)
    669 {
    670 	uint q0, q1;
    671 	int deltap;
    672 
    673 	q0 = e->q0;
    674 	q1 = e->q1;
    675 	if(q1 <= q.p)
    676 		return e->q1-e->q0;
    677 	if(q0 >= q.p+ntyper)
    678 		return 0;
    679 	deltap = 0;
    680 	if(q0 < q.p){
    681 		deltap = q.p-q0;
    682 		q0 = 0;
    683 	}else
    684 		q0 -= q.p;
    685 	if(q1 > q.p+ntyper)
    686 		q1 = ntyper;
    687 	else
    688 		q1 -= q.p;
    689 	deltype(q0, q1);
    690 	return deltap;
    691 }
    692 
    693 void
    694 addtype(int c, uint p0, char *b, int nb, int nr)
    695 {
    696 	int i, w;
    697 	Rune r;
    698 	uint p;
    699 	char *b0;
    700 
    701 	for(i=0; i<nb; i+=w){
    702 		w = chartorune(&r, b+i);
    703 		if((r==0x7F||r==3) && c=='K'){
    704 			write(rcfd, "\x7F", 1);
    705 			/* toss all typing */
    706 			q.p += ntyper+nr;
    707 			ntypebreak = 0;
    708 			ntypeb = 0;
    709 			ntyper = 0;
    710 			/* buglet:  more than one delete ignored */
    711 			return;
    712 		}
    713 		if(r=='\n' || r==0x04)
    714 			ntypebreak++;
    715 	}
    716 	typing = realloc(typing, ntypeb+nb);
    717 	if(typing == nil)
    718 		error("realloc");
    719 	if(p0 == ntyper)
    720 		memmove(typing+ntypeb, b, nb);
    721 	else{
    722 		b0 = typing;
    723 		for(p=0; p<p0 && b0<typing+ntypeb; p++){
    724 			w = chartorune(&r, b0+i);
    725 			b0 += w;
    726 		}
    727 		if(p != p0)
    728 			error("typing: findrune");
    729 		memmove(b0+nb, b0, (typing+ntypeb)-b0);
    730 		memmove(b0, b, nb);
    731 	}
    732 	ntypeb += nb;
    733 	ntyper += nr;
    734 }
    735 
    736 int
    737 israw(int fd0)
    738 {
    739 	return (!cook || password) && !isecho(fd0);
    740 }
    741 
    742 void
    743 sendtype(int fd0)
    744 {
    745 	int i, n, nr, raw;
    746 
    747 	raw = israw(fd0);
    748 	while(ntypebreak || (raw && ntypeb > 0)){
    749 		for(i=0; i<ntypeb; i++)
    750 			if(typing[i]=='\n' || typing[i]==0x04 || (i==ntypeb-1 && raw)){
    751 				if((typing[i] == '\n' || typing[i] == 0x04) && ntypebreak > 0)
    752 					ntypebreak--;
    753 				n = i+1;
    754 				i++;
    755 				if(!raw)
    756 					echoed(typing, n);
    757 				if(write(fd0, typing, n) != n)
    758 					error("sending to program");
    759 				nr = nrunes(typing, i);
    760 				q.p += nr;
    761 				ntyper -= nr;
    762 				ntypeb -= i;
    763 				memmove(typing, typing+i, ntypeb);
    764 				goto cont2;
    765 			}
    766 		print("no breakchar\n");
    767 		ntypebreak = 0;
    768 cont2:;
    769 	}
    770 }
    771 
    772 void
    773 sendbs(int fd0, int n)
    774 {
    775 	char buf[128];
    776 	int m;
    777 
    778 	memset(buf, 0x08, sizeof buf);
    779 	while(n > 0) {
    780 		m = sizeof buf;
    781 		if(m > n)
    782 			m = n;
    783 		n -= m;
    784 		write(fd0, buf, m);
    785 	}
    786 }
    787 
    788 void
    789 deltype(uint p0, uint p1)
    790 {
    791 	int w;
    792 	uint p, b0, b1;
    793 	Rune r;
    794 
    795 	/* advance to p0 */
    796 	b0 = 0;
    797 	for(p=0; p<p0 && b0<ntypeb; p++){
    798 		w = chartorune(&r, typing+b0);
    799 		b0 += w;
    800 	}
    801 	if(p != p0)
    802 		error("deltype 1");
    803 	/* advance to p1 */
    804 	b1 = b0;
    805 	for(; p<p1 && b1<ntypeb; p++){
    806 		w = chartorune(&r, typing+b1);
    807 		b1 += w;
    808 		if(r=='\n' || r==0x04)
    809 			ntypebreak--;
    810 	}
    811 	if(p != p1)
    812 		error("deltype 2");
    813 	memmove(typing+b0, typing+b1, ntypeb-b1);
    814 	ntypeb -= b1-b0;
    815 	ntyper -= p1-p0;
    816 }
    817 
    818 void
    819 type(Event *e, int fd0, CFid *afd, CFid *dfd)
    820 {
    821 	int m, n, nr;
    822 	char buf[128];
    823 
    824 	if(e->nr > 0)
    825 		addtype(e->c1, e->q0-q.p, e->b, e->nb, e->nr);
    826 	else{
    827 		m = e->q0;
    828 		while(m < e->q1){
    829 			n = sprint(buf, "#%d", m);
    830 			fswrite(afd, buf, n);
    831 			n = fsread(dfd, buf, sizeof buf);
    832 			nr = nrunes(buf, n);
    833 			while(m+nr > e->q1){
    834 				do; while(n>0 && (buf[--n]&0xC0)==0x80);
    835 				--nr;
    836 			}
    837 			if(n == 0)
    838 				break;
    839 			addtype(e->c1, m-q.p, buf, n, nr);
    840 			m += nr;
    841 		}
    842 	}
    843 	if(israw(fd0)) {
    844 		n = sprint(buf, "#%d,#%d", e->q0, e->q1);
    845 		fswrite(afd, buf, n);
    846 		fswrite(dfd, "", 0);
    847 		q.p -= e->q1 - e->q0;
    848 	}
    849 	sendtype(fd0);
    850 	if(e->nb > 0 && e->b[e->nb-1] == '\n')
    851 		cook = 1;
    852 }
    853 
    854 void
    855 sende(Event *e, int fd0, CFid *cfd, CFid *afd, CFid *dfd, int donl)
    856 {
    857 	int l, m, n, nr, lastc, end;
    858 	char abuf[16], buf[128];
    859 
    860 	end = q.p+ntyper;
    861 	l = sprint(abuf, "#%d", end);
    862 	fswrite(afd, abuf, l);
    863 	if(e->nr > 0){
    864 		fswrite(dfd, e->b, e->nb);
    865 		addtype(e->c1, ntyper, e->b, e->nb, e->nr);
    866 		lastc = e->r[e->nr-1];
    867 	}else{
    868 		m = e->q0;
    869 		lastc = 0;
    870 		while(m < e->q1){
    871 			n = sprint(buf, "#%d", m);
    872 			fswrite(afd, buf, n);
    873 			n = fsread(dfd, buf, sizeof buf);
    874 			nr = nrunes(buf, n);
    875 			while(m+nr > e->q1){
    876 				do; while(n>0 && (buf[--n]&0xC0)==0x80);
    877 				--nr;
    878 			}
    879 			if(n == 0)
    880 				break;
    881 			l = sprint(abuf, "#%d", end);
    882 			fswrite(afd, abuf, l);
    883 			fswrite(dfd, buf, n);
    884 			addtype(e->c1, ntyper, buf, n, nr);
    885 			lastc = buf[n-1];
    886 			m += nr;
    887 			end += nr;
    888 		}
    889 	}
    890 	if(donl && lastc!='\n'){
    891 		fswrite(dfd, "\n", 1);
    892 		addtype(e->c1, ntyper, "\n", 1, 1);
    893 	}
    894 	fswrite(cfd, "dot=addr", 8);
    895 	sendtype(fd0);
    896 }