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 q = FILE(x->f->qid);
102 if(w){
103 winlock(w, 'E');
104 switch(q){
105 case QWaddr:
106 if(w->nopen[q]++ == 0){
107 w->addr = range(0, 0);
108 w->limit = range(-1,-1);
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);
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;
137 w->rdselfd = tempfile();
138 if(w->rdselfd < 0){
139 winunlock(w);
140 respond(x, &fc, "can't create temp file");
141 return;
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->b, 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;
158 q0 += n;
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;
177 if(!canqlock(&w->editoutlk)){
178 winunlock(w);
179 respond(x, &fc, Einuse);
180 return;
182 w->wrselrange = range(t->q1, t->q1);
183 break;
185 winunlock(w);
187 else{
188 switch(q){
189 case Qeditout:
190 if(!canqlock(&editoutlk)){
191 respond(x, &fc, Einuse);
192 return;
194 break;
197 fc.qid = x->f->qid;
198 fc.iounit = messagesize-IOHDRSZ;
199 x->f->open = TRUE;
200 respond(x, &fc, nil);
203 void
204 xfidclose(Xfid *x)
206 Fcall fc;
207 Window *w;
208 int q;
209 Text *t;
211 w = x->f->w;
212 x->f->busy = FALSE;
213 x->f->w = nil;
214 if(x->f->open == FALSE){
215 if(w != nil)
216 winclose(w);
217 respond(x, &fc, nil);
218 return;
221 q = FILE(x->f->qid);
222 x->f->open = FALSE;
223 if(w){
224 winlock(w, 'E');
225 switch(q){
226 case QWctl:
227 if(w->ctlfid!=~0 && w->ctlfid==x->f->fid){
228 w->ctlfid = ~0;
229 qunlock(&w->ctllock);
231 break;
232 case QWdata:
233 case QWxdata:
234 w->nomark = FALSE;
235 /* fall through */
236 case QWaddr:
237 case QWevent: /* BUG: do we need to shut down Xfid? */
238 if(--w->nopen[q] == 0){
239 if(q == QWdata || q == QWxdata)
240 w->nomark = FALSE;
241 if(q==QWevent && !w->isdir && w->col!=nil){
242 w->filemenu = TRUE;
243 winsettag(w);
245 if(q == QWevent){
246 free(w->dumpstr);
247 free(w->dumpdir);
248 w->dumpstr = nil;
249 w->dumpdir = nil;
252 break;
253 case QWrdsel:
254 close(w->rdselfd);
255 w->rdselfd = 0;
256 break;
257 case QWwrsel:
258 w->nomark = FALSE;
259 t = &w->body;
260 /* before: only did this if !w->noscroll, but that didn't seem right in practice */
261 textshow(t, min(w->wrselrange.q0, t->file->b.nc),
262 min(w->wrselrange.q1, t->file->b.nc), 1);
263 textscrdraw(t);
264 break;
265 case QWeditout:
266 qunlock(&w->editoutlk);
267 break;
269 winunlock(w);
270 winclose(w);
272 else{
273 switch(q){
274 case Qeditout:
275 qunlock(&editoutlk);
276 break;
279 respond(x, &fc, nil);
282 void
283 xfidread(Xfid *x)
285 Fcall fc;
286 int n, q;
287 uint off;
288 char *b;
289 char buf[256];
290 Window *w;
292 q = FILE(x->f->qid);
293 w = x->f->w;
294 if(w == nil){
295 fc.count = 0;
296 switch(q){
297 case Qcons:
298 case Qlabel:
299 break;
300 case Qindex:
301 xfidindexread(x);
302 return;
303 default:
304 warning(nil, "unknown qid %d\n", q);
305 break;
307 respond(x, &fc, nil);
308 return;
310 winlock(w, 'F');
311 if(w->col == nil){
312 winunlock(w);
313 respond(x, &fc, Edel);
314 return;
316 off = x->fcall.offset;
317 switch(q){
318 case QWaddr:
319 textcommit(&w->body, TRUE);
320 clampaddr(w);
321 sprint(buf, "%11d %11d ", w->addr.q0, w->addr.q1);
322 goto Readbuf;
324 case QWbody:
325 xfidutfread(x, &w->body, w->body.file->b.nc, QWbody);
326 break;
328 case QWctl:
329 b = winctlprint(w, buf, 1);
330 goto Readb;
332 Readbuf:
333 b = buf;
334 Readb:
335 n = strlen(b);
336 if(off > n)
337 off = n;
338 if(off+x->fcall.count > n)
339 x->fcall.count = n-off;
340 fc.count = x->fcall.count;
341 fc.data = b+off;
342 respond(x, &fc, nil);
343 if(b != buf)
344 free(b);
345 break;
347 case QWevent:
348 xfideventread(x, w);
349 break;
351 case QWdata:
352 /* BUG: what should happen if q1 > q0? */
353 if(w->addr.q0 > w->body.file->b.nc){
354 respond(x, &fc, Eaddr);
355 break;
357 w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->b.nc);
358 w->addr.q1 = w->addr.q0;
359 break;
361 case QWxdata:
362 /* BUG: what should happen if q1 > q0? */
363 if(w->addr.q0 > w->body.file->b.nc){
364 respond(x, &fc, Eaddr);
365 break;
367 w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1);
368 break;
370 case QWtag:
371 xfidutfread(x, &w->tag, w->tag.file->b.nc, QWtag);
372 break;
374 case QWrdsel:
375 seek(w->rdselfd, off, 0);
376 n = x->fcall.count;
377 if(n > BUFSIZE)
378 n = BUFSIZE;
379 b = fbufalloc();
380 n = read(w->rdselfd, b, n);
381 if(n < 0){
382 respond(x, &fc, "I/O error in temp file");
383 break;
385 fc.count = n;
386 fc.data = b;
387 respond(x, &fc, nil);
388 fbuffree(b);
389 break;
391 default:
392 sprint(buf, "unknown qid %d in read", q);
393 respond(x, &fc, nil);
395 winunlock(w);
398 static int
399 shouldscroll(Text *t, uint q0, int qid)
401 if(qid == Qcons)
402 return TRUE;
403 return t->org <= q0 && q0 <= t->org+t->fr.nchars;
406 void
407 xfidwrite(Xfid *x)
409 Fcall fc;
410 int c, cnt, qid, q, nb, nr, eval;
411 char buf[64], *err;
412 Window *w;
413 Rune *r;
414 Range a;
415 Text *t;
416 uint q0, tq0, tq1;
418 qid = FILE(x->f->qid);
419 w = x->f->w;
420 if(w){
421 c = 'F';
422 if(qid==QWtag || qid==QWbody)
423 c = 'E';
424 winlock(w, c);
425 if(w->col == nil){
426 winunlock(w);
427 respond(x, &fc, Edel);
428 return;
431 x->fcall.data[x->fcall.count] = 0;
432 switch(qid){
433 case Qcons:
434 w = errorwin(x->f->mntdir, 'X');
435 t=&w->body;
436 goto BodyTag;
438 case Qlabel:
439 fc.count = x->fcall.count;
440 respond(x, &fc, nil);
441 break;
443 case QWaddr:
444 x->fcall.data[x->fcall.count] = 0;
445 r = bytetorune(x->fcall.data, &nr);
446 t = &w->body;
447 wincommit(w, t);
448 eval = TRUE;
449 a = address(FALSE, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb);
450 free(r);
451 if(nb < nr){
452 respond(x, &fc, Ebadaddr);
453 break;
455 if(!eval){
456 respond(x, &fc, Eaddr);
457 break;
459 w->addr = a;
460 fc.count = x->fcall.count;
461 respond(x, &fc, nil);
462 break;
464 case Qeditout:
465 case QWeditout:
466 r = bytetorune(x->fcall.data, &nr);
467 if(w)
468 err = edittext(w, w->wrselrange.q1, r, nr);
469 else
470 err = edittext(nil, 0, r, nr);
471 free(r);
472 if(err != nil){
473 respond(x, &fc, err);
474 break;
476 fc.count = x->fcall.count;
477 respond(x, &fc, nil);
478 break;
480 case QWerrors:
481 w = errorwinforwin(w);
482 t = &w->body;
483 goto BodyTag;
485 case QWbody:
486 case QWwrsel:
487 t = &w->body;
488 goto BodyTag;
490 case QWctl:
491 xfidctlwrite(x, w);
492 break;
494 case QWdata:
495 a = w->addr;
496 t = &w->body;
497 wincommit(w, t);
498 if(a.q0>t->file->b.nc || a.q1>t->file->b.nc){
499 respond(x, &fc, Eaddr);
500 break;
502 r = runemalloc(x->fcall.count);
503 cvttorunes(x->fcall.data, x->fcall.count, r, &nb, &nr, nil);
504 if(w->nomark == FALSE){
505 seq++;
506 filemark(t->file);
508 q0 = a.q0;
509 if(a.q1 > q0){
510 textdelete(t, q0, a.q1, TRUE);
511 w->addr.q1 = q0;
513 tq0 = t->q0;
514 tq1 = t->q1;
515 textinsert(t, q0, r, nr, TRUE);
516 if(tq0 >= q0)
517 tq0 += nr;
518 if(tq1 >= q0)
519 tq1 += nr;
520 textsetselect(t, tq0, tq1);
521 if(shouldscroll(t, q0, qid))
522 textshow(t, q0+nr, q0+nr, 0);
523 textscrdraw(t);
524 winsettag(w);
525 free(r);
526 w->addr.q0 += nr;
527 w->addr.q1 = w->addr.q0;
528 fc.count = x->fcall.count;
529 respond(x, &fc, nil);
530 break;
532 case QWevent:
533 xfideventwrite(x, w);
534 break;
536 case QWtag:
537 t = &w->tag;
538 goto BodyTag;
540 BodyTag:
541 q = x->f->nrpart;
542 cnt = x->fcall.count;
543 if(q > 0){
544 memmove(x->fcall.data+q, x->fcall.data, cnt); /* there's room; see fsysproc */
545 memmove(x->fcall.data, x->f->rpart, q);
546 cnt += q;
547 x->f->nrpart = 0;
549 r = runemalloc(cnt);
550 cvttorunes(x->fcall.data, cnt-UTFmax, r, &nb, &nr, nil);
551 /* approach end of buffer */
552 while(fullrune(x->fcall.data+nb, cnt-nb)){
553 c = nb;
554 nb += chartorune(&r[nr], x->fcall.data+c);
555 if(r[nr])
556 nr++;
558 if(nb < cnt){
559 memmove(x->f->rpart, x->fcall.data+nb, cnt-nb);
560 x->f->nrpart = cnt-nb;
562 if(nr > 0){
563 wincommit(w, t);
564 if(qid == QWwrsel){
565 q0 = w->wrselrange.q1;
566 if(q0 > t->file->b.nc)
567 q0 = t->file->b.nc;
568 }else
569 q0 = t->file->b.nc;
570 if(qid == QWtag)
571 textinsert(t, q0, r, nr, TRUE);
572 else{
573 if(w->nomark == FALSE){
574 seq++;
575 filemark(t->file);
577 q0 = textbsinsert(t, q0, r, nr, TRUE, &nr);
578 textsetselect(t, t->q0, t->q1); /* insert could leave it somewhere else */
579 if(qid!=QWwrsel && shouldscroll(t, q0, qid))
580 textshow(t, q0+nr, q0+nr, 1);
581 textscrdraw(t);
583 winsettag(w);
584 if(qid == QWwrsel)
585 w->wrselrange.q1 += nr;
586 free(r);
588 fc.count = x->fcall.count;
589 respond(x, &fc, nil);
590 break;
592 default:
593 sprint(buf, "unknown qid %d in write", qid);
594 respond(x, &fc, buf);
595 break;
597 if(w)
598 winunlock(w);
601 void
602 xfidctlwrite(Xfid *x, Window *w)
604 Fcall fc;
605 int i, m, n, nb, nr, nulls;
606 Rune *r;
607 char *err, *p, *pp, *q, *e;
608 int isfbuf, scrdraw, settag;
609 Text *t;
611 err = nil;
612 e = x->fcall.data+x->fcall.count;
613 scrdraw = FALSE;
614 settag = FALSE;
615 isfbuf = TRUE;
616 if(x->fcall.count < RBUFSIZE)
617 r = fbufalloc();
618 else{
619 isfbuf = FALSE;
620 r = emalloc(x->fcall.count*UTFmax+1);
622 x->fcall.data[x->fcall.count] = 0;
623 textcommit(&w->tag, TRUE);
624 for(n=0; n<x->fcall.count; n+=m){
625 p = x->fcall.data+n;
626 if(strncmp(p, "lock", 4) == 0){ /* make window exclusive use */
627 qlock(&w->ctllock);
628 w->ctlfid = x->f->fid;
629 m = 4;
630 }else
631 if(strncmp(p, "unlock", 6) == 0){ /* release exclusive use */
632 w->ctlfid = ~0;
633 qunlock(&w->ctllock);
634 m = 6;
635 }else
636 if(strncmp(p, "clean", 5) == 0){ /* mark window 'clean', seq=0 */
637 t = &w->body;
638 t->eq0 = ~0;
639 filereset(t->file);
640 t->file->mod = FALSE;
641 w->dirty = FALSE;
642 settag = TRUE;
643 m = 5;
644 }else
645 if(strncmp(p, "dirty", 5) == 0){ /* mark window 'dirty' */
646 t = &w->body;
647 /* doesn't change sequence number, so "Put" won't appear. it shouldn't. */
648 t->file->mod = TRUE;
649 w->dirty = TRUE;
650 settag = TRUE;
651 m = 5;
652 }else
653 if(strncmp(p, "show", 4) == 0){ /* show dot */
654 t = &w->body;
655 textshow(t, t->q0, t->q1, 1);
656 m = 4;
657 }else
658 if(strncmp(p, "name ", 5) == 0){ /* set file name */
659 pp = p+5;
660 m = 5;
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 file name";
671 break;
673 for(i=0; i<nr; i++)
674 if(r[i] <= ' '){
675 err = "bad character in file name";
676 goto out;
678 out:
679 seq++;
680 filemark(w->body.file);
681 winsetname(w, r, nr);
682 m += (q+1) - pp;
683 }else
684 if(strncmp(p, "dump ", 5) == 0){ /* set dump string */
685 pp = p+5;
686 m = 5;
687 q = memchr(pp, '\n', e-pp);
688 if(q==nil || q==pp){
689 err = Ebadctl;
690 break;
692 *q = 0;
693 nulls = FALSE;
694 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
695 if(nulls){
696 err = "nulls in dump string";
697 break;
699 w->dumpstr = runetobyte(r, nr);
700 m += (q+1) - pp;
701 }else
702 if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */
703 pp = p+8;
704 m = 8;
705 q = memchr(pp, '\n', e-pp);
706 if(q==nil || q==pp){
707 err = Ebadctl;
708 break;
710 *q = 0;
711 nulls = FALSE;
712 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
713 if(nulls){
714 err = "nulls in dump directory string";
715 break;
717 w->dumpdir = runetobyte(r, nr);
718 m += (q+1) - pp;
719 }else
720 if(strncmp(p, "delete", 6) == 0){ /* delete for sure */
721 colclose(w->col, w, TRUE);
722 m = 6;
723 }else
724 if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */
725 if(!winclean(w, TRUE)){
726 err = "file dirty";
727 break;
729 colclose(w->col, w, TRUE);
730 m = 3;
731 }else
732 if(strncmp(p, "get", 3) == 0){ /* get file */
733 get(&w->body, nil, nil, FALSE, XXX, nil, 0);
734 m = 3;
735 }else
736 if(strncmp(p, "put", 3) == 0){ /* put file */
737 put(&w->body, nil, nil, XXX, XXX, nil, 0);
738 m = 3;
739 }else
740 if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */
741 textcommit(&w->body, TRUE);
742 clampaddr(w);
743 w->body.q0 = w->addr.q0;
744 w->body.q1 = w->addr.q1;
745 textsetselect(&w->body, w->body.q0, w->body.q1);
746 settag = TRUE;
747 m = 8;
748 }else
749 if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */
750 w->addr.q0 = w->body.q0;
751 w->addr.q1 = w->body.q1;
752 m = 8;
753 }else
754 if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */
755 textcommit(&w->body, TRUE);
756 clampaddr(w);
757 w->limit.q0 = w->addr.q0;
758 w->limit.q1 = w->addr.q1;
759 m = 10;
760 }else
761 if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */
762 w->nomark = TRUE;
763 m = 6;
764 }else
765 if(strncmp(p, "mark", 4) == 0){ /* mark file */
766 seq++;
767 filemark(w->body.file);
768 settag = TRUE;
769 m = 4;
770 }else
771 if(strncmp(p, "nomenu", 6) == 0){ /* turn off automatic menu */
772 w->filemenu = FALSE;
773 m = 6;
774 }else
775 if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */
776 w->filemenu = TRUE;
777 m = 4;
778 }else
779 if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */
780 wincleartag(w);
781 settag = TRUE;
782 m = 8;
783 }else{
784 err = Ebadctl;
785 break;
787 while(p[m] == '\n')
788 m++;
791 if(isfbuf)
792 fbuffree(r);
793 else
794 free(r);
795 if(err)
796 n = 0;
797 fc.count = n;
798 respond(x, &fc, err);
799 if(settag)
800 winsettag(w);
801 if(scrdraw)
802 textscrdraw(&w->body);
805 void
806 xfideventwrite(Xfid *x, Window *w)
808 Fcall fc;
809 int m, n;
810 Rune *r;
811 char *err, *p, *q;
812 int isfbuf;
813 Text *t;
814 int c;
815 uint q0, q1;
817 err = nil;
818 isfbuf = TRUE;
819 if(x->fcall.count < RBUFSIZE)
820 r = fbufalloc();
821 else{
822 isfbuf = FALSE;
823 r = emalloc(x->fcall.count*UTFmax+1);
825 for(n=0; n<x->fcall.count; n+=m){
826 p = x->fcall.data+n;
827 w->owner = *p++; /* disgusting */
828 c = *p++;
829 while(*p == ' ')
830 p++;
831 q0 = strtoul(p, &q, 10);
832 if(q == p)
833 goto Rescue;
834 p = q;
835 while(*p == ' ')
836 p++;
837 q1 = strtoul(p, &q, 10);
838 if(q == p)
839 goto Rescue;
840 p = q;
841 while(*p == ' ')
842 p++;
843 if(*p++ != '\n')
844 goto Rescue;
845 m = p-(x->fcall.data+n);
846 if('a'<=c && c<='z')
847 t = &w->tag;
848 else if('A'<=c && c<='Z')
849 t = &w->body;
850 else
851 goto Rescue;
852 if(q0>t->file->b.nc || q1>t->file->b.nc || q0>q1)
853 goto Rescue;
855 qlock(&row.lk); /* just like mousethread */
856 switch(c){
857 case 'x':
858 case 'X':
859 execute(t, q0, q1, TRUE, nil);
860 break;
861 case 'l':
862 case 'L':
863 look3(t, q0, q1, TRUE);
864 break;
865 default:
866 qunlock(&row.lk);
867 goto Rescue;
869 qunlock(&row.lk);
873 Out:
874 if(isfbuf)
875 fbuffree(r);
876 else
877 free(r);
878 if(err)
879 n = 0;
880 fc.count = n;
881 respond(x, &fc, err);
882 return;
884 Rescue:
885 err = Ebadevent;
886 goto Out;
889 void
890 xfidutfread(Xfid *x, Text *t, uint q1, int qid)
892 Fcall fc;
893 Window *w;
894 Rune *r;
895 char *b, *b1;
896 uint q, off, boff;
897 int m, n, nr, nb;
899 w = t->w;
900 wincommit(w, t);
901 off = x->fcall.offset;
902 r = fbufalloc();
903 b = fbufalloc();
904 b1 = fbufalloc();
905 n = 0;
906 if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
907 boff = w->utflastboff;
908 q = w->utflastq;
909 }else{
910 /* BUG: stupid code: scan from beginning */
911 boff = 0;
912 q = 0;
914 w->utflastqid = qid;
915 while(q<q1 && n<x->fcall.count){
916 /*
917 * Updating here avoids partial rune problem: we're always on a
918 * char boundary. The cost is we will usually do one more read
919 * than we really need, but that's better than being n^2.
920 */
921 w->utflastboff = boff;
922 w->utflastq = q;
923 nr = q1-q;
924 if(nr > BUFSIZE/UTFmax)
925 nr = BUFSIZE/UTFmax;
926 bufread(&t->file->b, q, r, nr);
927 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
928 if(boff >= off){
929 m = nb;
930 if(boff+m > off+x->fcall.count)
931 m = off+x->fcall.count - boff;
932 memmove(b1+n, b, m);
933 n += m;
934 }else if(boff+nb > off){
935 if(n != 0)
936 error("bad count in utfrune");
937 m = nb - (off-boff);
938 if(m > x->fcall.count)
939 m = x->fcall.count;
940 memmove(b1, b+(off-boff), m);
941 n += m;
943 boff += nb;
944 q += nr;
946 fbuffree(r);
947 fbuffree(b);
948 fc.count = n;
949 fc.data = b1;
950 respond(x, &fc, nil);
951 fbuffree(b1);
954 int
955 xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
957 Fcall fc;
958 Window *w;
959 Rune *r, junk;
960 char *b, *b1;
961 uint q, boff;
962 int i, rw, m, n, nr, nb;
964 w = t->w;
965 wincommit(w, t);
966 r = fbufalloc();
967 b = fbufalloc();
968 b1 = fbufalloc();
969 n = 0;
970 q = q0;
971 boff = 0;
972 while(q<q1 && n<x->fcall.count){
973 nr = q1-q;
974 if(nr > BUFSIZE/UTFmax)
975 nr = BUFSIZE/UTFmax;
976 bufread(&t->file->b, q, r, nr);
977 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
978 m = nb;
979 if(boff+m > x->fcall.count){
980 i = x->fcall.count - boff;
981 /* copy whole runes only */
982 m = 0;
983 nr = 0;
984 while(m < i){
985 rw = chartorune(&junk, b+m);
986 if(m+rw > i)
987 break;
988 m += rw;
989 nr++;
991 if(m == 0)
992 break;
994 memmove(b1+n, b, m);
995 n += m;
996 boff += nb;
997 q += nr;
999 fbuffree(r);
1000 fbuffree(b);
1001 fc.count = n;
1002 fc.data = b1;
1003 respond(x, &fc, nil);
1004 fbuffree(b1);
1005 return q-q0;
1008 void
1009 xfideventread(Xfid *x, Window *w)
1011 Fcall fc;
1012 int i, n;
1014 i = 0;
1015 x->flushed = FALSE;
1016 while(w->nevents == 0){
1017 if(i){
1018 if(!x->flushed)
1019 respond(x, &fc, "window shut down");
1020 return;
1022 w->eventx = x;
1023 winunlock(w);
1024 recvp(x->c);
1025 winlock(w, 'F');
1026 i++;
1029 n = w->nevents;
1030 if(n > x->fcall.count)
1031 n = x->fcall.count;
1032 fc.count = n;
1033 fc.data = w->events;
1034 respond(x, &fc, nil);
1035 w->nevents -= n;
1036 if(w->nevents){
1037 memmove(w->events, w->events+n, w->nevents);
1038 w->events = erealloc(w->events, w->nevents);
1039 }else{
1040 free(w->events);
1041 w->events = nil;
1045 void
1046 xfidindexread(Xfid *x)
1048 Fcall fc;
1049 int i, j, m, n, nmax, isbuf, cnt, off;
1050 Window *w;
1051 char *b;
1052 Rune *r;
1053 Column *c;
1055 qlock(&row.lk);
1056 nmax = 0;
1057 for(j=0; j<row.ncol; j++){
1058 c = row.col[j];
1059 for(i=0; i<c->nw; i++){
1060 w = c->w[i];
1061 nmax += Ctlsize + w->tag.file->b.nc*UTFmax + 1;
1064 nmax++;
1065 isbuf = (nmax<=RBUFSIZE);
1066 if(isbuf)
1067 b = (char*)x->buf;
1068 else
1069 b = emalloc(nmax);
1070 r = fbufalloc();
1071 n = 0;
1072 for(j=0; j<row.ncol; j++){
1073 c = row.col[j];
1074 for(i=0; i<c->nw; i++){
1075 w = c->w[i];
1076 /* only show the currently active window of a set */
1077 if(w->body.file->curtext != &w->body)
1078 continue;
1079 winctlprint(w, b+n, 0);
1080 n += Ctlsize;
1081 m = min(RBUFSIZE, w->tag.file->b.nc);
1082 bufread(&w->tag.file->b, 0, r, m);
1083 m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1084 while(n<m && b[n]!='\n')
1085 n++;
1086 b[n++] = '\n';
1089 qunlock(&row.lk);
1090 off = x->fcall.offset;
1091 cnt = x->fcall.count;
1092 if(off > n)
1093 off = n;
1094 if(off+cnt > n)
1095 cnt = n-off;
1096 fc.count = cnt;
1097 memmove(r, b+off, cnt);
1098 fc.data = (char*)r;
1099 if(!isbuf)
1100 free(b);
1101 respond(x, &fc, nil);
1102 fbuffree(r);