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