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 static Rune*
407 fullrunewrite(Xfid *x, int *inr)
409 int q, cnt, c, nb, nr;
410 Rune *r;
412 q = x->f->nrpart;
413 cnt = x->fcall.count;
414 if(q > 0){
415 memmove(x->fcall.data+q, x->fcall.data, cnt); /* there's room; see fsysproc */
416 memmove(x->fcall.data, x->f->rpart, q);
417 cnt += q;
418 x->f->nrpart = 0;
420 r = runemalloc(cnt);
421 cvttorunes(x->fcall.data, cnt-UTFmax, r, &nb, &nr, nil);
422 /* approach end of buffer */
423 while(fullrune(x->fcall.data+nb, cnt-nb)){
424 c = nb;
425 nb += chartorune(&r[nr], x->fcall.data+c);
426 if(r[nr])
427 nr++;
429 if(nb < cnt){
430 memmove(x->f->rpart, x->fcall.data+nb, cnt-nb);
431 x->f->nrpart = cnt-nb;
433 *inr = nr;
434 return r;
437 void
438 xfidwrite(Xfid *x)
440 Fcall fc;
441 int c, qid, nb, nr, eval;
442 char buf[64], *err;
443 Window *w;
444 Rune *r;
445 Range a;
446 Text *t;
447 uint q0, tq0, tq1;
449 qid = FILE(x->f->qid);
450 w = x->f->w;
451 if(w){
452 c = 'F';
453 if(qid==QWtag || qid==QWbody)
454 c = 'E';
455 winlock(w, c);
456 if(w->col == nil){
457 winunlock(w);
458 respond(x, &fc, Edel);
459 return;
462 x->fcall.data[x->fcall.count] = 0;
463 switch(qid){
464 case Qcons:
465 w = errorwin(x->f->mntdir, 'X');
466 t=&w->body;
467 goto BodyTag;
469 case Qlabel:
470 fc.count = x->fcall.count;
471 respond(x, &fc, nil);
472 break;
474 case QWaddr:
475 x->fcall.data[x->fcall.count] = 0;
476 r = bytetorune(x->fcall.data, &nr);
477 t = &w->body;
478 wincommit(w, t);
479 eval = TRUE;
480 a = address(FALSE, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb);
481 free(r);
482 if(nb < nr){
483 respond(x, &fc, Ebadaddr);
484 break;
486 if(!eval){
487 respond(x, &fc, Eaddr);
488 break;
490 w->addr = a;
491 fc.count = x->fcall.count;
492 respond(x, &fc, nil);
493 break;
495 case Qeditout:
496 case QWeditout:
497 r = fullrunewrite(x, &nr);
498 if(w)
499 err = edittext(w, w->wrselrange.q1, r, nr);
500 else
501 err = edittext(nil, 0, r, nr);
502 free(r);
503 if(err != nil){
504 respond(x, &fc, err);
505 break;
507 fc.count = x->fcall.count;
508 respond(x, &fc, nil);
509 break;
511 case QWerrors:
512 w = errorwinforwin(w);
513 t = &w->body;
514 goto BodyTag;
516 case QWbody:
517 case QWwrsel:
518 t = &w->body;
519 goto BodyTag;
521 case QWctl:
522 xfidctlwrite(x, w);
523 break;
525 case QWdata:
526 a = w->addr;
527 t = &w->body;
528 wincommit(w, t);
529 if(a.q0>t->file->b.nc || a.q1>t->file->b.nc){
530 respond(x, &fc, Eaddr);
531 break;
533 r = runemalloc(x->fcall.count);
534 cvttorunes(x->fcall.data, x->fcall.count, r, &nb, &nr, nil);
535 if(w->nomark == FALSE){
536 seq++;
537 filemark(t->file);
539 q0 = a.q0;
540 if(a.q1 > q0){
541 textdelete(t, q0, a.q1, TRUE);
542 w->addr.q1 = q0;
544 tq0 = t->q0;
545 tq1 = t->q1;
546 textinsert(t, q0, r, nr, TRUE);
547 if(tq0 >= q0)
548 tq0 += nr;
549 if(tq1 >= q0)
550 tq1 += nr;
551 textsetselect(t, tq0, tq1);
552 if(shouldscroll(t, q0, qid))
553 textshow(t, q0+nr, q0+nr, 0);
554 textscrdraw(t);
555 winsettag(w);
556 free(r);
557 w->addr.q0 += nr;
558 w->addr.q1 = w->addr.q0;
559 fc.count = x->fcall.count;
560 respond(x, &fc, nil);
561 break;
563 case QWevent:
564 xfideventwrite(x, w);
565 break;
567 case QWtag:
568 t = &w->tag;
569 goto BodyTag;
571 BodyTag:
572 r = fullrunewrite(x, &nr);
573 if(nr > 0){
574 wincommit(w, t);
575 if(qid == QWwrsel){
576 q0 = w->wrselrange.q1;
577 if(q0 > t->file->b.nc)
578 q0 = t->file->b.nc;
579 }else
580 q0 = t->file->b.nc;
581 if(qid == QWtag)
582 textinsert(t, q0, r, nr, TRUE);
583 else{
584 if(w->nomark == FALSE){
585 seq++;
586 filemark(t->file);
588 q0 = textbsinsert(t, q0, r, nr, TRUE, &nr);
589 textsetselect(t, t->q0, t->q1); /* insert could leave it somewhere else */
590 if(qid!=QWwrsel && shouldscroll(t, q0, qid))
591 textshow(t, q0+nr, q0+nr, 1);
592 textscrdraw(t);
594 winsettag(w);
595 if(qid == QWwrsel)
596 w->wrselrange.q1 += nr;
597 free(r);
599 fc.count = x->fcall.count;
600 respond(x, &fc, nil);
601 break;
603 default:
604 sprint(buf, "unknown qid %d in write", qid);
605 respond(x, &fc, buf);
606 break;
608 if(w)
609 winunlock(w);
612 void
613 xfidctlwrite(Xfid *x, Window *w)
615 Fcall fc;
616 int i, m, n, nb, nr, nulls;
617 Rune *r;
618 char *err, *p, *pp, *q, *e;
619 int isfbuf, scrdraw, settag;
620 Text *t;
622 err = nil;
623 e = x->fcall.data+x->fcall.count;
624 scrdraw = FALSE;
625 settag = FALSE;
626 isfbuf = TRUE;
627 if(x->fcall.count < RBUFSIZE)
628 r = fbufalloc();
629 else{
630 isfbuf = FALSE;
631 r = emalloc(x->fcall.count*UTFmax+1);
633 x->fcall.data[x->fcall.count] = 0;
634 textcommit(&w->tag, TRUE);
635 for(n=0; n<x->fcall.count; n+=m){
636 p = x->fcall.data+n;
637 if(strncmp(p, "lock", 4) == 0){ /* make window exclusive use */
638 qlock(&w->ctllock);
639 w->ctlfid = x->f->fid;
640 m = 4;
641 }else
642 if(strncmp(p, "unlock", 6) == 0){ /* release exclusive use */
643 w->ctlfid = ~0;
644 qunlock(&w->ctllock);
645 m = 6;
646 }else
647 if(strncmp(p, "clean", 5) == 0){ /* mark window 'clean', seq=0 */
648 t = &w->body;
649 t->eq0 = ~0;
650 filereset(t->file);
651 t->file->mod = FALSE;
652 w->dirty = FALSE;
653 settag = TRUE;
654 m = 5;
655 }else
656 if(strncmp(p, "dirty", 5) == 0){ /* mark window 'dirty' */
657 t = &w->body;
658 /* doesn't change sequence number, so "Put" won't appear. it shouldn't. */
659 t->file->mod = TRUE;
660 w->dirty = TRUE;
661 settag = TRUE;
662 m = 5;
663 }else
664 if(strncmp(p, "show", 4) == 0){ /* show dot */
665 t = &w->body;
666 textshow(t, t->q0, t->q1, 1);
667 m = 4;
668 }else
669 if(strncmp(p, "name ", 5) == 0){ /* set file name */
670 pp = p+5;
671 m = 5;
672 q = memchr(pp, '\n', e-pp);
673 if(q==nil || q==pp){
674 err = Ebadctl;
675 break;
677 *q = 0;
678 nulls = FALSE;
679 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
680 if(nulls){
681 err = "nulls in file name";
682 break;
684 for(i=0; i<nr; i++)
685 if(r[i] <= ' '){
686 err = "bad character in file name";
687 goto out;
689 out:
690 seq++;
691 filemark(w->body.file);
692 winsetname(w, r, nr);
693 m += (q+1) - pp;
694 }else
695 if(strncmp(p, "dump ", 5) == 0){ /* set dump string */
696 pp = p+5;
697 m = 5;
698 q = memchr(pp, '\n', e-pp);
699 if(q==nil || q==pp){
700 err = Ebadctl;
701 break;
703 *q = 0;
704 nulls = FALSE;
705 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
706 if(nulls){
707 err = "nulls in dump string";
708 break;
710 w->dumpstr = runetobyte(r, nr);
711 m += (q+1) - pp;
712 }else
713 if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */
714 pp = p+8;
715 m = 8;
716 q = memchr(pp, '\n', e-pp);
717 if(q==nil || q==pp){
718 err = Ebadctl;
719 break;
721 *q = 0;
722 nulls = FALSE;
723 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
724 if(nulls){
725 err = "nulls in dump directory string";
726 break;
728 w->dumpdir = runetobyte(r, nr);
729 m += (q+1) - pp;
730 }else
731 if(strncmp(p, "delete", 6) == 0){ /* delete for sure */
732 colclose(w->col, w, TRUE);
733 m = 6;
734 }else
735 if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */
736 if(!winclean(w, TRUE)){
737 err = "file dirty";
738 break;
740 colclose(w->col, w, TRUE);
741 m = 3;
742 }else
743 if(strncmp(p, "get", 3) == 0){ /* get file */
744 get(&w->body, nil, nil, FALSE, XXX, nil, 0);
745 m = 3;
746 }else
747 if(strncmp(p, "put", 3) == 0){ /* put file */
748 put(&w->body, nil, nil, XXX, XXX, nil, 0);
749 m = 3;
750 }else
751 if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */
752 textcommit(&w->body, TRUE);
753 clampaddr(w);
754 w->body.q0 = w->addr.q0;
755 w->body.q1 = w->addr.q1;
756 textsetselect(&w->body, w->body.q0, w->body.q1);
757 settag = TRUE;
758 m = 8;
759 }else
760 if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */
761 w->addr.q0 = w->body.q0;
762 w->addr.q1 = w->body.q1;
763 m = 8;
764 }else
765 if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */
766 textcommit(&w->body, TRUE);
767 clampaddr(w);
768 w->limit.q0 = w->addr.q0;
769 w->limit.q1 = w->addr.q1;
770 m = 10;
771 }else
772 if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */
773 w->nomark = TRUE;
774 m = 6;
775 }else
776 if(strncmp(p, "mark", 4) == 0){ /* mark file */
777 seq++;
778 filemark(w->body.file);
779 settag = TRUE;
780 m = 4;
781 }else
782 if(strncmp(p, "nomenu", 6) == 0){ /* turn off automatic menu */
783 w->filemenu = FALSE;
784 m = 6;
785 }else
786 if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */
787 w->filemenu = TRUE;
788 m = 4;
789 }else
790 if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */
791 wincleartag(w);
792 settag = TRUE;
793 m = 8;
794 }else{
795 err = Ebadctl;
796 break;
798 while(p[m] == '\n')
799 m++;
802 if(isfbuf)
803 fbuffree(r);
804 else
805 free(r);
806 if(err)
807 n = 0;
808 fc.count = n;
809 respond(x, &fc, err);
810 if(settag)
811 winsettag(w);
812 if(scrdraw)
813 textscrdraw(&w->body);
816 void
817 xfideventwrite(Xfid *x, Window *w)
819 Fcall fc;
820 int m, n;
821 Rune *r;
822 char *err, *p, *q;
823 int isfbuf;
824 Text *t;
825 int c;
826 uint q0, q1;
828 err = nil;
829 isfbuf = TRUE;
830 if(x->fcall.count < RBUFSIZE)
831 r = fbufalloc();
832 else{
833 isfbuf = FALSE;
834 r = emalloc(x->fcall.count*UTFmax+1);
836 for(n=0; n<x->fcall.count; n+=m){
837 p = x->fcall.data+n;
838 w->owner = *p++; /* disgusting */
839 c = *p++;
840 while(*p == ' ')
841 p++;
842 q0 = strtoul(p, &q, 10);
843 if(q == p)
844 goto Rescue;
845 p = q;
846 while(*p == ' ')
847 p++;
848 q1 = strtoul(p, &q, 10);
849 if(q == p)
850 goto Rescue;
851 p = q;
852 while(*p == ' ')
853 p++;
854 if(*p++ != '\n')
855 goto Rescue;
856 m = p-(x->fcall.data+n);
857 if('a'<=c && c<='z')
858 t = &w->tag;
859 else if('A'<=c && c<='Z')
860 t = &w->body;
861 else
862 goto Rescue;
863 if(q0>t->file->b.nc || q1>t->file->b.nc || q0>q1)
864 goto Rescue;
866 qlock(&row.lk); /* just like mousethread */
867 switch(c){
868 case 'x':
869 case 'X':
870 execute(t, q0, q1, TRUE, nil);
871 break;
872 case 'l':
873 case 'L':
874 look3(t, q0, q1, TRUE);
875 break;
876 default:
877 qunlock(&row.lk);
878 goto Rescue;
880 qunlock(&row.lk);
884 Out:
885 if(isfbuf)
886 fbuffree(r);
887 else
888 free(r);
889 if(err)
890 n = 0;
891 fc.count = n;
892 respond(x, &fc, err);
893 return;
895 Rescue:
896 err = Ebadevent;
897 goto Out;
900 void
901 xfidutfread(Xfid *x, Text *t, uint q1, int qid)
903 Fcall fc;
904 Window *w;
905 Rune *r;
906 char *b, *b1;
907 uint q, off, boff;
908 int m, n, nr, nb;
910 w = t->w;
911 wincommit(w, t);
912 off = x->fcall.offset;
913 r = fbufalloc();
914 b = fbufalloc();
915 b1 = fbufalloc();
916 n = 0;
917 if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
918 boff = w->utflastboff;
919 q = w->utflastq;
920 }else{
921 /* BUG: stupid code: scan from beginning */
922 boff = 0;
923 q = 0;
925 w->utflastqid = qid;
926 while(q<q1 && n<x->fcall.count){
927 /*
928 * Updating here avoids partial rune problem: we're always on a
929 * char boundary. The cost is we will usually do one more read
930 * than we really need, but that's better than being n^2.
931 */
932 w->utflastboff = boff;
933 w->utflastq = q;
934 nr = q1-q;
935 if(nr > BUFSIZE/UTFmax)
936 nr = BUFSIZE/UTFmax;
937 bufread(&t->file->b, q, r, nr);
938 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
939 if(boff >= off){
940 m = nb;
941 if(boff+m > off+x->fcall.count)
942 m = off+x->fcall.count - boff;
943 memmove(b1+n, b, m);
944 n += m;
945 }else if(boff+nb > off){
946 if(n != 0)
947 error("bad count in utfrune");
948 m = nb - (off-boff);
949 if(m > x->fcall.count)
950 m = x->fcall.count;
951 memmove(b1, b+(off-boff), m);
952 n += m;
954 boff += nb;
955 q += nr;
957 fbuffree(r);
958 fbuffree(b);
959 fc.count = n;
960 fc.data = b1;
961 respond(x, &fc, nil);
962 fbuffree(b1);
965 int
966 xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
968 Fcall fc;
969 Window *w;
970 Rune *r, junk;
971 char *b, *b1;
972 uint q, boff;
973 int i, rw, m, n, nr, nb;
975 w = t->w;
976 wincommit(w, t);
977 r = fbufalloc();
978 b = fbufalloc();
979 b1 = fbufalloc();
980 n = 0;
981 q = q0;
982 boff = 0;
983 while(q<q1 && n<x->fcall.count){
984 nr = q1-q;
985 if(nr > BUFSIZE/UTFmax)
986 nr = BUFSIZE/UTFmax;
987 bufread(&t->file->b, q, r, nr);
988 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
989 m = nb;
990 if(boff+m > x->fcall.count){
991 i = x->fcall.count - boff;
992 /* copy whole runes only */
993 m = 0;
994 nr = 0;
995 while(m < i){
996 rw = chartorune(&junk, b+m);
997 if(m+rw > i)
998 break;
999 m += rw;
1000 nr++;
1002 if(m == 0)
1003 break;
1005 memmove(b1+n, b, m);
1006 n += m;
1007 boff += nb;
1008 q += nr;
1010 fbuffree(r);
1011 fbuffree(b);
1012 fc.count = n;
1013 fc.data = b1;
1014 respond(x, &fc, nil);
1015 fbuffree(b1);
1016 return q-q0;
1019 void
1020 xfideventread(Xfid *x, Window *w)
1022 Fcall fc;
1023 int i, n;
1025 i = 0;
1026 x->flushed = FALSE;
1027 while(w->nevents == 0){
1028 if(i){
1029 if(!x->flushed)
1030 respond(x, &fc, "window shut down");
1031 return;
1033 w->eventx = x;
1034 winunlock(w);
1035 recvp(x->c);
1036 winlock(w, 'F');
1037 i++;
1040 n = w->nevents;
1041 if(n > x->fcall.count)
1042 n = x->fcall.count;
1043 fc.count = n;
1044 fc.data = w->events;
1045 respond(x, &fc, nil);
1046 w->nevents -= n;
1047 if(w->nevents){
1048 memmove(w->events, w->events+n, w->nevents);
1049 w->events = erealloc(w->events, w->nevents);
1050 }else{
1051 free(w->events);
1052 w->events = nil;
1056 void
1057 xfidindexread(Xfid *x)
1059 Fcall fc;
1060 int i, j, m, n, nmax, isbuf, cnt, off;
1061 Window *w;
1062 char *b;
1063 Rune *r;
1064 Column *c;
1066 qlock(&row.lk);
1067 nmax = 0;
1068 for(j=0; j<row.ncol; j++){
1069 c = row.col[j];
1070 for(i=0; i<c->nw; i++){
1071 w = c->w[i];
1072 nmax += Ctlsize + w->tag.file->b.nc*UTFmax + 1;
1075 nmax++;
1076 isbuf = (nmax<=RBUFSIZE);
1077 if(isbuf)
1078 b = (char*)x->buf;
1079 else
1080 b = emalloc(nmax);
1081 r = fbufalloc();
1082 n = 0;
1083 for(j=0; j<row.ncol; j++){
1084 c = row.col[j];
1085 for(i=0; i<c->nw; i++){
1086 w = c->w[i];
1087 /* only show the currently active window of a set */
1088 if(w->body.file->curtext != &w->body)
1089 continue;
1090 winctlprint(w, b+n, 0);
1091 n += Ctlsize;
1092 m = min(RBUFSIZE, w->tag.file->b.nc);
1093 bufread(&w->tag.file->b, 0, r, m);
1094 m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1095 while(n<m && b[n]!='\n')
1096 n++;
1097 b[n++] = '\n';
1100 qunlock(&row.lk);
1101 off = x->fcall.offset;
1102 cnt = x->fcall.count;
1103 if(off > n)
1104 off = n;
1105 if(off+cnt > n)
1106 cnt = n-off;
1107 fc.count = cnt;
1108 memmove(r, b+off, cnt);
1109 fc.data = (char*)r;
1110 if(!isbuf)
1111 free(b);
1112 respond(x, &fc, nil);
1113 fbuffree(r);