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 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 w->wrselrange = range(t->q1, t->q1);
178 break;
180 winunlock(w);
182 fc.qid = x->f->qid;
183 fc.iounit = messagesize-IOHDRSZ;
184 x->f->open = TRUE;
185 respond(x, &fc, nil);
188 void
189 xfidclose(Xfid *x)
191 Fcall fc;
192 Window *w;
193 int q;
194 Text *t;
196 w = x->f->w;
197 x->f->busy = FALSE;
198 x->f->w = nil;
199 if(x->f->open == FALSE){
200 if(w != nil)
201 winclose(w);
202 respond(x, &fc, nil);
203 return;
206 x->f->open = FALSE;
207 if(w){
208 winlock(w, 'E');
209 q = FILE(x->f->qid);
210 switch(q){
211 case QWctl:
212 if(w->ctlfid!=~0 && w->ctlfid==x->f->fid){
213 w->ctlfid = ~0;
214 qunlock(&w->ctllock);
216 break;
217 case QWdata:
218 case QWxdata:
219 w->nomark = FALSE;
220 /* fall through */
221 case QWaddr:
222 case QWevent: /* BUG: do we need to shut down Xfid? */
223 if(--w->nopen[q] == 0){
224 if(q == QWdata || q == QWxdata)
225 w->nomark = FALSE;
226 if(q==QWevent && !w->isdir && w->col!=nil){
227 w->filemenu = TRUE;
228 winsettag(w);
230 if(q == QWevent){
231 free(w->dumpstr);
232 free(w->dumpdir);
233 w->dumpstr = nil;
234 w->dumpdir = nil;
237 break;
238 case QWrdsel:
239 close(w->rdselfd);
240 w->rdselfd = 0;
241 break;
242 case QWwrsel:
243 w->nomark = FALSE;
244 t = &w->body;
245 /* before: only did this if !w->noscroll, but that didn't seem right in practice */
246 textshow(t, min(w->wrselrange.q0, t->file->b.nc),
247 min(w->wrselrange.q1, t->file->b.nc), 1);
248 textscrdraw(t);
249 break;
251 winunlock(w);
252 winclose(w);
254 respond(x, &fc, nil);
257 void
258 xfidread(Xfid *x)
260 Fcall fc;
261 int n, q;
262 uint off;
263 char *b;
264 char buf[256];
265 Window *w;
267 q = FILE(x->f->qid);
268 w = x->f->w;
269 if(w == nil){
270 fc.count = 0;
271 switch(q){
272 case Qcons:
273 case Qlabel:
274 break;
275 case Qindex:
276 xfidindexread(x);
277 return;
278 default:
279 warning(nil, "unknown qid %d\n", q);
280 break;
282 respond(x, &fc, nil);
283 return;
285 winlock(w, 'F');
286 if(w->col == nil){
287 winunlock(w);
288 respond(x, &fc, Edel);
289 return;
291 off = x->fcall.offset;
292 switch(q){
293 case QWaddr:
294 textcommit(&w->body, TRUE);
295 clampaddr(w);
296 sprint(buf, "%11d %11d ", w->addr.q0, w->addr.q1);
297 goto Readbuf;
299 case QWbody:
300 xfidutfread(x, &w->body, w->body.file->b.nc, QWbody);
301 break;
303 case QWctl:
304 b = winctlprint(w, buf, 1);
305 goto Readb;
307 Readbuf:
308 b = buf;
309 Readb:
310 n = strlen(b);
311 if(off > n)
312 off = n;
313 if(off+x->fcall.count > n)
314 x->fcall.count = n-off;
315 fc.count = x->fcall.count;
316 fc.data = b+off;
317 respond(x, &fc, nil);
318 if(b != buf)
319 free(b);
320 break;
322 case QWevent:
323 xfideventread(x, w);
324 break;
326 case QWdata:
327 /* BUG: what should happen if q1 > q0? */
328 if(w->addr.q0 > w->body.file->b.nc){
329 respond(x, &fc, Eaddr);
330 break;
332 w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->b.nc);
333 w->addr.q1 = w->addr.q0;
334 break;
336 case QWxdata:
337 /* BUG: what should happen if q1 > q0? */
338 if(w->addr.q0 > w->body.file->b.nc){
339 respond(x, &fc, Eaddr);
340 break;
342 w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1);
343 break;
345 case QWtag:
346 xfidutfread(x, &w->tag, w->tag.file->b.nc, QWtag);
347 break;
349 case QWrdsel:
350 seek(w->rdselfd, off, 0);
351 n = x->fcall.count;
352 if(n > BUFSIZE)
353 n = BUFSIZE;
354 b = fbufalloc();
355 n = read(w->rdselfd, b, n);
356 if(n < 0){
357 respond(x, &fc, "I/O error in temp file");
358 break;
360 fc.count = n;
361 fc.data = b;
362 respond(x, &fc, nil);
363 fbuffree(b);
364 break;
366 default:
367 sprint(buf, "unknown qid %d in read", q);
368 respond(x, &fc, nil);
370 winunlock(w);
373 void
374 xfidwrite(Xfid *x)
376 Fcall fc;
377 int c, cnt, qid, q, nb, nr, eval;
378 char buf[64], *err;
379 Window *w;
380 Rune *r;
381 Range a;
382 Text *t;
383 uint q0, tq0, tq1;
385 qid = FILE(x->f->qid);
386 w = x->f->w;
387 if(w){
388 c = 'F';
389 if(qid==QWtag || qid==QWbody)
390 c = 'E';
391 winlock(w, c);
392 if(w->col == nil){
393 winunlock(w);
394 respond(x, &fc, Edel);
395 return;
398 x->fcall.data[x->fcall.count] = 0;
399 switch(qid){
400 case Qcons:
401 w = errorwin(x->f->mntdir, 'X');
402 t=&w->body;
403 goto BodyTag;
405 case Qlabel:
406 fc.count = x->fcall.count;
407 respond(x, &fc, nil);
408 break;
410 case QWaddr:
411 x->fcall.data[x->fcall.count] = 0;
412 r = bytetorune(x->fcall.data, &nr);
413 t = &w->body;
414 wincommit(w, t);
415 eval = TRUE;
416 a = address(FALSE, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb);
417 free(r);
418 if(nb < nr){
419 respond(x, &fc, Ebadaddr);
420 break;
422 if(!eval){
423 respond(x, &fc, Eaddr);
424 break;
426 w->addr = a;
427 fc.count = x->fcall.count;
428 respond(x, &fc, nil);
429 break;
431 case Qeditout:
432 case QWeditout:
433 r = bytetorune(x->fcall.data, &nr);
434 if(w)
435 err = edittext(w, w->wrselrange.q1, r, nr);
436 else
437 err = edittext(nil, 0, r, nr);
438 free(r);
439 if(err != nil){
440 respond(x, &fc, err);
441 break;
443 fc.count = x->fcall.count;
444 respond(x, &fc, nil);
445 break;
447 case QWerrors:
448 w = errorwinforwin(w);
449 t = &w->body;
450 goto BodyTag;
452 case QWbody:
453 case QWwrsel:
454 t = &w->body;
455 goto BodyTag;
457 case QWctl:
458 xfidctlwrite(x, w);
459 break;
461 case QWdata:
462 a = w->addr;
463 t = &w->body;
464 wincommit(w, t);
465 if(a.q0>t->file->b.nc || a.q1>t->file->b.nc){
466 respond(x, &fc, Eaddr);
467 break;
469 r = runemalloc(x->fcall.count);
470 cvttorunes(x->fcall.data, x->fcall.count, r, &nb, &nr, nil);
471 if(w->nomark == FALSE){
472 seq++;
473 filemark(t->file);
475 q0 = a.q0;
476 if(a.q1 > q0){
477 textdelete(t, q0, a.q1, TRUE);
478 w->addr.q1 = q0;
480 tq0 = t->q0;
481 tq1 = t->q1;
482 textinsert(t, q0, r, nr, TRUE);
483 if(tq0 >= q0)
484 tq0 += nr;
485 if(tq1 >= q0)
486 tq1 += nr;
487 textsetselect(t, tq0, tq1);
488 if(!t->w->noscroll)
489 textshow(t, q0, q0+nr, 0);
490 textscrdraw(t);
491 winsettag(w);
492 free(r);
493 w->addr.q0 += nr;
494 w->addr.q1 = w->addr.q0;
495 fc.count = x->fcall.count;
496 respond(x, &fc, nil);
497 break;
499 case QWevent:
500 xfideventwrite(x, w);
501 break;
503 case QWtag:
504 t = &w->tag;
505 goto BodyTag;
507 BodyTag:
508 q = x->f->nrpart;
509 cnt = x->fcall.count;
510 if(q > 0){
511 memmove(x->fcall.data+q, x->fcall.data, cnt); /* there's room; see fsysproc */
512 memmove(x->fcall.data, x->f->rpart, q);
513 cnt += q;
514 x->f->nrpart = 0;
516 r = runemalloc(cnt);
517 cvttorunes(x->fcall.data, cnt-UTFmax, r, &nb, &nr, nil);
518 /* approach end of buffer */
519 while(fullrune(x->fcall.data+nb, cnt-nb)){
520 c = nb;
521 nb += chartorune(&r[nr], x->fcall.data+c);
522 if(r[nr])
523 nr++;
525 if(nb < cnt){
526 memmove(x->f->rpart, x->fcall.data+nb, cnt-nb);
527 x->f->nrpart = cnt-nb;
529 if(nr > 0){
530 wincommit(w, t);
531 if(qid == QWwrsel){
532 q0 = w->wrselrange.q1;
533 if(q0 > t->file->b.nc)
534 q0 = t->file->b.nc;
535 }else
536 q0 = t->file->b.nc;
537 if(qid == QWtag)
538 textinsert(t, q0, r, nr, TRUE);
539 else{
540 if(w->nomark == FALSE){
541 seq++;
542 filemark(t->file);
544 q0 = textbsinsert(t, q0, r, nr, TRUE, &nr);
545 textsetselect(t, t->q0, t->q1); /* insert could leave it somewhere else */
546 if(qid!=QWwrsel && !t->w->noscroll)
547 textshow(t, q0+nr, q0+nr, 1);
548 textscrdraw(t);
550 winsettag(w);
551 if(qid == QWwrsel)
552 w->wrselrange.q1 += nr;
553 free(r);
555 fc.count = x->fcall.count;
556 respond(x, &fc, nil);
557 break;
559 default:
560 sprint(buf, "unknown qid %d in write", qid);
561 respond(x, &fc, buf);
562 break;
564 if(w)
565 winunlock(w);
568 void
569 xfidctlwrite(Xfid *x, Window *w)
571 Fcall fc;
572 int i, m, n, nb, nr, nulls;
573 Rune *r;
574 char *err, *p, *pp, *q, *e;
575 int isfbuf, scrdraw, settag;
576 Text *t;
578 err = nil;
579 e = x->fcall.data+x->fcall.count;
580 scrdraw = FALSE;
581 settag = FALSE;
582 isfbuf = TRUE;
583 if(x->fcall.count < RBUFSIZE)
584 r = fbufalloc();
585 else{
586 isfbuf = FALSE;
587 r = emalloc(x->fcall.count*UTFmax+1);
589 x->fcall.data[x->fcall.count] = 0;
590 textcommit(&w->tag, TRUE);
591 for(n=0; n<x->fcall.count; n+=m){
592 p = x->fcall.data+n;
593 if(strncmp(p, "lock", 4) == 0){ /* make window exclusive use */
594 qlock(&w->ctllock);
595 w->ctlfid = x->f->fid;
596 m = 4;
597 }else
598 if(strncmp(p, "unlock", 6) == 0){ /* release exclusive use */
599 w->ctlfid = ~0;
600 qunlock(&w->ctllock);
601 m = 6;
602 }else
603 if(strncmp(p, "clean", 5) == 0){ /* mark window 'clean', seq=0 */
604 t = &w->body;
605 t->eq0 = ~0;
606 filereset(t->file);
607 t->file->mod = FALSE;
608 w->dirty = FALSE;
609 settag = TRUE;
610 m = 5;
611 }else
612 if(strncmp(p, "dirty", 5) == 0){ /* mark window 'dirty' */
613 t = &w->body;
614 /* doesn't change sequence number, so "Put" won't appear. it shouldn't. */
615 t->file->mod = TRUE;
616 w->dirty = TRUE;
617 settag = TRUE;
618 m = 5;
619 }else
620 if(strncmp(p, "show", 4) == 0){ /* show dot */
621 t = &w->body;
622 textshow(t, t->q0, t->q1, 1);
623 m = 4;
624 }else
625 if(strncmp(p, "name ", 5) == 0){ /* set file name */
626 pp = p+5;
627 m = 5;
628 q = memchr(pp, '\n', e-pp);
629 if(q==nil || q==pp){
630 err = Ebadctl;
631 break;
633 *q = 0;
634 nulls = FALSE;
635 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
636 if(nulls){
637 err = "nulls in file name";
638 break;
640 for(i=0; i<nr; i++)
641 if(r[i] <= ' '){
642 err = "bad character in file name";
643 goto out;
645 out:
646 seq++;
647 filemark(w->body.file);
648 winsetname(w, r, nr);
649 m += (q+1) - pp;
650 }else
651 if(strncmp(p, "dump ", 5) == 0){ /* set dump string */
652 pp = p+5;
653 m = 5;
654 q = memchr(pp, '\n', e-pp);
655 if(q==nil || q==pp){
656 err = Ebadctl;
657 break;
659 *q = 0;
660 nulls = FALSE;
661 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
662 if(nulls){
663 err = "nulls in dump string";
664 break;
666 w->dumpstr = runetobyte(r, nr);
667 m += (q+1) - pp;
668 }else
669 if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */
670 pp = p+8;
671 m = 8;
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 dump directory string";
682 break;
684 w->dumpdir = runetobyte(r, nr);
685 m += (q+1) - pp;
686 }else
687 if(strncmp(p, "delete", 6) == 0){ /* delete for sure */
688 colclose(w->col, w, TRUE);
689 m = 6;
690 }else
691 if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */
692 if(!winclean(w, TRUE)){
693 err = "file dirty";
694 break;
696 colclose(w->col, w, TRUE);
697 m = 3;
698 }else
699 if(strncmp(p, "get", 3) == 0){ /* get file */
700 get(&w->body, nil, nil, FALSE, XXX, nil, 0);
701 m = 3;
702 }else
703 if(strncmp(p, "put", 3) == 0){ /* put file */
704 put(&w->body, nil, nil, XXX, XXX, nil, 0);
705 m = 3;
706 }else
707 if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */
708 textcommit(&w->body, TRUE);
709 clampaddr(w);
710 w->body.q0 = w->addr.q0;
711 w->body.q1 = w->addr.q1;
712 textsetselect(&w->body, w->body.q0, w->body.q1);
713 settag = TRUE;
714 m = 8;
715 }else
716 if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */
717 w->addr.q0 = w->body.q0;
718 w->addr.q1 = w->body.q1;
719 m = 8;
720 }else
721 if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */
722 textcommit(&w->body, TRUE);
723 clampaddr(w);
724 w->limit.q0 = w->addr.q0;
725 w->limit.q1 = w->addr.q1;
726 m = 10;
727 }else
728 if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */
729 w->nomark = TRUE;
730 m = 6;
731 }else
732 if(strncmp(p, "mark", 4) == 0){ /* mark file */
733 seq++;
734 filemark(w->body.file);
735 settag = TRUE;
736 m = 4;
737 }else
738 if(strncmp(p, "noscroll", 8) == 0){ /* turn off automatic scrolling */
739 w->noscroll = TRUE;
740 m = 8;
741 }else
742 if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */
743 wincleartag(w);
744 settag = TRUE;
745 m = 8;
746 }else
747 if(strncmp(p, "scroll", 6) == 0){ /* turn on automatic scrolling (writes to body only) */
748 w->noscroll = FALSE;
749 m = 6;
750 }else{
751 err = Ebadctl;
752 break;
754 while(p[m] == '\n')
755 m++;
758 if(isfbuf)
759 fbuffree(r);
760 else
761 free(r);
762 if(err)
763 n = 0;
764 fc.count = n;
765 respond(x, &fc, err);
766 if(settag)
767 winsettag(w);
768 if(scrdraw)
769 textscrdraw(&w->body);
772 void
773 xfideventwrite(Xfid *x, Window *w)
775 Fcall fc;
776 int m, n;
777 Rune *r;
778 char *err, *p, *q;
779 int isfbuf;
780 Text *t;
781 int c;
782 uint q0, q1;
784 err = nil;
785 isfbuf = TRUE;
786 if(x->fcall.count < RBUFSIZE)
787 r = fbufalloc();
788 else{
789 isfbuf = FALSE;
790 r = emalloc(x->fcall.count*UTFmax+1);
792 for(n=0; n<x->fcall.count; n+=m){
793 p = x->fcall.data+n;
794 w->owner = *p++; /* disgusting */
795 c = *p++;
796 while(*p == ' ')
797 p++;
798 q0 = strtoul(p, &q, 10);
799 if(q == p)
800 goto Rescue;
801 p = q;
802 while(*p == ' ')
803 p++;
804 q1 = strtoul(p, &q, 10);
805 if(q == p)
806 goto Rescue;
807 p = q;
808 while(*p == ' ')
809 p++;
810 if(*p++ != '\n')
811 goto Rescue;
812 m = p-(x->fcall.data+n);
813 if('a'<=c && c<='z')
814 t = &w->tag;
815 else if('A'<=c && c<='Z')
816 t = &w->body;
817 else
818 goto Rescue;
819 if(q0>t->file->b.nc || q1>t->file->b.nc || q0>q1)
820 goto Rescue;
822 qlock(&row.lk); /* just like mousethread */
823 switch(c){
824 case 'x':
825 case 'X':
826 execute(t, q0, q1, TRUE, nil);
827 break;
828 case 'l':
829 case 'L':
830 look3(t, q0, q1, TRUE);
831 break;
832 default:
833 qunlock(&row.lk);
834 goto Rescue;
836 qunlock(&row.lk);
840 Out:
841 if(isfbuf)
842 fbuffree(r);
843 else
844 free(r);
845 if(err)
846 n = 0;
847 fc.count = n;
848 respond(x, &fc, err);
849 return;
851 Rescue:
852 err = Ebadevent;
853 goto Out;
856 void
857 xfidutfread(Xfid *x, Text *t, uint q1, int qid)
859 Fcall fc;
860 Window *w;
861 Rune *r;
862 char *b, *b1;
863 uint q, off, boff;
864 int m, n, nr, nb;
866 w = t->w;
867 wincommit(w, t);
868 off = x->fcall.offset;
869 r = fbufalloc();
870 b = fbufalloc();
871 b1 = fbufalloc();
872 n = 0;
873 if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
874 boff = w->utflastboff;
875 q = w->utflastq;
876 }else{
877 /* BUG: stupid code: scan from beginning */
878 boff = 0;
879 q = 0;
881 w->utflastqid = qid;
882 while(q<q1 && n<x->fcall.count){
883 /*
884 * Updating here avoids partial rune problem: we're always on a
885 * char boundary. The cost is we will usually do one more read
886 * than we really need, but that's better than being n^2.
887 */
888 w->utflastboff = boff;
889 w->utflastq = q;
890 nr = q1-q;
891 if(nr > BUFSIZE/UTFmax)
892 nr = BUFSIZE/UTFmax;
893 bufread(&t->file->b, q, r, nr);
894 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
895 if(boff >= off){
896 m = nb;
897 if(boff+m > off+x->fcall.count)
898 m = off+x->fcall.count - boff;
899 memmove(b1+n, b, m);
900 n += m;
901 }else if(boff+nb > off){
902 if(n != 0)
903 error("bad count in utfrune");
904 m = nb - (off-boff);
905 if(m > x->fcall.count)
906 m = x->fcall.count;
907 memmove(b1, b+(off-boff), m);
908 n += m;
910 boff += nb;
911 q += nr;
913 fbuffree(r);
914 fbuffree(b);
915 fc.count = n;
916 fc.data = b1;
917 respond(x, &fc, nil);
918 fbuffree(b1);
921 int
922 xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
924 Fcall fc;
925 Window *w;
926 Rune *r, junk;
927 char *b, *b1;
928 uint q, boff;
929 int i, rw, m, n, nr, nb;
931 w = t->w;
932 wincommit(w, t);
933 r = fbufalloc();
934 b = fbufalloc();
935 b1 = fbufalloc();
936 n = 0;
937 q = q0;
938 boff = 0;
939 while(q<q1 && n<x->fcall.count){
940 nr = q1-q;
941 if(nr > BUFSIZE/UTFmax)
942 nr = BUFSIZE/UTFmax;
943 bufread(&t->file->b, q, r, nr);
944 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
945 m = nb;
946 if(boff+m > x->fcall.count){
947 i = x->fcall.count - boff;
948 /* copy whole runes only */
949 m = 0;
950 nr = 0;
951 while(m < i){
952 rw = chartorune(&junk, b+m);
953 if(m+rw > i)
954 break;
955 m += rw;
956 nr++;
958 if(m == 0)
959 break;
961 memmove(b1+n, b, m);
962 n += m;
963 boff += nb;
964 q += nr;
966 fbuffree(r);
967 fbuffree(b);
968 fc.count = n;
969 fc.data = b1;
970 respond(x, &fc, nil);
971 fbuffree(b1);
972 return q-q0;
975 void
976 xfideventread(Xfid *x, Window *w)
978 Fcall fc;
979 char *b;
980 int i, n;
982 i = 0;
983 x->flushed = FALSE;
984 while(w->nevents == 0){
985 if(i){
986 if(!x->flushed)
987 respond(x, &fc, "window shut down");
988 return;
990 w->eventx = x;
991 winunlock(w);
992 recvp(x->c);
993 winlock(w, 'F');
994 i++;
997 n = w->nevents;
998 if(n > x->fcall.count)
999 n = x->fcall.count;
1000 fc.count = n;
1001 fc.data = w->events;
1002 respond(x, &fc, nil);
1003 b = w->events;
1004 w->events = estrdup(w->events+n);
1005 free(b);
1006 w->nevents -= n;
1009 void
1010 xfidindexread(Xfid *x)
1012 Fcall fc;
1013 int i, j, m, n, nmax, isbuf, cnt, off;
1014 Window *w;
1015 char *b;
1016 Rune *r;
1017 Column *c;
1019 qlock(&row.lk);
1020 nmax = 0;
1021 for(j=0; j<row.ncol; j++){
1022 c = row.col[j];
1023 for(i=0; i<c->nw; i++){
1024 w = c->w[i];
1025 nmax += Ctlsize + w->tag.file->b.nc*UTFmax + 1;
1028 nmax++;
1029 isbuf = (nmax<=RBUFSIZE);
1030 if(isbuf)
1031 b = (char*)x->buf;
1032 else
1033 b = emalloc(nmax);
1034 r = fbufalloc();
1035 n = 0;
1036 for(j=0; j<row.ncol; j++){
1037 c = row.col[j];
1038 for(i=0; i<c->nw; i++){
1039 w = c->w[i];
1040 /* only show the currently active window of a set */
1041 if(w->body.file->curtext != &w->body)
1042 continue;
1043 winctlprint(w, b+n, 0);
1044 n += Ctlsize;
1045 m = min(RBUFSIZE, w->tag.file->b.nc);
1046 bufread(&w->tag.file->b, 0, r, m);
1047 m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1048 while(n<m && b[n]!='\n')
1049 n++;
1050 b[n++] = '\n';
1053 qunlock(&row.lk);
1054 off = x->fcall.offset;
1055 cnt = x->fcall.count;
1056 if(off > n)
1057 off = n;
1058 if(off+cnt > n)
1059 cnt = n-off;
1060 fc.count = cnt;
1061 memmove(r, b+off, cnt);
1062 fc.data = (char*)r;
1063 if(!isbuf)
1064 free(b);
1065 respond(x, &fc, nil);
1066 fbuffree(r);