Blob


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"
15 enum
16 {
17 Ctlsize = 5*12
18 };
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[];
28 static
29 void
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 }
42 void
43 xfidctl(void *arg)
44 {
45 Xfid *x;
46 void (*f)(Xfid*);
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 }
58 void
59 xfidflush(Xfid *x)
60 {
61 Fcall fc;
62 int i, j;
63 Window *w;
64 Column *c;
65 Xfid *wx;
67 xfidlogflush(x);
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 }
92 void
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;
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);
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);
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;
140 w->rdselfd = tempfile();
141 if(w->rdselfd < 0){
142 winunlock(w);
143 respond(x, &fc, "can't create temp file");
144 return;
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;
161 q0 += n;
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;
180 if(!canqlock(&w->editoutlk)){
181 winunlock(w);
182 respond(x, &fc, Einuse);
183 return;
185 w->wrselrange = range(t->q1, t->q1);
186 break;
188 winunlock(w);
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;
200 break;
203 fc.qid = x->f->qid;
204 fc.iounit = messagesize-IOHDRSZ;
205 x->f->open = TRUE;
206 respond(x, &fc, nil);
209 void
210 xfidclose(Xfid *x)
212 Fcall fc;
213 Window *w;
214 int q;
215 Text *t;
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;
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);
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);
251 if(q == QWevent){
252 free(w->dumpstr);
253 free(w->dumpdir);
254 w->dumpstr = nil;
255 w->dumpdir = nil;
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;
275 winunlock(w);
276 winclose(w);
278 else{
279 switch(q){
280 case Qeditout:
281 qunlock(&editoutlk);
282 break;
285 respond(x, &fc, nil);
288 void
289 xfidread(Xfid *x)
291 Fcall fc;
292 int n, q;
293 uint off;
294 char *b;
295 char buf[256];
296 Window *w;
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;
316 respond(x, &fc, nil);
317 return;
319 winlock(w, 'F');
320 if(w->col == nil){
321 winunlock(w);
322 respond(x, &fc, Edel);
323 return;
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;
333 case QWbody:
334 xfidutfread(x, &w->body, w->body.file->b.nc, QWbody);
335 break;
337 case QWctl:
338 b = winctlprint(w, buf, 1);
339 goto Readb;
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;
356 case QWevent:
357 xfideventread(x, w);
358 break;
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;
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;
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;
376 w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1);
377 break;
379 case QWtag:
380 xfidutfread(x, &w->tag, w->tag.file->b.nc, QWtag);
381 break;
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;
394 fc.count = n;
395 fc.data = b;
396 respond(x, &fc, nil);
397 fbuffree(b);
398 break;
400 default:
401 sprint(buf, "unknown qid %d in read", q);
402 respond(x, &fc, nil);
404 winunlock(w);
407 static int
408 shouldscroll(Text *t, uint q0, int qid)
410 if(qid == Qcons)
411 return TRUE;
412 return t->org <= q0 && q0 <= t->org+t->fr.nchars;
415 static Rune*
416 fullrunewrite(Xfid *x, int *inr)
418 int q, cnt, c, nb, nr;
419 Rune *r;
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;
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++;
438 if(nb < cnt){
439 memmove(x->f->rpart, x->fcall.data+nb, cnt-nb);
440 x->f->nrpart = cnt-nb;
442 *inr = nr;
443 return r;
446 void
447 xfidwrite(Xfid *x)
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;
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;
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;
478 case Qlabel:
479 fc.count = x->fcall.count;
480 respond(x, &fc, nil);
481 break;
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;
495 if(!eval){
496 respond(x, &fc, Eaddr);
497 break;
499 w->addr = a;
500 fc.count = x->fcall.count;
501 respond(x, &fc, nil);
502 break;
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;
516 fc.count = x->fcall.count;
517 respond(x, &fc, nil);
518 break;
520 case QWerrors:
521 w = errorwinforwin(w);
522 t = &w->body;
523 goto BodyTag;
525 case QWbody:
526 case QWwrsel:
527 t = &w->body;
528 goto BodyTag;
530 case QWctl:
531 xfidctlwrite(x, w);
532 break;
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;
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);
548 q0 = a.q0;
549 if(a.q1 > q0){
550 textdelete(t, q0, a.q1, TRUE);
551 w->addr.q1 = q0;
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;
572 case QWevent:
573 xfideventwrite(x, w);
574 break;
576 case QWtag:
577 t = &w->tag;
578 goto BodyTag;
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);
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);
603 winsettag(w);
604 if(qid == QWwrsel)
605 w->wrselrange.q1 += nr;
606 free(r);
608 fc.count = x->fcall.count;
609 respond(x, &fc, nil);
610 break;
612 default:
613 sprint(buf, "unknown qid %d in write", qid);
614 respond(x, &fc, buf);
615 break;
617 if(w)
618 winunlock(w);
621 void
622 xfidctlwrite(Xfid *x, Window *w)
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;
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);
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;
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;
693 for(i=0; i<nr; i++)
694 if(r[i] <= ' '){
695 err = "bad character in file name";
696 goto out;
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, "dump ", 5) == 0){ /* set dump string */
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;
712 *q = 0;
713 nulls = FALSE;
714 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
715 if(nulls){
716 err = "nulls in dump string";
717 break;
719 w->dumpstr = runetobyte(r, nr);
720 m += (q+1) - pp;
721 }else
722 if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */
723 pp = p+8;
724 m = 8;
725 q = memchr(pp, '\n', e-pp);
726 if(q==nil || q==pp){
727 err = Ebadctl;
728 break;
730 *q = 0;
731 nulls = FALSE;
732 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
733 if(nulls){
734 err = "nulls in dump directory string";
735 break;
737 w->dumpdir = runetobyte(r, nr);
738 m += (q+1) - pp;
739 }else
740 if(strncmp(p, "delete", 6) == 0){ /* delete for sure */
741 colclose(w->col, w, TRUE);
742 m = 6;
743 }else
744 if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */
745 if(!winclean(w, TRUE)){
746 err = "file dirty";
747 break;
749 colclose(w->col, w, TRUE);
750 m = 3;
751 }else
752 if(strncmp(p, "get", 3) == 0){ /* get file */
753 get(&w->body, nil, nil, FALSE, XXX, nil, 0);
754 m = 3;
755 }else
756 if(strncmp(p, "put", 3) == 0){ /* put file */
757 put(&w->body, nil, nil, XXX, XXX, nil, 0);
758 m = 3;
759 }else
760 if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */
761 textcommit(&w->body, TRUE);
762 clampaddr(w);
763 w->body.q0 = w->addr.q0;
764 w->body.q1 = w->addr.q1;
765 textsetselect(&w->body, w->body.q0, w->body.q1);
766 settag = TRUE;
767 m = 8;
768 }else
769 if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */
770 w->addr.q0 = w->body.q0;
771 w->addr.q1 = w->body.q1;
772 m = 8;
773 }else
774 if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */
775 textcommit(&w->body, TRUE);
776 clampaddr(w);
777 w->limit.q0 = w->addr.q0;
778 w->limit.q1 = w->addr.q1;
779 m = 10;
780 }else
781 if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */
782 w->nomark = TRUE;
783 m = 6;
784 }else
785 if(strncmp(p, "mark", 4) == 0){ /* mark file */
786 seq++;
787 filemark(w->body.file);
788 settag = TRUE;
789 m = 4;
790 }else
791 if(strncmp(p, "nomenu", 6) == 0){ /* turn off automatic menu */
792 w->filemenu = FALSE;
793 settag = TRUE;
794 m = 6;
795 }else
796 if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */
797 w->filemenu = TRUE;
798 settag = TRUE;
799 m = 4;
800 }else
801 if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */
802 wincleartag(w);
803 settag = TRUE;
804 m = 8;
805 }else{
806 err = Ebadctl;
807 break;
809 while(p[m] == '\n')
810 m++;
813 if(isfbuf)
814 fbuffree(r);
815 else
816 free(r);
817 if(err)
818 n = 0;
819 fc.count = n;
820 respond(x, &fc, err);
821 if(settag)
822 winsettag(w);
823 if(scrdraw)
824 textscrdraw(&w->body);
827 void
828 xfideventwrite(Xfid *x, Window *w)
830 Fcall fc;
831 int m, n;
832 Rune *r;
833 char *err, *p, *q;
834 int isfbuf;
835 Text *t;
836 int c;
837 uint q0, q1;
839 err = nil;
840 isfbuf = TRUE;
841 if(x->fcall.count < RBUFSIZE)
842 r = fbufalloc();
843 else{
844 isfbuf = FALSE;
845 r = emalloc(x->fcall.count*UTFmax+1);
847 for(n=0; n<x->fcall.count; n+=m){
848 p = x->fcall.data+n;
849 w->owner = *p++; /* disgusting */
850 c = *p++;
851 while(*p == ' ')
852 p++;
853 q0 = strtoul(p, &q, 10);
854 if(q == p)
855 goto Rescue;
856 p = q;
857 while(*p == ' ')
858 p++;
859 q1 = strtoul(p, &q, 10);
860 if(q == p)
861 goto Rescue;
862 p = q;
863 while(*p == ' ')
864 p++;
865 if(*p++ != '\n')
866 goto Rescue;
867 m = p-(x->fcall.data+n);
868 if('a'<=c && c<='z')
869 t = &w->tag;
870 else if('A'<=c && c<='Z')
871 t = &w->body;
872 else
873 goto Rescue;
874 if(q0>t->file->b.nc || q1>t->file->b.nc || q0>q1)
875 goto Rescue;
877 qlock(&row.lk); /* just like mousethread */
878 switch(c){
879 case 'x':
880 case 'X':
881 execute(t, q0, q1, TRUE, nil);
882 break;
883 case 'l':
884 case 'L':
885 look3(t, q0, q1, TRUE);
886 break;
887 default:
888 qunlock(&row.lk);
889 goto Rescue;
891 qunlock(&row.lk);
895 Out:
896 if(isfbuf)
897 fbuffree(r);
898 else
899 free(r);
900 if(err)
901 n = 0;
902 fc.count = n;
903 respond(x, &fc, err);
904 return;
906 Rescue:
907 err = Ebadevent;
908 goto Out;
911 void
912 xfidutfread(Xfid *x, Text *t, uint q1, int qid)
914 Fcall fc;
915 Window *w;
916 Rune *r;
917 char *b, *b1;
918 uint q, off, boff;
919 int m, n, nr, nb;
921 w = t->w;
922 wincommit(w, t);
923 off = x->fcall.offset;
924 r = fbufalloc();
925 b = fbufalloc();
926 b1 = fbufalloc();
927 n = 0;
928 if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
929 boff = w->utflastboff;
930 q = w->utflastq;
931 }else{
932 /* BUG: stupid code: scan from beginning */
933 boff = 0;
934 q = 0;
936 w->utflastqid = qid;
937 while(q<q1 && n<x->fcall.count){
938 /*
939 * Updating here avoids partial rune problem: we're always on a
940 * char boundary. The cost is we will usually do one more read
941 * than we really need, but that's better than being n^2.
942 */
943 w->utflastboff = boff;
944 w->utflastq = q;
945 nr = q1-q;
946 if(nr > BUFSIZE/UTFmax)
947 nr = BUFSIZE/UTFmax;
948 bufread(&t->file->b, q, r, nr);
949 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
950 if(boff >= off){
951 m = nb;
952 if(boff+m > off+x->fcall.count)
953 m = off+x->fcall.count - boff;
954 memmove(b1+n, b, m);
955 n += m;
956 }else if(boff+nb > off){
957 if(n != 0)
958 error("bad count in utfrune");
959 m = nb - (off-boff);
960 if(m > x->fcall.count)
961 m = x->fcall.count;
962 memmove(b1, b+(off-boff), m);
963 n += m;
965 boff += nb;
966 q += nr;
968 fbuffree(r);
969 fbuffree(b);
970 fc.count = n;
971 fc.data = b1;
972 respond(x, &fc, nil);
973 fbuffree(b1);
976 int
977 xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
979 Fcall fc;
980 Window *w;
981 Rune *r, junk;
982 char *b, *b1;
983 uint q, boff;
984 int i, rw, m, n, nr, nb;
986 w = t->w;
987 wincommit(w, t);
988 r = fbufalloc();
989 b = fbufalloc();
990 b1 = fbufalloc();
991 n = 0;
992 q = q0;
993 boff = 0;
994 while(q<q1 && n<x->fcall.count){
995 nr = q1-q;
996 if(nr > BUFSIZE/UTFmax)
997 nr = BUFSIZE/UTFmax;
998 bufread(&t->file->b, q, r, nr);
999 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
1000 m = nb;
1001 if(boff+m > x->fcall.count){
1002 i = x->fcall.count - boff;
1003 /* copy whole runes only */
1004 m = 0;
1005 nr = 0;
1006 while(m < i){
1007 rw = chartorune(&junk, b+m);
1008 if(m+rw > i)
1009 break;
1010 m += rw;
1011 nr++;
1013 if(m == 0)
1014 break;
1016 memmove(b1+n, b, m);
1017 n += m;
1018 boff += nb;
1019 q += nr;
1021 fbuffree(r);
1022 fbuffree(b);
1023 fc.count = n;
1024 fc.data = b1;
1025 respond(x, &fc, nil);
1026 fbuffree(b1);
1027 return q-q0;
1030 void
1031 xfideventread(Xfid *x, Window *w)
1033 Fcall fc;
1034 int i, n;
1036 i = 0;
1037 x->flushed = FALSE;
1038 while(w->nevents == 0){
1039 if(i){
1040 if(!x->flushed)
1041 respond(x, &fc, "window shut down");
1042 return;
1044 w->eventx = x;
1045 winunlock(w);
1046 recvp(x->c);
1047 winlock(w, 'F');
1048 i++;
1051 n = w->nevents;
1052 if(n > x->fcall.count)
1053 n = x->fcall.count;
1054 fc.count = n;
1055 fc.data = w->events;
1056 respond(x, &fc, nil);
1057 w->nevents -= n;
1058 if(w->nevents){
1059 memmove(w->events, w->events+n, w->nevents);
1060 w->events = erealloc(w->events, w->nevents);
1061 }else{
1062 free(w->events);
1063 w->events = nil;
1067 void
1068 xfidindexread(Xfid *x)
1070 Fcall fc;
1071 int i, j, m, n, nmax, isbuf, cnt, off;
1072 Window *w;
1073 char *b;
1074 Rune *r;
1075 Column *c;
1077 qlock(&row.lk);
1078 nmax = 0;
1079 for(j=0; j<row.ncol; j++){
1080 c = row.col[j];
1081 for(i=0; i<c->nw; i++){
1082 w = c->w[i];
1083 nmax += Ctlsize + w->tag.file->b.nc*UTFmax + 1;
1086 nmax++;
1087 isbuf = (nmax<=RBUFSIZE);
1088 if(isbuf)
1089 b = (char*)x->buf;
1090 else
1091 b = emalloc(nmax);
1092 r = fbufalloc();
1093 n = 0;
1094 for(j=0; j<row.ncol; j++){
1095 c = row.col[j];
1096 for(i=0; i<c->nw; i++){
1097 w = c->w[i];
1098 /* only show the currently active window of a set */
1099 if(w->body.file->curtext != &w->body)
1100 continue;
1101 winctlprint(w, b+n, 0);
1102 n += Ctlsize;
1103 m = min(RBUFSIZE, w->tag.file->b.nc);
1104 bufread(&w->tag.file->b, 0, r, m);
1105 m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1106 while(n<m && b[n]!='\n')
1107 n++;
1108 b[n++] = '\n';
1111 qunlock(&row.lk);
1112 off = x->fcall.offset;
1113 cnt = x->fcall.count;
1114 if(off > n)
1115 off = n;
1116 if(off+cnt > n)
1117 cnt = n-off;
1118 fc.count = cnt;
1119 memmove(r, b+off, cnt);
1120 fc.data = (char*)r;
1121 if(!isbuf)
1122 free(b);
1123 respond(x, &fc, nil);
1124 fbuffree(r);