master
xfid.c
ohmu
·
2026-09-13
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"
14
15enum
16{
17 Ctlsize = 5*12
18};
19
20char Edel[] = "deleted window";
21char Ebadctl[] = "ill-formed control message";
22char Ebadaddr[] = "bad address syntax";
23char Eaddr[] = "address out of range";
24char Einuse[] = "already in use";
25char Ebadevent[] = "bad event syntax";
26extern char Eperm[];
27
28static
29void
30clampaddr(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}
41
42void
43xfidctl(void *arg)
44{
45 Xfid *x;
46 void (*f)(Xfid*);
47
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}
57
58void
59xfidflush(Xfid *x)
60{
61 Fcall fc;
62 int i, j;
63 Window *w;
64 Column *c;
65 Xfid *wx;
66
67 xfidlogflush(x);
68
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 }
87out:
88 qunlock(&row.lk);
89 respond(x, &fc, nil);
90}
91
92void
93xfidopen(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;
101
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);
112 }
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);
123 }
124 }
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;
139 }
140 w->rdselfd = tempfile();
141 if(w->rdselfd < 0){
142 winunlock(w);
143 respond(x, &fc, "can't create temp file");
144 return;
145 }
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;
160 }
161 q0 += n;
162 }
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;
179 }
180 if(!canqlock(&w->editoutlk)){
181 winunlock(w);
182 respond(x, &fc, Einuse);
183 return;
184 }
185 w->wrselrange = range(t->q1, t->q1);
186 break;
187 }
188 winunlock(w);
189 }
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;
199 }
200 break;
201 }
202 }
203 fc.qid = x->f->qid;
204 fc.iounit = messagesize-IOHDRSZ;
205 x->f->open = TRUE;
206 respond(x, &fc, nil);
207}
208
209void
210xfidclose(Xfid *x)
211{
212 Fcall fc;
213 Window *w;
214 int q;
215 Text *t;
216
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;
225 }
226
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);
236 }
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);
250 }
251 if(q == QWevent){
252 free(w->dumpstr);
253 free(w->dumpdir);
254 w->dumpstr = nil;
255 w->dumpdir = nil;
256 }
257 }
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;
274 }
275 winunlock(w);
276 winclose(w);
277 }
278 else{
279 switch(q){
280 case Qeditout:
281 qunlock(&editoutlk);
282 break;
283 }
284 }
285 respond(x, &fc, nil);
286}
287
288void
289xfidread(Xfid *x)
290{
291 Fcall fc;
292 int n, q;
293 uint off;
294 char *b;
295 char buf[256];
296 Window *w;
297
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;
315 }
316 respond(x, &fc, nil);
317 return;
318 }
319 winlock(w, 'F');
320 if(w->col == nil){
321 winunlock(w);
322 respond(x, &fc, Edel);
323 return;
324 }
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;
332
333 case QWbody:
334 xfidutfread(x, &w->body, w->body.file->b.nc, QWbody);
335 break;
336
337 case QWctl:
338 b = winctlprint(w, buf, 1);
339 goto Readb;
340
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;
355
356 case QWevent:
357 xfideventread(x, w);
358 break;
359
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;
365 }
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;
369
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;
375 }
376 w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1);
377 break;
378
379 case QWtag:
380 xfidutfread(x, &w->tag, w->tag.file->b.nc, QWtag);
381 break;
382
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;
393 }
394 fc.count = n;
395 fc.data = b;
396 respond(x, &fc, nil);
397 fbuffree(b);
398 break;
399
400 default:
401 sprint(buf, "unknown qid %d in read", q);
402 respond(x, &fc, nil);
403 }
404 winunlock(w);
405}
406
407static int
408shouldscroll(Text *t, uint q0, int qid)
409{
410 if(qid == Qcons)
411 return TRUE;
412 return t->org <= q0 && q0 <= t->org+t->fr.nchars;
413}
414
415static Rune*
416fullrunewrite(Xfid *x, int *inr)
417{
418 int q, cnt, c, nb, nr;
419 Rune *r;
420
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;
428 }
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++;
437 }
438 if(nb < cnt){
439 memmove(x->f->rpart, x->fcall.data+nb, cnt-nb);
440 x->f->nrpart = cnt-nb;
441 }
442 *inr = nr;
443 return r;
444}
445
446void
447xfidwrite(Xfid *x)
448{
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;
457
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;
469 }
470 }
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;
477
478 case Qlabel:
479 fc.count = x->fcall.count;
480 respond(x, &fc, nil);
481 break;
482
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;
494 }
495 if(!eval){
496 respond(x, &fc, Eaddr);
497 break;
498 }
499 w->addr = a;
500 fc.count = x->fcall.count;
501 respond(x, &fc, nil);
502 break;
503
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;
515 }
516 fc.count = x->fcall.count;
517 respond(x, &fc, nil);
518 break;
519
520 case QWerrors:
521 w = errorwinforwin(w);
522 t = &w->body;
523 goto BodyTag;
524
525 case QWbody:
526 case QWwrsel:
527 t = &w->body;
528 goto BodyTag;
529
530 case QWctl:
531 xfidctlwrite(x, w);
532 break;
533
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;
541 }
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);
547 }
548 q0 = a.q0;
549 if(a.q1 > q0){
550 textdelete(t, q0, a.q1, TRUE);
551 w->addr.q1 = q0;
552 }
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;
571
572 case QWevent:
573 xfideventwrite(x, w);
574 break;
575
576 case QWtag:
577 t = &w->tag;
578 goto BodyTag;
579
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);
596 }
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);
602 }
603 winsettag(w);
604 if(qid == QWwrsel)
605 w->wrselrange.q1 += nr;
606 free(r);
607 }
608 fc.count = x->fcall.count;
609 respond(x, &fc, nil);
610 break;
611
612 default:
613 sprint(buf, "unknown qid %d in write", qid);
614 respond(x, &fc, buf);
615 break;
616 }
617 if(w)
618 winunlock(w);
619}
620
621void
622xfidctlwrite(Xfid *x, Window *w)
623{
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;
630
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);
641 }
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;
685 }
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;
692 }
693 for(i=0; i<nr; i++)
694 if(r[i] <= ' '){
695 err = "bad character in file name";
696 goto out;
697 }
698out:
699 seq++;
700 filemark(w->body.file);
701 winsetname(w, r, nr);
702 m += (q+1) - pp;
703 }else
704 if(strncmp(p, "dump ", 5) == 0){ /* set dump string */
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;
711 }
712 *q = 0;
713 nulls = FALSE;
714 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
715 if(nulls){
716 err = "nulls in dump string";
717 break;
718 }
719 w->dumpstr = runetobyte(r, nr);
720 m += (q+1) - pp;
721 }else
722 if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */
723 pp = p+8;
724 m = 8;
725 q = memchr(pp, '\n', e-pp);
726 if(q==nil || q==pp){
727 err = Ebadctl;
728 break;
729 }
730 *q = 0;
731 nulls = FALSE;
732 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
733 if(nulls){
734 err = "nulls in dump directory string";
735 break;
736 }
737 w->dumpdir = runetobyte(r, nr);
738 m += (q+1) - pp;
739 }else
740 if(strncmp(p, "delete", 6) == 0){ /* delete for sure */
741 colclose(w->col, w, TRUE);
742 m = 6;
743 }else
744 if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */
745 if(!winclean(w, TRUE)){
746 err = "file dirty";
747 break;
748 }
749 colclose(w->col, w, TRUE);
750 m = 3;
751 }else
752 if(strncmp(p, "get", 3) == 0){ /* get file */
753 get(&w->body, nil, nil, FALSE, XXX, nil, 0);
754 m = 3;
755 }else
756 if(strncmp(p, "put", 3) == 0){ /* put file */
757 put(&w->body, nil, nil, XXX, XXX, nil, 0);
758 m = 3;
759 }else
760 if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */
761 textcommit(&w->body, TRUE);
762 clampaddr(w);
763 w->body.q0 = w->addr.q0;
764 w->body.q1 = w->addr.q1;
765 textsetselect(&w->body, w->body.q0, w->body.q1);
766 settag = TRUE;
767 m = 8;
768 }else
769 if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */
770 w->addr.q0 = w->body.q0;
771 w->addr.q1 = w->body.q1;
772 m = 8;
773 }else
774 if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */
775 textcommit(&w->body, TRUE);
776 clampaddr(w);
777 w->limit.q0 = w->addr.q0;
778 w->limit.q1 = w->addr.q1;
779 m = 10;
780 }else
781 if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */
782 w->nomark = TRUE;
783 m = 6;
784 }else
785 if(strncmp(p, "mark", 4) == 0){ /* mark file */
786 seq++;
787 filemark(w->body.file);
788 settag = TRUE;
789 m = 4;
790 }else
791 if(strncmp(p, "nomenu", 6) == 0){ /* turn off automatic menu */
792 w->filemenu = FALSE;
793 m = 6;
794 }else
795 if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */
796 w->filemenu = TRUE;
797 m = 4;
798 }else
799 if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */
800 wincleartag(w);
801 settag = TRUE;
802 m = 8;
803 }else{
804 err = Ebadctl;
805 break;
806 }
807 while(p[m] == '\n')
808 m++;
809 }
810
811 if(isfbuf)
812 fbuffree(r);
813 else
814 free(r);
815 if(err)
816 n = 0;
817 fc.count = n;
818 respond(x, &fc, err);
819 if(settag)
820 winsettag(w);
821 if(scrdraw)
822 textscrdraw(&w->body);
823}
824
825void
826xfideventwrite(Xfid *x, Window *w)
827{
828 Fcall fc;
829 int m, n;
830 Rune *r;
831 char *err, *p, *q;
832 int isfbuf;
833 Text *t;
834 int c;
835 uint q0, q1;
836
837 err = nil;
838 isfbuf = TRUE;
839 if(x->fcall.count < RBUFSIZE)
840 r = fbufalloc();
841 else{
842 isfbuf = FALSE;
843 r = emalloc(x->fcall.count*UTFmax+1);
844 }
845 for(n=0; n<x->fcall.count; n+=m){
846 p = x->fcall.data+n;
847 w->owner = *p++; /* disgusting */
848 c = *p++;
849 while(*p == ' ')
850 p++;
851 q0 = strtoul(p, &q, 10);
852 if(q == p)
853 goto Rescue;
854 p = q;
855 while(*p == ' ')
856 p++;
857 q1 = strtoul(p, &q, 10);
858 if(q == p)
859 goto Rescue;
860 p = q;
861 while(*p == ' ')
862 p++;
863 if(*p++ != '\n')
864 goto Rescue;
865 m = p-(x->fcall.data+n);
866 if('a'<=c && c<='z')
867 t = &w->tag;
868 else if('A'<=c && c<='Z')
869 t = &w->body;
870 else
871 goto Rescue;
872 if(q0>t->file->b.nc || q1>t->file->b.nc || q0>q1)
873 goto Rescue;
874
875 qlock(&row.lk); /* just like mousethread */
876 switch(c){
877 case 'x':
878 case 'X':
879 execute(t, q0, q1, TRUE, nil);
880 break;
881 case 'l':
882 case 'L':
883 look3(t, q0, q1, TRUE);
884 break;
885 default:
886 qunlock(&row.lk);
887 goto Rescue;
888 }
889 qunlock(&row.lk);
890
891 }
892
893 Out:
894 if(isfbuf)
895 fbuffree(r);
896 else
897 free(r);
898 if(err)
899 n = 0;
900 fc.count = n;
901 respond(x, &fc, err);
902 return;
903
904 Rescue:
905 err = Ebadevent;
906 goto Out;
907}
908
909void
910xfidutfread(Xfid *x, Text *t, uint q1, int qid)
911{
912 Fcall fc;
913 Window *w;
914 Rune *r;
915 char *b, *b1;
916 uint q, off, boff;
917 int m, n, nr, nb;
918
919 w = t->w;
920 wincommit(w, t);
921 off = x->fcall.offset;
922 r = fbufalloc();
923 b = fbufalloc();
924 b1 = fbufalloc();
925 n = 0;
926 if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
927 boff = w->utflastboff;
928 q = w->utflastq;
929 }else{
930 /* BUG: stupid code: scan from beginning */
931 boff = 0;
932 q = 0;
933 }
934 w->utflastqid = qid;
935 while(q<q1 && n<x->fcall.count){
936 /*
937 * Updating here avoids partial rune problem: we're always on a
938 * char boundary. The cost is we will usually do one more read
939 * than we really need, but that's better than being n^2.
940 */
941 w->utflastboff = boff;
942 w->utflastq = q;
943 nr = q1-q;
944 if(nr > BUFSIZE/UTFmax)
945 nr = BUFSIZE/UTFmax;
946 bufread(&t->file->b, q, r, nr);
947 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
948 if(boff >= off){
949 m = nb;
950 if(boff+m > off+x->fcall.count)
951 m = off+x->fcall.count - boff;
952 memmove(b1+n, b, m);
953 n += m;
954 }else if(boff+nb > off){
955 if(n != 0)
956 error("bad count in utfrune");
957 m = nb - (off-boff);
958 if(m > x->fcall.count)
959 m = x->fcall.count;
960 memmove(b1, b+(off-boff), m);
961 n += m;
962 }
963 boff += nb;
964 q += nr;
965 }
966 fbuffree(r);
967 fbuffree(b);
968 fc.count = n;
969 fc.data = b1;
970 respond(x, &fc, nil);
971 fbuffree(b1);
972}
973
974int
975xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
976{
977 Fcall fc;
978 Window *w;
979 Rune *r, junk;
980 char *b, *b1;
981 uint q, boff;
982 int i, rw, m, n, nr, nb;
983
984 w = t->w;
985 wincommit(w, t);
986 r = fbufalloc();
987 b = fbufalloc();
988 b1 = fbufalloc();
989 n = 0;
990 q = q0;
991 boff = 0;
992 while(q<q1 && n<x->fcall.count){
993 nr = q1-q;
994 if(nr > BUFSIZE/UTFmax)
995 nr = BUFSIZE/UTFmax;
996 bufread(&t->file->b, q, r, nr);
997 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
998 m = nb;
999 if(boff+m > x->fcall.count){
1000 i = x->fcall.count - boff;
1001 /* copy whole runes only */
1002 m = 0;
1003 nr = 0;
1004 while(m < i){
1005 rw = chartorune(&junk, b+m);
1006 if(m+rw > i)
1007 break;
1008 m += rw;
1009 nr++;
1010 }
1011 if(m == 0)
1012 break;
1013 }
1014 memmove(b1+n, b, m);
1015 n += m;
1016 boff += nb;
1017 q += nr;
1018 }
1019 fbuffree(r);
1020 fbuffree(b);
1021 fc.count = n;
1022 fc.data = b1;
1023 respond(x, &fc, nil);
1024 fbuffree(b1);
1025 return q-q0;
1026}
1027
1028void
1029xfideventread(Xfid *x, Window *w)
1030{
1031 Fcall fc;
1032 int i, n;
1033
1034 i = 0;
1035 x->flushed = FALSE;
1036 while(w->nevents == 0){
1037 if(i){
1038 if(!x->flushed)
1039 respond(x, &fc, "window shut down");
1040 return;
1041 }
1042 w->eventx = x;
1043 winunlock(w);
1044 recvp(x->c);
1045 winlock(w, 'F');
1046 i++;
1047 }
1048
1049 n = w->nevents;
1050 if(n > x->fcall.count)
1051 n = x->fcall.count;
1052 fc.count = n;
1053 fc.data = w->events;
1054 respond(x, &fc, nil);
1055 w->nevents -= n;
1056 if(w->nevents){
1057 memmove(w->events, w->events+n, w->nevents);
1058 w->events = erealloc(w->events, w->nevents);
1059 }else{
1060 free(w->events);
1061 w->events = nil;
1062 }
1063}
1064
1065void
1066xfidindexread(Xfid *x)
1067{
1068 Fcall fc;
1069 int i, j, m, n, nmax, isbuf, cnt, off;
1070 Window *w;
1071 char *b;
1072 Rune *r;
1073 Column *c;
1074
1075 qlock(&row.lk);
1076 nmax = 0;
1077 for(j=0; j<row.ncol; j++){
1078 c = row.col[j];
1079 for(i=0; i<c->nw; i++){
1080 w = c->w[i];
1081 nmax += Ctlsize + w->tag.file->b.nc*UTFmax + 1;
1082 }
1083 }
1084 nmax++;
1085 isbuf = (nmax<=RBUFSIZE);
1086 if(isbuf)
1087 b = (char*)x->buf;
1088 else
1089 b = emalloc(nmax);
1090 r = fbufalloc();
1091 n = 0;
1092 for(j=0; j<row.ncol; j++){
1093 c = row.col[j];
1094 for(i=0; i<c->nw; i++){
1095 w = c->w[i];
1096 /* only show the currently active window of a set */
1097 if(w->body.file->curtext != &w->body)
1098 continue;
1099 winctlprint(w, b+n, 0);
1100 n += Ctlsize;
1101 m = min(RBUFSIZE, w->tag.file->b.nc);
1102 bufread(&w->tag.file->b, 0, r, m);
1103 m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1104 while(n<m && b[n]!='\n')
1105 n++;
1106 b[n++] = '\n';
1107 }
1108 }
1109 qunlock(&row.lk);
1110 off = x->fcall.offset;
1111 cnt = x->fcall.count;
1112 if(off > n)
1113 off = n;
1114 if(off+cnt > n)
1115 cnt = n-off;
1116 fc.count = cnt;
1117 memmove(r, b+off, cnt);
1118 fc.data = (char*)r;
1119 if(!isbuf)
1120 free(b);
1121 respond(x, &fc, nil);
1122 fbuffree(r);
1123}