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, "font ", 5) == 0){ /* execute font command */
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 font string";
717 break;
719 fontx(&w->body, nil, nil, FALSE, XXX, r, nr);
720 m += (q+1) - pp;
721 }else
722 if(strncmp(p, "dump ", 5) == 0){ /* set dump string */
723 pp = p+5;
724 m = 5;
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 string";
735 break;
737 w->dumpstr = runetobyte(r, nr);
738 m += (q+1) - pp;
739 }else
740 if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */
741 pp = p+8;
742 m = 8;
743 q = memchr(pp, '\n', e-pp);
744 if(q==nil || q==pp){
745 err = Ebadctl;
746 break;
748 *q = 0;
749 nulls = FALSE;
750 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
751 if(nulls){
752 err = "nulls in dump directory string";
753 break;
755 w->dumpdir = runetobyte(r, nr);
756 m += (q+1) - pp;
757 }else
758 if(strncmp(p, "delete", 6) == 0){ /* delete for sure */
759 colclose(w->col, w, TRUE);
760 m = 6;
761 }else
762 if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */
763 if(!winclean(w, TRUE)){
764 err = "file dirty";
765 break;
767 colclose(w->col, w, TRUE);
768 m = 3;
769 }else
770 if(strncmp(p, "get", 3) == 0){ /* get file */
771 get(&w->body, nil, nil, FALSE, XXX, nil, 0);
772 m = 3;
773 }else
774 if(strncmp(p, "put", 3) == 0){ /* put file */
775 put(&w->body, nil, nil, XXX, XXX, nil, 0);
776 m = 3;
777 }else
778 if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */
779 textcommit(&w->body, TRUE);
780 clampaddr(w);
781 w->body.q0 = w->addr.q0;
782 w->body.q1 = w->addr.q1;
783 textsetselect(&w->body, w->body.q0, w->body.q1);
784 settag = TRUE;
785 m = 8;
786 }else
787 if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */
788 w->addr.q0 = w->body.q0;
789 w->addr.q1 = w->body.q1;
790 m = 8;
791 }else
792 if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */
793 textcommit(&w->body, TRUE);
794 clampaddr(w);
795 w->limit.q0 = w->addr.q0;
796 w->limit.q1 = w->addr.q1;
797 m = 10;
798 }else
799 if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */
800 w->nomark = TRUE;
801 m = 6;
802 }else
803 if(strncmp(p, "mark", 4) == 0){ /* mark file */
804 seq++;
805 filemark(w->body.file);
806 settag = TRUE;
807 m = 4;
808 }else
809 if(strncmp(p, "nomenu", 6) == 0){ /* turn off automatic menu */
810 w->filemenu = FALSE;
811 settag = TRUE;
812 m = 6;
813 }else
814 if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */
815 w->filemenu = TRUE;
816 settag = TRUE;
817 m = 4;
818 }else
819 if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */
820 wincleartag(w);
821 settag = TRUE;
822 m = 8;
823 }else{
824 err = Ebadctl;
825 break;
827 while(p[m] == '\n')
828 m++;
831 if(isfbuf)
832 fbuffree(r);
833 else
834 free(r);
835 if(err)
836 n = 0;
837 fc.count = n;
838 respond(x, &fc, err);
839 if(settag)
840 winsettag(w);
841 if(scrdraw)
842 textscrdraw(&w->body);
845 void
846 xfideventwrite(Xfid *x, Window *w)
848 Fcall fc;
849 int m, n;
850 Rune *r;
851 char *err, *p, *q;
852 int isfbuf;
853 Text *t;
854 int c;
855 uint q0, q1;
857 err = nil;
858 isfbuf = TRUE;
859 if(x->fcall.count < RBUFSIZE)
860 r = fbufalloc();
861 else{
862 isfbuf = FALSE;
863 r = emalloc(x->fcall.count*UTFmax+1);
865 for(n=0; n<x->fcall.count; n+=m){
866 p = x->fcall.data+n;
867 w->owner = *p++; /* disgusting */
868 c = *p++;
869 while(*p == ' ')
870 p++;
871 q0 = strtoul(p, &q, 10);
872 if(q == p)
873 goto Rescue;
874 p = q;
875 while(*p == ' ')
876 p++;
877 q1 = strtoul(p, &q, 10);
878 if(q == p)
879 goto Rescue;
880 p = q;
881 while(*p == ' ')
882 p++;
883 if(*p++ != '\n')
884 goto Rescue;
885 m = p-(x->fcall.data+n);
886 if('a'<=c && c<='z')
887 t = &w->tag;
888 else if('A'<=c && c<='Z')
889 t = &w->body;
890 else
891 goto Rescue;
892 if(q0>t->file->b.nc || q1>t->file->b.nc || q0>q1)
893 goto Rescue;
895 qlock(&row.lk); /* just like mousethread */
896 switch(c){
897 case 'x':
898 case 'X':
899 execute(t, q0, q1, TRUE, nil);
900 break;
901 case 'l':
902 case 'L':
903 look3(t, q0, q1, TRUE);
904 break;
905 default:
906 qunlock(&row.lk);
907 goto Rescue;
909 qunlock(&row.lk);
913 Out:
914 if(isfbuf)
915 fbuffree(r);
916 else
917 free(r);
918 if(err)
919 n = 0;
920 fc.count = n;
921 respond(x, &fc, err);
922 return;
924 Rescue:
925 err = Ebadevent;
926 goto Out;
929 void
930 xfidutfread(Xfid *x, Text *t, uint q1, int qid)
932 Fcall fc;
933 Window *w;
934 Rune *r;
935 char *b, *b1;
936 uint q, off, boff;
937 int m, n, nr, nb;
939 w = t->w;
940 wincommit(w, t);
941 off = x->fcall.offset;
942 r = fbufalloc();
943 b = fbufalloc();
944 b1 = fbufalloc();
945 n = 0;
946 if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
947 boff = w->utflastboff;
948 q = w->utflastq;
949 }else{
950 /* BUG: stupid code: scan from beginning */
951 boff = 0;
952 q = 0;
954 w->utflastqid = qid;
955 while(q<q1 && n<x->fcall.count){
956 /*
957 * Updating here avoids partial rune problem: we're always on a
958 * char boundary. The cost is we will usually do one more read
959 * than we really need, but that's better than being n^2.
960 */
961 w->utflastboff = boff;
962 w->utflastq = q;
963 nr = q1-q;
964 if(nr > BUFSIZE/UTFmax)
965 nr = BUFSIZE/UTFmax;
966 bufread(&t->file->b, q, r, nr);
967 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
968 if(boff >= off){
969 m = nb;
970 if(boff+m > off+x->fcall.count)
971 m = off+x->fcall.count - boff;
972 memmove(b1+n, b, m);
973 n += m;
974 }else if(boff+nb > off){
975 if(n != 0)
976 error("bad count in utfrune");
977 m = nb - (off-boff);
978 if(m > x->fcall.count)
979 m = x->fcall.count;
980 memmove(b1, b+(off-boff), m);
981 n += m;
983 boff += nb;
984 q += nr;
986 fbuffree(r);
987 fbuffree(b);
988 fc.count = n;
989 fc.data = b1;
990 respond(x, &fc, nil);
991 fbuffree(b1);
994 int
995 xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
997 Fcall fc;
998 Window *w;
999 Rune *r, junk;
1000 char *b, *b1;
1001 uint q, boff;
1002 int i, rw, m, n, nr, nb;
1004 w = t->w;
1005 wincommit(w, t);
1006 r = fbufalloc();
1007 b = fbufalloc();
1008 b1 = fbufalloc();
1009 n = 0;
1010 q = q0;
1011 boff = 0;
1012 while(q<q1 && n<x->fcall.count){
1013 nr = q1-q;
1014 if(nr > BUFSIZE/UTFmax)
1015 nr = BUFSIZE/UTFmax;
1016 bufread(&t->file->b, q, r, nr);
1017 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
1018 m = nb;
1019 if(boff+m > x->fcall.count){
1020 i = x->fcall.count - boff;
1021 /* copy whole runes only */
1022 m = 0;
1023 nr = 0;
1024 while(m < i){
1025 rw = chartorune(&junk, b+m);
1026 if(m+rw > i)
1027 break;
1028 m += rw;
1029 nr++;
1031 if(m == 0)
1032 break;
1034 memmove(b1+n, b, m);
1035 n += m;
1036 boff += nb;
1037 q += nr;
1039 fbuffree(r);
1040 fbuffree(b);
1041 fc.count = n;
1042 fc.data = b1;
1043 respond(x, &fc, nil);
1044 fbuffree(b1);
1045 return q-q0;
1048 void
1049 xfideventread(Xfid *x, Window *w)
1051 Fcall fc;
1052 int i, n;
1054 i = 0;
1055 x->flushed = FALSE;
1056 while(w->nevents == 0){
1057 if(i){
1058 if(!x->flushed)
1059 respond(x, &fc, "window shut down");
1060 return;
1062 w->eventx = x;
1063 winunlock(w);
1064 recvp(x->c);
1065 winlock(w, 'F');
1066 i++;
1069 n = w->nevents;
1070 if(n > x->fcall.count)
1071 n = x->fcall.count;
1072 fc.count = n;
1073 fc.data = w->events;
1074 respond(x, &fc, nil);
1075 w->nevents -= n;
1076 if(w->nevents){
1077 memmove(w->events, w->events+n, w->nevents);
1078 w->events = erealloc(w->events, w->nevents);
1079 }else{
1080 free(w->events);
1081 w->events = nil;
1085 void
1086 xfidindexread(Xfid *x)
1088 Fcall fc;
1089 int i, j, m, n, nmax, isbuf, cnt, off;
1090 Window *w;
1091 char *b;
1092 Rune *r;
1093 Column *c;
1095 qlock(&row.lk);
1096 nmax = 0;
1097 for(j=0; j<row.ncol; j++){
1098 c = row.col[j];
1099 for(i=0; i<c->nw; i++){
1100 w = c->w[i];
1101 nmax += Ctlsize + w->tag.file->b.nc*UTFmax + 1;
1104 nmax++;
1105 isbuf = (nmax<=RBUFSIZE);
1106 if(isbuf)
1107 b = (char*)x->buf;
1108 else
1109 b = emalloc(nmax);
1110 r = fbufalloc();
1111 n = 0;
1112 for(j=0; j<row.ncol; j++){
1113 c = row.col[j];
1114 for(i=0; i<c->nw; i++){
1115 w = c->w[i];
1116 /* only show the currently active window of a set */
1117 if(w->body.file->curtext != &w->body)
1118 continue;
1119 winctlprint(w, b+n, 0);
1120 n += Ctlsize;
1121 m = min(RBUFSIZE, w->tag.file->b.nc);
1122 bufread(&w->tag.file->b, 0, r, m);
1123 m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1124 while(n<m && b[n]!='\n')
1125 n++;
1126 b[n++] = '\n';
1129 qunlock(&row.lk);
1130 off = x->fcall.offset;
1131 cnt = x->fcall.count;
1132 if(off > n)
1133 off = n;
1134 if(off+cnt > n)
1135 cnt = n-off;
1136 fc.count = cnt;
1137 memmove(r, b+off, cnt);
1138 fc.data = (char*)r;
1139 if(!isbuf)
1140 free(b);
1141 respond(x, &fc, nil);
1142 fbuffree(r);