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