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