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