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
fsfidprint(CFid * fid,char * fmt,...)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
usage(void)94 usage(void)
95 {
96 	fprint(2, "usage: win cmd args...\n");
97 	threadexitsall("usage");
98 }
99 
100 void
waitthread(void * v)101 waitthread(void *v)
102 {
103 	recvp(cwait);
104 	threadexitsall(nil);
105 }
106 
107 void
hangupnote(void * a,char * msg)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
threadmain(int argc,char ** argv)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
error(char * s,...)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*
onestring(int argc,char ** argv)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
getec(CFid * efd)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
geten(CFid * efd)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
geter(CFid * efd,char * buf,int * nb)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
gete(CFid * efd,Event * e)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
nrunes(char * s,int nb)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
stdinproc(void * v)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
dropcr(char * p,int n)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
stdoutproc(void * v)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
label(char * sr,int n)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
delete(Event * e)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
addtype(int c,uint p0,char * b,int nb,int nr)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
israw(int fd0)737 israw(int fd0)
738 {
739 	return (!cook || password) && !isecho(fd0);
740 }
741 
742 void
sendtype(int fd0)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
sendbs(int fd0,int n)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
deltype(uint p0,uint p1)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
type(Event * e,int fd0,CFid * afd,CFid * dfd)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
sende(Event * e,int fd0,CFid * cfd,CFid * afd,CFid * dfd,int donl)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 }
897