xref: /plan9-contrib/sys/src/cmd/acme/xfid.c (revision 9b7bf7df4595c26f1e9b67beb0c6e44c9876fb05)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include "dat.h"
12 #include "fns.h"
13 
14 enum
15 {
16 	Ctlsize	= 5*12
17 };
18 
19 char	Edel[]		= "deleted window";
20 char	Ebadctl[]		= "ill-formed control message";
21 char	Ebadaddr[]	= "bad address syntax";
22 char	Eaddr[]		= "address out of range";
23 char	Einuse[]		= "already in use";
24 char	Ebadevent[]	= "bad event syntax";
25 extern char Eperm[];
26 
27 static
28 void
clampaddr(Window * w)29 clampaddr(Window *w)
30 {
31 	if(w->addr.q0 < 0)
32 		w->addr.q0 = 0;
33 	if(w->addr.q1 < 0)
34 		w->addr.q1 = 0;
35 	if(w->addr.q0 > w->body.file->nc)
36 		w->addr.q0 = w->body.file->nc;
37 	if(w->addr.q1 > w->body.file->nc)
38 		w->addr.q1 = w->body.file->nc;
39 }
40 
41 void
xfidctl(void * arg)42 xfidctl(void *arg)
43 {
44 	Xfid *x;
45 	void (*f)(Xfid*);
46 
47 	threadsetname("xfidctlthread");
48 	x = arg;
49 	for(;;){
50 		f = recvp(x->c);
51 		(*f)(x);
52 		flushimage(display, 1);
53 		sendp(cxfidfree, x);
54 	}
55 }
56 
57 void
xfidflush(Xfid * x)58 xfidflush(Xfid *x)
59 {
60 	Fcall fc;
61 	int i, j;
62 	Window *w;
63 	Column *c;
64 	Xfid *wx;
65 
66 	/* search windows for matching tag */
67 	qlock(&row);
68 	for(j=0; j<row.ncol; j++){
69 		c = row.col[j];
70 		for(i=0; i<c->nw; i++){
71 			w = c->w[i];
72 			winlock(w, 'E');
73 			wx = w->eventx;
74 			if(wx!=nil && wx->tag==x->oldtag){
75 				w->eventx = nil;
76 				wx->flushed = TRUE;
77 				sendp(wx->c, nil);
78 				winunlock(w);
79 				goto out;
80 			}
81 			winunlock(w);
82 		}
83 	}
84 out:
85 	qunlock(&row);
86 	respond(x, &fc, nil);
87 }
88 
89 void
xfidopen(Xfid * x)90 xfidopen(Xfid *x)
91 {
92 	Fcall fc;
93 	Window *w;
94 	Text *t;
95 	char *s;
96 	Rune *r;
97 	int m, n, q, q0, q1;
98 
99 	w = x->f->w;
100 	t = &w->body;
101 	if(w){
102 		winlock(w, 'E');
103 		q = FILE(x->f->qid);
104 		switch(q){
105 		case QWaddr:
106 			if(w->nopen[q]++ == 0){
107 				w->addr = (Range){0,0};
108 				w->limit = (Range){-1,-1};
109 			}
110 			break;
111 		case QWdata:
112 		case QWxdata:
113 			w->nopen[q]++;
114 			break;
115 		case QWevent:
116 			if(w->nopen[q]++ == 0){
117 				if(!w->isdir && w->col!=nil){
118 					w->filemenu = FALSE;
119 					winsettag(w);
120 				}
121 			}
122 			break;
123 		case QWrdsel:
124 			/*
125 			 * Use a temporary file.
126 			 * A pipe would be the obvious, but we can't afford the
127 			 * broken pipe notification.  Using the code to read QWbody
128 			 * is n², which should probably also be fixed.  Even then,
129 			 * though, we'd need to squirrel away the data in case it's
130 			 * modified during the operation, e.g. by |sort
131 			 */
132 			if(w->rdselfd > 0){
133 				winunlock(w);
134 				respond(x, &fc, Einuse);
135 				return;
136 			}
137 			w->rdselfd = tempfile();
138 			if(w->rdselfd < 0){
139 				winunlock(w);
140 				respond(x, &fc, "can't create temp file");
141 				return;
142 			}
143 			w->nopen[q]++;
144 			q0 = t->q0;
145 			q1 = t->q1;
146 			r = fbufalloc();
147 			s = fbufalloc();
148 			while(q0 < q1){
149 				n = q1 - q0;
150 				if(n > BUFSIZE/UTFmax)
151 					n = BUFSIZE/UTFmax;
152 				bufread(t->file, q0, r, n);
153 				m = snprint(s, BUFSIZE+1, "%.*S", n, r);
154 				if(write(w->rdselfd, s, m) != m){
155 					warning(nil, "can't write temp file for pipe command %r\n");
156 					break;
157 				}
158 				q0 += n;
159 			}
160 			fbuffree(s);
161 			fbuffree(r);
162 			break;
163 		case QWwrsel:
164 			w->nopen[q]++;
165 			seq++;
166 			filemark(t->file);
167 			cut(t, t, nil, FALSE, TRUE, nil, 0);
168 			w->wrselrange = (Range){t->q1, t->q1};
169 			w->nomark = TRUE;
170 			break;
171 		case QWeditout:
172 			if(editing == FALSE){
173 				winunlock(w);
174 				respond(x, &fc, Eperm);
175 				return;
176 			}
177 			w->wrselrange = (Range){t->q1, t->q1};
178 			break;
179 		}
180 		winunlock(w);
181 	}
182 	fc.qid = x->f->qid;
183 	fc.iounit = messagesize-IOHDRSZ;
184 	x->f->open = TRUE;
185 	respond(x, &fc, nil);
186 }
187 
188 void
xfidclose(Xfid * x)189 xfidclose(Xfid *x)
190 {
191 	Fcall fc;
192 	Window *w;
193 	int q;
194 	Text *t;
195 
196 	w = x->f->w;
197 	x->f->busy = FALSE;
198 	if(x->f->open == FALSE){
199 		if(w != nil)
200 			winclose(w);
201 		respond(x, &fc, nil);
202 		return;
203 	}
204 
205 	x->f->open = FALSE;
206 	if(w){
207 		winlock(w, 'E');
208 		q = FILE(x->f->qid);
209 		switch(q){
210 		case QWctl:
211 			if(w->ctlfid!=~0 && w->ctlfid==x->f->fid){
212 				w->ctlfid = ~0;
213 				qunlock(&w->ctllock);
214 			}
215 			break;
216 		case QWdata:
217 		case QWxdata:
218 			w->nomark = FALSE;
219 			/* fall through */
220 		case QWaddr:
221 		case QWevent:	/* BUG: do we need to shut down Xfid? */
222 			if(--w->nopen[q] == 0){
223 				if(q == QWdata || q == QWxdata)
224 					w->nomark = FALSE;
225 				if(q==QWevent && !w->isdir && w->col!=nil){
226 					w->filemenu = TRUE;
227 					winsettag(w);
228 				}
229 				if(q == QWevent){
230 					free(w->dumpstr);
231 					free(w->dumpdir);
232 					w->dumpstr = nil;
233 					w->dumpdir = nil;
234 				}
235 			}
236 			break;
237 		case QWrdsel:
238 			close(w->rdselfd);
239 			w->rdselfd = 0;
240 			break;
241 		case QWwrsel:
242 			w->nomark = FALSE;
243 			t = &w->body;
244 			/* before: only did this if !w->noscroll, but that didn't seem right in practice */
245 			textshow(t, min(w->wrselrange.q0, t->file->nc),
246 				min(w->wrselrange.q1, t->file->nc), 1);
247 			textscrdraw(t);
248 			break;
249 		}
250 		winunlock(w);
251 		winclose(w);
252 	}
253 	respond(x, &fc, nil);
254 }
255 
256 void
xfidread(Xfid * x)257 xfidread(Xfid *x)
258 {
259 	Fcall fc;
260 	int n, q;
261 	uint off;
262 	char *b;
263 	char buf[256];
264 	Window *w;
265 
266 	q = FILE(x->f->qid);
267 	w = x->f->w;
268 	if(w == nil){
269 		fc.count = 0;
270 		switch(q){
271 		case Qcons:
272 		case Qlabel:
273 			break;
274 		case Qindex:
275 			xfidindexread(x);
276 			return;
277 		default:
278 			warning(nil, "unknown qid %d\n", q);
279 			break;
280 		}
281 		respond(x, &fc, nil);
282 		return;
283 	}
284 	winlock(w, 'F');
285 	if(w->col == nil){
286 		winunlock(w);
287 		respond(x, &fc, Edel);
288 		return;
289 	}
290 	off = x->offset;
291 	switch(q){
292 	case QWaddr:
293 		textcommit(&w->body, TRUE);
294 		clampaddr(w);
295 		sprint(buf, "%11d %11d ", w->addr.q0, w->addr.q1);
296 		goto Readbuf;
297 
298 	case QWbody:
299 		xfidutfread(x, &w->body, w->body.file->nc, QWbody);
300 		break;
301 
302 	case QWctl:
303 		b = winctlprint(w, buf, 1);
304 		goto Readb;
305 
306 	Readbuf:
307 		b = buf;
308 	Readb:
309 		n = strlen(b);
310 		if(off > n)
311 			off = n;
312 		if(off+x->count > n)
313 			x->count = n-off;
314 		fc.count = x->count;
315 		fc.data = b+off;
316 		respond(x, &fc, nil);
317 		if(b != buf)
318 			free(b);
319 		break;
320 
321 	case QWevent:
322 		xfideventread(x, w);
323 		break;
324 
325 	case QWdata:
326 		/* BUG: what should happen if q1 > q0? */
327 		if(w->addr.q0 > w->body.file->nc){
328 			respond(x, &fc, Eaddr);
329 			break;
330 		}
331 		w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->nc);
332 		w->addr.q1 = w->addr.q0;
333 		break;
334 
335 	case QWxdata:
336 		/* BUG: what should happen if q1 > q0? */
337 		if(w->addr.q0 > w->body.file->nc){
338 			respond(x, &fc, Eaddr);
339 			break;
340 		}
341 		w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1);
342 		break;
343 
344 	case QWtag:
345 		xfidutfread(x, &w->tag, w->tag.file->nc, QWtag);
346 		break;
347 
348 	case QWrdsel:
349 		seek(w->rdselfd, off, 0);
350 		n = x->count;
351 		if(n > BUFSIZE)
352 			n = BUFSIZE;
353 		b = fbufalloc();
354 		n = read(w->rdselfd, b, n);
355 		if(n < 0){
356 			respond(x, &fc, "I/O error in temp file");
357 			break;
358 		}
359 		fc.count = n;
360 		fc.data = b;
361 		respond(x, &fc, nil);
362 		fbuffree(b);
363 		break;
364 
365 	default:
366 		sprint(buf, "unknown qid %d in read", q);
367 		respond(x, &fc, nil);
368 	}
369 	winunlock(w);
370 }
371 
372 static Rune*
fullrunewrite(Xfid * x,int * inr)373 fullrunewrite(Xfid *x, int *inr)
374 {
375 	int q, cnt, c, nb, nr;
376 	Rune *r;
377 
378 	q = x->f->nrpart;
379 	cnt = x->count;
380 	if(q > 0){
381 		memmove(x->data+q, x->data, cnt);	/* there's room; see fsysproc */
382 		memmove(x->data, x->f->rpart, q);
383 		cnt += q;
384 		x->f->nrpart = 0;
385 	}
386 	r = runemalloc(cnt);
387 	cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil);
388 	/* approach end of buffer */
389 	while(fullrune(x->data+nb, cnt-nb)){
390 		c = nb;
391 		nb += chartorune(&r[nr], x->data+c);
392 		if(r[nr])
393 			nr++;
394 	}
395 	if(nb < cnt){
396 		memmove(x->f->rpart, x->data+nb, cnt-nb);
397 		x->f->nrpart = cnt-nb;
398 	}
399 	*inr = nr;
400 	return r;
401 }
402 
403 void
xfidwrite(Xfid * x)404 xfidwrite(Xfid *x)
405 {
406 	Fcall fc;
407 	int c, qid, nb, nr, eval;
408 	char buf[64], *err;
409 	Window *w;
410 	Rune *r;
411 	Range a;
412 	Text *t;
413 	uint q0, tq0, tq1;
414 
415 	qid = FILE(x->f->qid);
416 	w = x->f->w;
417 	if(w){
418 		c = 'F';
419 		if(qid==QWtag || qid==QWbody)
420 			c = 'E';
421 		winlock(w, c);
422 		if(w->col == nil){
423 			winunlock(w);
424 			respond(x, &fc, Edel);
425 			return;
426 		}
427 	}
428 	x->data[x->count] = 0;
429 	switch(qid){
430 	case Qcons:
431 		w = errorwin(x->f->mntdir, 'X');
432 		t=&w->body;
433 		goto BodyTag;
434 
435 	case Qlabel:
436 		fc.count = x->count;
437 		respond(x, &fc, nil);
438 		break;
439 
440 	case QWaddr:
441 		x->data[x->count] = 0;
442 		r = bytetorune(x->data, &nr);
443 		t = &w->body;
444 		wincommit(w, t);
445 		eval = TRUE;
446 		a = address(x->f->mntdir, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb);
447 		free(r);
448 		if(nb < nr){
449 			respond(x, &fc, Ebadaddr);
450 			break;
451 		}
452 		if(!eval){
453 			respond(x, &fc, Eaddr);
454 			break;
455 		}
456 		w->addr = a;
457 		fc.count = x->count;
458 		respond(x, &fc, nil);
459 		break;
460 
461 	case Qeditout:
462 	case QWeditout:
463 		r = fullrunewrite(x, &nr);
464 		if(w)
465 			err = edittext(w, w->wrselrange.q1, r, nr);
466 		else
467 			err = edittext(nil, 0, r, nr);
468 		free(r);
469 		if(err != nil){
470 			respond(x, &fc, err);
471 			break;
472 		}
473 		fc.count = x->count;
474 		respond(x, &fc, nil);
475 		break;
476 
477 	case QWerrors:
478 		w = errorwinforwin(w);
479 		t = &w->body;
480 		goto BodyTag;
481 
482 	case QWbody:
483 	case QWwrsel:
484 		t = &w->body;
485 		goto BodyTag;
486 
487 	case QWctl:
488 		xfidctlwrite(x, w);
489 		break;
490 
491 	case QWdata:
492 		a = w->addr;
493 		t = &w->body;
494 		wincommit(w, t);
495 		if(a.q0>t->file->nc || a.q1>t->file->nc){
496 			respond(x, &fc, Eaddr);
497 			break;
498 		}
499 		r = runemalloc(x->count);
500 		cvttorunes(x->data, x->count, r, &nb, &nr, nil);
501 		if(w->nomark == FALSE){
502 			seq++;
503 			filemark(t->file);
504 		}
505 		q0 = a.q0;
506 		if(a.q1 > q0){
507 			textdelete(t, q0, a.q1, TRUE);
508 			w->addr.q1 = q0;
509 		}
510 		tq0 = t->q0;
511 		tq1 = t->q1;
512 		textinsert(t, q0, r, nr, TRUE);
513 		if(tq0 >= q0)
514 			tq0 += nr;
515 		if(tq1 >= q0)
516 			tq1 += nr;
517 		textsetselect(t, tq0, tq1);
518 		if(!t->w->noscroll)
519 			textshow(t, q0, q0+nr, 0);
520 		textscrdraw(t);
521 		winsettag(w);
522 		free(r);
523 		w->addr.q0 += nr;
524 		w->addr.q1 = w->addr.q0;
525 		fc.count = x->count;
526 		respond(x, &fc, nil);
527 		break;
528 
529 	case QWevent:
530 		xfideventwrite(x, w);
531 		break;
532 
533 	case QWtag:
534 		t = &w->tag;
535 		goto BodyTag;
536 
537 	BodyTag:
538 		r = fullrunewrite(x, &nr);
539 		if(nr > 0){
540 			wincommit(w, t);
541 			if(qid == QWwrsel){
542 				q0 = w->wrselrange.q1;
543 				if(q0 > t->file->nc)
544 					q0 = t->file->nc;
545 			}else
546 				q0 = t->file->nc;
547 			if(qid == QWtag)
548 				textinsert(t, q0, r, nr, TRUE);
549 			else{
550 				if(w->nomark == FALSE){
551 					seq++;
552 					filemark(t->file);
553 				}
554 				q0 = textbsinsert(t, q0, r, nr, TRUE, &nr);
555 				textsetselect(t, t->q0, t->q1);	/* insert could leave it somewhere else */
556 				if(qid!=QWwrsel && !t->w->noscroll)
557 					textshow(t, q0+nr, q0+nr, 1);
558 				textscrdraw(t);
559 			}
560 			winsettag(w);
561 			if(qid == QWwrsel)
562 				w->wrselrange.q1 += nr;
563 			free(r);
564 		}
565 		fc.count = x->count;
566 		respond(x, &fc, nil);
567 		break;
568 
569 	default:
570 		sprint(buf, "unknown qid %d in write", qid);
571 		respond(x, &fc, buf);
572 		break;
573 	}
574 	if(w)
575 		winunlock(w);
576 }
577 
578 void
xfidctlwrite(Xfid * x,Window * w)579 xfidctlwrite(Xfid *x, Window *w)
580 {
581 	Fcall fc;
582 	int i, m, n, nb, nr, nulls;
583 	Rune *r;
584 	char *err, *p, *pp, *q, *e;
585 	int isfbuf, scrdraw, settag;
586 	Text *t;
587 
588 	err = nil;
589 	e = x->data+x->count;
590 	scrdraw = FALSE;
591 	settag = FALSE;
592 	isfbuf = TRUE;
593 	if(x->count < RBUFSIZE)
594 		r = fbufalloc();
595 	else{
596 		isfbuf = FALSE;
597 		r = emalloc(x->count*UTFmax+1);
598 	}
599 	x->data[x->count] = 0;
600 	textcommit(&w->tag, TRUE);
601 	for(n=0; n<x->count; n+=m){
602 		p = x->data+n;
603 		if(strncmp(p, "lock", 4) == 0){	/* make window exclusive use */
604 			qlock(&w->ctllock);
605 			w->ctlfid = x->f->fid;
606 			m = 4;
607 		}else
608 		if(strncmp(p, "unlock", 6) == 0){	/* release exclusive use */
609 			w->ctlfid = ~0;
610 			qunlock(&w->ctllock);
611 			m = 6;
612 		}else
613 		if(strncmp(p, "clean", 5) == 0){	/* mark window 'clean', seq=0 */
614 			t = &w->body;
615 			t->eq0 = ~0;
616 			filereset(t->file);
617 			t->file->mod = FALSE;
618 			w->dirty = FALSE;
619 			settag = TRUE;
620 			m = 5;
621 		}else
622 		if(strncmp(p, "dirty", 5) == 0){	/* mark window 'dirty' */
623 			t = &w->body;
624 			/* doesn't change sequence number, so "Put" won't appear.  it shouldn't. */
625 			t->file->mod = TRUE;
626 			w->dirty = TRUE;
627 			settag = TRUE;
628 			m = 5;
629 		}else
630 		if(strncmp(p, "show", 4) == 0){	/* show dot */
631 			t = &w->body;
632 			textshow(t, t->q0, t->q1, 1);
633 			m = 4;
634 		}else
635 		if(strncmp(p, "name ", 5) == 0){	/* set file name */
636 			pp = p+5;
637 			m = 5;
638 			q = memchr(pp, '\n', e-pp);
639 			if(q==nil || q==pp){
640 				err = Ebadctl;
641 				break;
642 			}
643 			*q = 0;
644 			nulls = FALSE;
645 			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
646 			if(nulls){
647 				err = "nulls in file name";
648 				break;
649 			}
650 			for(i=0; i<nr; i++)
651 				if(r[i] <= ' '){
652 					err = "bad character in file name";
653 					goto out;
654 				}
655 out:
656 			seq++;
657 			filemark(w->body.file);
658 			winsetname(w, r, nr);
659 			m += (q+1) - pp;
660 		}else
661 		if(strncmp(p, "dump ", 5) == 0){	/* set dump string */
662 			pp = p+5;
663 			m = 5;
664 			q = memchr(pp, '\n', e-pp);
665 			if(q==nil || q==pp){
666 				err = Ebadctl;
667 				break;
668 			}
669 			*q = 0;
670 			nulls = FALSE;
671 			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
672 			if(nulls){
673 				err = "nulls in dump string";
674 				break;
675 			}
676 			w->dumpstr = runetobyte(r, nr);
677 			m += (q+1) - pp;
678 		}else
679 		if(strncmp(p, "dumpdir ", 8) == 0){	/* set dump directory */
680 			pp = p+8;
681 			m = 8;
682 			q = memchr(pp, '\n', e-pp);
683 			if(q==nil || q==pp){
684 				err = Ebadctl;
685 				break;
686 			}
687 			*q = 0;
688 			nulls = FALSE;
689 			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
690 			if(nulls){
691 				err = "nulls in dump directory string";
692 				break;
693 			}
694 			w->dumpdir = runetobyte(r, nr);
695 			m += (q+1) - pp;
696 		}else
697 		if(strncmp(p, "delete", 6) == 0){	/* delete for sure */
698 			colclose(w->col, w, TRUE);
699 			m = 6;
700 		}else
701 		if(strncmp(p, "del", 3) == 0){	/* delete, but check dirty */
702 			if(!winclean(w, TRUE)){
703 				err = "file dirty";
704 				break;
705 			}
706 			colclose(w->col, w, TRUE);
707 			m = 3;
708 		}else
709 		if(strncmp(p, "get", 3) == 0){	/* get file */
710 			get(&w->body, nil, nil, FALSE, XXX, nil, 0);
711 			m = 3;
712 		}else
713 		if(strncmp(p, "put", 3) == 0){	/* put file */
714 			put(&w->body, nil, nil, XXX, XXX, nil, 0);
715 			m = 3;
716 		}else
717 		if(strncmp(p, "dot=addr", 8) == 0){	/* set dot */
718 			textcommit(&w->body, TRUE);
719 			clampaddr(w);
720 			w->body.q0 = w->addr.q0;
721 			w->body.q1 = w->addr.q1;
722 			textsetselect(&w->body, w->body.q0, w->body.q1);
723 			settag = TRUE;
724 			m = 8;
725 		}else
726 		if(strncmp(p, "addr=dot", 8) == 0){	/* set addr */
727 			w->addr.q0 = w->body.q0;
728 			w->addr.q1 = w->body.q1;
729 			m = 8;
730 		}else
731 		if(strncmp(p, "limit=addr", 10) == 0){	/* set limit */
732 			textcommit(&w->body, TRUE);
733 			clampaddr(w);
734 			w->limit.q0 = w->addr.q0;
735 			w->limit.q1 = w->addr.q1;
736 			m = 10;
737 		}else
738 		if(strncmp(p, "nomark", 6) == 0){	/* turn off automatic marking */
739 			w->nomark = TRUE;
740 			m = 6;
741 		}else
742 		if(strncmp(p, "mark", 4) == 0){	/* mark file */
743 			seq++;
744 			filemark(w->body.file);
745 			settag = TRUE;
746 			m = 4;
747 		}else
748 		if(strncmp(p, "nomenu", 6) == 0){	/* turn off automatic menu */
749 			w->filemenu = FALSE;
750 			m = 6;
751 		}else
752 		if(strncmp(p, "menu", 4) == 0){	/* enable automatic menu */
753 			w->filemenu = TRUE;
754 			m = 4;
755 		}else
756 		if(strncmp(p, "noscroll", 8) == 0){	/* turn off automatic scrolling */
757 			w->noscroll = TRUE;
758 			m = 8;
759 		}else
760 		if(strncmp(p, "cleartag", 8) == 0){	/* wipe tag right of bar */
761 			wincleartag(w);
762 			settag = TRUE;
763 			m = 8;
764 		}else
765 		if(strncmp(p, "scroll", 6) == 0){	/* turn on automatic scrolling (writes to body only) */
766 			w->noscroll = FALSE;
767 			m = 6;
768 		}else{
769 			err = Ebadctl;
770 			break;
771 		}
772 		while(p[m] == '\n')
773 			m++;
774 	}
775 
776 	if(isfbuf)
777 		fbuffree(r);
778 	else
779 		free(r);
780 	if(err)
781 		n = 0;
782 	fc.count = n;
783 	respond(x, &fc, err);
784 	if(settag)
785 		winsettag(w);
786 	if(scrdraw)
787 		textscrdraw(&w->body);
788 }
789 
790 void
xfideventwrite(Xfid * x,Window * w)791 xfideventwrite(Xfid *x, Window *w)
792 {
793 	Fcall fc;
794 	int m, n;
795 	Rune *r;
796 	char *err, *p, *q;
797 	int isfbuf;
798 	Text *t;
799 	int c;
800 	uint q0, q1;
801 
802 	err = nil;
803 	isfbuf = TRUE;
804 	if(x->count < RBUFSIZE)
805 		r = fbufalloc();
806 	else{
807 		isfbuf = FALSE;
808 		r = emalloc(x->count*UTFmax+1);
809 	}
810 	for(n=0; n<x->count; n+=m){
811 		p = x->data+n;
812 		w->owner = *p++;	/* disgusting */
813 		c = *p++;
814 		while(*p == ' ')
815 			p++;
816 		q0 = strtoul(p, &q, 10);
817 		if(q == p)
818 			goto Rescue;
819 		p = q;
820 		while(*p == ' ')
821 			p++;
822 		q1 = strtoul(p, &q, 10);
823 		if(q == p)
824 			goto Rescue;
825 		p = q;
826 		while(*p == ' ')
827 			p++;
828 		if(*p++ != '\n')
829 			goto Rescue;
830 		m = p-(x->data+n);
831 		if('a'<=c && c<='z')
832 			t = &w->tag;
833 		else if('A'<=c && c<='Z')
834 			t = &w->body;
835 		else
836 			goto Rescue;
837 		if(q0>t->file->nc || q1>t->file->nc || q0>q1)
838 			goto Rescue;
839 
840 		qlock(&row);	/* just like mousethread */
841 		switch(c){
842 		case 'x':
843 		case 'X':
844 			execute(t, q0, q1, TRUE, nil);
845 			break;
846 		case 'l':
847 		case 'L':
848 			look3(t, q0, q1, TRUE);
849 			break;
850 		default:
851 			qunlock(&row);
852 			goto Rescue;
853 		}
854 		qunlock(&row);
855 
856 	}
857 
858     Out:
859 	if(isfbuf)
860 		fbuffree(r);
861 	else
862 		free(r);
863 	if(err)
864 		n = 0;
865 	fc.count = n;
866 	respond(x, &fc, err);
867 	return;
868 
869     Rescue:
870 	err = Ebadevent;
871 	goto Out;
872 }
873 
874 void
xfidutfread(Xfid * x,Text * t,uint q1,int qid)875 xfidutfread(Xfid *x, Text *t, uint q1, int qid)
876 {
877 	Fcall fc;
878 	Window *w;
879 	Rune *r;
880 	char *b, *b1;
881 	uint q, off, boff;
882 	int m, n, nr, nb;
883 
884 	w = t->w;
885 	wincommit(w, t);
886 	off = x->offset;
887 	r = fbufalloc();
888 	b = fbufalloc();
889 	b1 = fbufalloc();
890 	n = 0;
891 	if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
892 		boff = w->utflastboff;
893 		q = w->utflastq;
894 	}else{
895 		/* BUG: stupid code: scan from beginning */
896 		boff = 0;
897 		q = 0;
898 	}
899 	w->utflastqid = qid;
900 	while(q<q1 && n<x->count){
901 		/*
902 		 * Updating here avoids partial rune problem: we're always on a
903 		 * char boundary. The cost is we will usually do one more read
904 		 * than we really need, but that's better than being n^2.
905 		 */
906 		w->utflastboff = boff;
907 		w->utflastq = q;
908 		nr = q1-q;
909 		if(nr > BUFSIZE/UTFmax)
910 			nr = BUFSIZE/UTFmax;
911 		bufread(t->file, q, r, nr);
912 		nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
913 		if(boff >= off){
914 			m = nb;
915 			if(boff+m > off+x->count)
916 				m = off+x->count - boff;
917 			memmove(b1+n, b, m);
918 			n += m;
919 		}else if(boff+nb > off){
920 			if(n != 0)
921 				error("bad count in utfrune");
922 			m = nb - (off-boff);
923 			if(m > x->count)
924 				m = x->count;
925 			memmove(b1, b+(off-boff), m);
926 			n += m;
927 		}
928 		boff += nb;
929 		q += nr;
930 	}
931 	fbuffree(r);
932 	fbuffree(b);
933 	fc.count = n;
934 	fc.data = b1;
935 	respond(x, &fc, nil);
936 	fbuffree(b1);
937 }
938 
939 int
xfidruneread(Xfid * x,Text * t,uint q0,uint q1)940 xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
941 {
942 	Fcall fc;
943 	Window *w;
944 	Rune *r, junk;
945 	char *b, *b1;
946 	uint q, boff;
947 	int i, rw, m, n, nr, nb;
948 
949 	w = t->w;
950 	wincommit(w, t);
951 	r = fbufalloc();
952 	b = fbufalloc();
953 	b1 = fbufalloc();
954 	n = 0;
955 	q = q0;
956 	boff = 0;
957 	while(q<q1 && n<x->count){
958 		nr = q1-q;
959 		if(nr > BUFSIZE/UTFmax)
960 			nr = BUFSIZE/UTFmax;
961 		bufread(t->file, q, r, nr);
962 		nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
963 		m = nb;
964 		if(boff+m > x->count){
965 			i = x->count - boff;
966 			/* copy whole runes only */
967 			m = 0;
968 			nr = 0;
969 			while(m < i){
970 				rw = chartorune(&junk, b+m);
971 				if(m+rw > i)
972 					break;
973 				m += rw;
974 				nr++;
975 			}
976 			if(m == 0)
977 				break;
978 		}
979 		memmove(b1+n, b, m);
980 		n += m;
981 		boff += nb;
982 		q += nr;
983 	}
984 	fbuffree(r);
985 	fbuffree(b);
986 	fc.count = n;
987 	fc.data = b1;
988 	respond(x, &fc, nil);
989 	fbuffree(b1);
990 	return q-q0;
991 }
992 
993 void
xfideventread(Xfid * x,Window * w)994 xfideventread(Xfid *x, Window *w)
995 {
996 	Fcall fc;
997 	char *b;
998 	int i, n;
999 
1000 	i = 0;
1001 	x->flushed = FALSE;
1002 	while(w->nevents == 0){
1003 		if(i){
1004 			if(!x->flushed)
1005 				respond(x, &fc, "window shut down");
1006 			return;
1007 		}
1008 		w->eventx = x;
1009 		winunlock(w);
1010 		recvp(x->c);
1011 		winlock(w, 'F');
1012 		i++;
1013 	}
1014 
1015 	n = w->nevents;
1016 	if(n > x->count)
1017 		n = x->count;
1018 	fc.count = n;
1019 	fc.data = w->events;
1020 	respond(x, &fc, nil);
1021 	b = w->events;
1022 	w->events = estrdup(w->events+n);
1023 	free(b);
1024 	w->nevents -= n;
1025 }
1026 
1027 void
xfidindexread(Xfid * x)1028 xfidindexread(Xfid *x)
1029 {
1030 	Fcall fc;
1031 	int i, j, m, n, nmax, isbuf, cnt, off;
1032 	Window *w;
1033 	char *b;
1034 	Rune *r;
1035 	Column *c;
1036 
1037 	qlock(&row);
1038 	nmax = 0;
1039 	for(j=0; j<row.ncol; j++){
1040 		c = row.col[j];
1041 		for(i=0; i<c->nw; i++){
1042 			w = c->w[i];
1043 			nmax += Ctlsize + w->tag.file->nc*UTFmax + 1;
1044 		}
1045 	}
1046 	nmax++;
1047 	isbuf = (nmax<=RBUFSIZE);
1048 	if(isbuf)
1049 		b = (char*)x->buf;
1050 	else
1051 		b = emalloc(nmax);
1052 	r = fbufalloc();
1053 	n = 0;
1054 	for(j=0; j<row.ncol; j++){
1055 		c = row.col[j];
1056 		for(i=0; i<c->nw; i++){
1057 			w = c->w[i];
1058 			/* only show the currently active window of a set */
1059 			if(w->body.file->curtext != &w->body)
1060 				continue;
1061 			winctlprint(w, b+n, 0);
1062 			n += Ctlsize;
1063 			m = min(RBUFSIZE, w->tag.file->nc);
1064 			bufread(w->tag.file, 0, r, m);
1065 			m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1066 			while(n<m && b[n]!='\n')
1067 				n++;
1068 			b[n++] = '\n';
1069 		}
1070 	}
1071 	qunlock(&row);
1072 	off = x->offset;
1073 	cnt = x->count;
1074 	if(off > n)
1075 		off = n;
1076 	if(off+cnt > n)
1077 		cnt = n-off;
1078 	fc.count = cnt;
1079 	memmove(r, b+off, cnt);
1080 	fc.data = (char*)r;
1081 	if(!isbuf)
1082 		free(b);
1083 	respond(x, &fc, nil);
1084 	fbuffree(r);
1085 }
1086