]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/acme/xfid.c
ndb/dnsquery, ndb/csquery: handle long lines
[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-1)/UTFmax)
151                                         n = (BUFSIZE-1)/UTFmax;
152                                 bufread(t->file, q0, r, n);
153                                 m = snprint(s, BUFSIZE, "%.*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[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*
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
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
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 scrdraw, settag;
586         Text *t;
587
588         err = nil;
589         e = x->data+x->count;
590         scrdraw = FALSE;
591         settag = FALSE;
592         r = emalloc(x->count*UTFmax+1);
593         x->data[x->count] = 0;
594         textcommit(&w->tag, TRUE);
595         for(n=0; n<x->count; n+=m){
596                 p = x->data+n;
597                 if(strncmp(p, "lock", 4) == 0){ /* make window exclusive use */
598                         qlock(&w->ctllock);
599                         w->ctlfid = x->f->fid;
600                         m = 4;
601                 }else
602                 if(strncmp(p, "unlock", 6) == 0){       /* release exclusive use */
603                         w->ctlfid = ~0;
604                         qunlock(&w->ctllock);
605                         m = 6;
606                 }else
607                 if(strncmp(p, "clean", 5) == 0){        /* mark window 'clean', seq=0 */
608                         t = &w->body;
609                         t->eq0 = ~0;
610                         filereset(t->file);
611                         t->file->mod = FALSE;
612                         w->dirty = FALSE;
613                         settag = TRUE;
614                         m = 5;
615                 }else
616                 if(strncmp(p, "dirty", 5) == 0){        /* mark window 'dirty' */
617                         t = &w->body;
618                         /* doesn't change sequence number, so "Put" won't appear.  it shouldn't. */
619                         t->file->mod = TRUE;
620                         w->dirty = TRUE;
621                         settag = TRUE;
622                         m = 5;
623                 }else
624                 if(strncmp(p, "show", 4) == 0){ /* show dot */
625                         t = &w->body;
626                         textshow(t, t->q0, t->q1, 1);
627                         m = 4;
628                 }else
629                 if(strncmp(p, "name ", 5) == 0){        /* set file name */
630                         pp = p+5;
631                         m = 5;
632                         q = memchr(pp, '\n', e-pp);
633                         if(q==nil || q==pp){
634                                 err = Ebadctl;
635                                 break;
636                         }
637                         *q = 0;
638                         nulls = FALSE;
639                         cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
640                         if(nulls){
641                                 err = "nulls in file name";
642                                 break;
643                         }
644                         for(i=0; i<nr; i++)
645                                 if(r[i] <= ' '){
646                                         err = "bad character in file name";
647                                         goto out;
648                                 }
649 out:
650                         seq++;
651                         filemark(w->body.file);
652                         winsetname(w, r, nr);
653                         m += (q+1) - pp;
654                 }else
655                 if(strncmp(p, "dump ", 5) == 0){        /* set dump string */
656                         pp = p+5;
657                         m = 5;
658                         q = memchr(pp, '\n', e-pp);
659                         if(q==nil || q==pp){
660                                 err = Ebadctl;
661                                 break;
662                         }
663                         *q = 0;
664                         nulls = FALSE;
665                         cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
666                         if(nulls){
667                                 err = "nulls in dump string";
668                                 break;
669                         }
670                         w->dumpstr = runetobyte(r, nr);
671                         m += (q+1) - pp;
672                 }else
673                 if(strncmp(p, "dumpdir ", 8) == 0){     /* set dump directory */
674                         pp = p+8;
675                         m = 8;
676                         q = memchr(pp, '\n', e-pp);
677                         if(q==nil || q==pp){
678                                 err = Ebadctl;
679                                 break;
680                         }
681                         *q = 0;
682                         nulls = FALSE;
683                         cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
684                         if(nulls){
685                                 err = "nulls in dump directory string";
686                                 break;
687                         }
688                         w->dumpdir = runetobyte(r, nr);
689                         m += (q+1) - pp;
690                 }else
691                 if(strncmp(p, "delete", 6) == 0){       /* delete for sure */
692                         colclose(w->col, w, TRUE);
693                         m = 6;
694                 }else
695                 if(strncmp(p, "del", 3) == 0){  /* delete, but check dirty */
696                         if(!winclean(w, TRUE)){
697                                 err = "file dirty";
698                                 break;
699                         }
700                         colclose(w->col, w, TRUE);
701                         m = 3;
702                 }else
703                 if(strncmp(p, "get", 3) == 0){  /* get file */
704                         get(&w->body, nil, nil, FALSE, XXX, nil, 0);
705                         m = 3;
706                 }else
707                 if(strncmp(p, "put", 3) == 0){  /* put file */
708                         put(&w->body, nil, nil, XXX, XXX, nil, 0);
709                         m = 3;
710                 }else
711                 if(strncmp(p, "dot=addr", 8) == 0){     /* set dot */
712                         textcommit(&w->body, TRUE);
713                         clampaddr(w);
714                         w->body.q0 = w->addr.q0;
715                         w->body.q1 = w->addr.q1;
716                         textsetselect(&w->body, w->body.q0, w->body.q1);
717                         settag = TRUE;
718                         m = 8;
719                 }else
720                 if(strncmp(p, "addr=dot", 8) == 0){     /* set addr */
721                         w->addr.q0 = w->body.q0;
722                         w->addr.q1 = w->body.q1;
723                         m = 8;
724                 }else
725                 if(strncmp(p, "limit=addr", 10) == 0){  /* set limit */
726                         textcommit(&w->body, TRUE);
727                         clampaddr(w);
728                         w->limit.q0 = w->addr.q0;
729                         w->limit.q1 = w->addr.q1;
730                         m = 10;
731                 }else
732                 if(strncmp(p, "nomark", 6) == 0){       /* turn off automatic marking */
733                         w->nomark = TRUE;
734                         m = 6;
735                 }else
736                 if(strncmp(p, "mark", 4) == 0){ /* mark file */
737                         seq++;
738                         filemark(w->body.file);
739                         settag = TRUE;
740                         m = 4;
741                 }else
742                 if(strncmp(p, "nomenu", 6) == 0){       /* turn off automatic menu */
743                         w->filemenu = FALSE;
744                         m = 6;
745                 }else
746                 if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */
747                         w->filemenu = TRUE;
748                         m = 4;
749                 }else
750                 if(strncmp(p, "noscroll", 8) == 0){     /* turn off automatic scrolling */
751                         w->noscroll = TRUE;
752                         m = 8;
753                 }else
754                 if(strncmp(p, "cleartag", 8) == 0){     /* wipe tag right of bar */
755                         wincleartag(w);
756                         settag = TRUE;
757                         m = 8;
758                 }else
759                 if(strncmp(p, "scroll", 6) == 0){       /* turn on automatic scrolling (writes to body only) */
760                         w->noscroll = FALSE;
761                         m = 6;
762                 }else{
763                         err = Ebadctl;
764                         break;
765                 }
766                 while(p[m] == '\n')
767                         m++;
768         }
769
770         free(r);
771         if(err)
772                 n = 0;
773         fc.count = n;
774         respond(x, &fc, err);
775         if(settag)
776                 winsettag(w);
777         if(scrdraw)
778                 textscrdraw(&w->body);
779 }
780
781 void
782 xfideventwrite(Xfid *x, Window *w)
783 {
784         Fcall fc;
785         int m, n;
786         Rune *r;
787         char *err, *p, *q;
788         Text *t;
789         int c;
790         uint q0, q1;
791
792         err = nil;
793         r = emalloc(x->count*UTFmax+1);
794         for(n=0; n<x->count; n+=m){
795                 p = x->data+n;
796                 w->owner = *p++;        /* disgusting */
797                 c = *p++;
798                 while(*p == ' ')
799                         p++;
800                 q0 = strtoul(p, &q, 10);
801                 if(q == p)
802                         goto Rescue;
803                 p = q;
804                 while(*p == ' ')
805                         p++;
806                 q1 = strtoul(p, &q, 10);
807                 if(q == p)
808                         goto Rescue;
809                 p = q;
810                 while(*p == ' ')
811                         p++;
812                 if(*p++ != '\n')
813                         goto Rescue;
814                 m = p-(x->data+n);
815                 if('a'<=c && c<='z')
816                         t = &w->tag;
817                 else if('A'<=c && c<='Z')
818                         t = &w->body;
819                 else
820                         goto Rescue;
821                 if(q0>t->file->nc || q1>t->file->nc || q0>q1)
822                         goto Rescue;
823
824                 qlock(&row);    /* just like mousethread */
825                 switch(c){
826                 case 'x':
827                 case 'X':
828                         execute(t, q0, q1, TRUE, nil);
829                         break;
830                 case 'l':
831                 case 'L':
832                         look3(t, q0, q1, TRUE);
833                         break;
834                 default:
835                         qunlock(&row);
836                         goto Rescue;
837                 }
838                 qunlock(&row);
839
840         }
841
842     Out:
843         free(r);
844         if(err)
845                 n = 0;
846         fc.count = n;
847         respond(x, &fc, err);
848         return;
849
850     Rescue:
851         err = Ebadevent;
852         goto Out;
853 }
854
855 void
856 xfidutfread(Xfid *x, Text *t, uint q1, int qid)
857 {
858         Fcall fc;
859         Window *w;
860         Rune *r;
861         char *b, *b1;
862         uint q, off, boff;
863         int m, n, nr, nb;
864
865         w = t->w;
866         wincommit(w, t);
867         off = x->offset;
868         r = fbufalloc();
869         b = fbufalloc();
870         b1 = emalloc(x->count);
871         n = 0;
872         if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
873                 boff = w->utflastboff;
874                 q = w->utflastq;
875         }else{
876                 /* BUG: stupid code: scan from beginning */
877                 boff = 0;
878                 q = 0;
879         }
880         w->utflastqid = qid;
881         while(q<q1 && n<x->count){
882                 /*
883                  * Updating here avoids partial rune problem: we're always on a
884                  * char boundary. The cost is we will usually do one more read
885                  * than we really need, but that's better than being n^2.
886                  */
887                 w->utflastboff = boff;
888                 w->utflastq = q;
889                 nr = q1-q;
890                 if(nr > (BUFSIZE-1)/UTFmax)
891                         nr = (BUFSIZE-1)/UTFmax;
892                 bufread(t->file, q, r, nr);
893                 nb = snprint(b, BUFSIZE, "%.*S", nr, r);
894                 if(boff >= off){
895                         m = nb;
896                         if(boff+m > off+x->count)
897                                 m = off+x->count - boff;
898                         memmove(b1+n, b, m);
899                         n += m;
900                 }else if(boff+nb > off){
901                         if(n != 0)
902                                 error("bad count in utfrune");
903                         m = nb - (off-boff);
904                         if(m > x->count)
905                                 m = x->count;
906                         memmove(b1, b+(off-boff), m);
907                         n += m;
908                 }
909                 boff += nb;
910                 q += nr;
911         }
912         fbuffree(r);
913         fbuffree(b);
914         fc.count = n;
915         fc.data = b1;
916         respond(x, &fc, nil);
917         free(b1);
918 }
919
920 int
921 xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
922 {
923         Fcall fc;
924         Window *w;
925         Rune *r, junk;
926         char *b, *b1;
927         uint q, boff;
928         int i, rw, m, n, nr, nb;
929
930         w = t->w;
931         wincommit(w, t);
932         r = fbufalloc();
933         b = fbufalloc();
934         b1 = emalloc(x->count);
935         n = 0;
936         q = q0;
937         boff = 0;
938         while(q<q1 && n<x->count){
939                 nr = q1-q;
940                 if(nr > (BUFSIZE-1)/UTFmax)
941                         nr = (BUFSIZE-1)/UTFmax;
942                 bufread(t->file, q, r, nr);
943                 nb = snprint(b, BUFSIZE, "%.*S", nr, r);
944                 m = nb;
945                 if(boff+m > x->count){
946                         i = x->count - boff;
947                         /* copy whole runes only */
948                         m = 0;
949                         nr = 0;
950                         while(m < i){
951                                 rw = chartorune(&junk, b+m);
952                                 if(m+rw > i)
953                                         break;
954                                 m += rw;
955                                 nr++;
956                         }
957                         if(m == 0)
958                                 break;
959                 }
960                 memmove(b1+n, b, m);
961                 n += m;
962                 boff += nb;
963                 q += nr;
964         }
965         fbuffree(r);
966         fbuffree(b);
967         fc.count = n;
968         fc.data = b1;
969         respond(x, &fc, nil);
970         free(b1);
971         return q-q0;
972 }
973
974 void
975 xfideventread(Xfid *x, Window *w)
976 {
977         Fcall fc;
978         char *b;
979         int i, n;
980
981         i = 0;
982         x->flushed = FALSE;
983         while(w->nevents == 0){
984                 if(i){
985                         if(!x->flushed)
986                                 respond(x, &fc, "window shut down");
987                         return;
988                 }
989                 w->eventx = x;
990                 winunlock(w);
991                 recvp(x->c);
992                 winlock(w, 'F');
993                 i++;
994         }
995
996         n = w->nevents;
997         if(n > x->count)
998                 n = x->count;
999         fc.count = n;
1000         fc.data = w->events;
1001         respond(x, &fc, nil);
1002         b = w->events;
1003         w->events = estrdup(w->events+n);
1004         free(b);
1005         w->nevents -= n;
1006 }
1007
1008 void
1009 xfidindexread(Xfid *x)
1010 {
1011         Fcall fc;
1012         int i, j, m, n, nmax, isbuf, cnt, off;
1013         Window *w;
1014         char *b;
1015         Rune *r;
1016         Column *c;
1017
1018         qlock(&row);
1019         nmax = 0;
1020         for(j=0; j<row.ncol; j++){
1021                 c = row.col[j];
1022                 for(i=0; i<c->nw; i++){
1023                         w = c->w[i];
1024                         nmax += Ctlsize + w->tag.file->nc*UTFmax + 1;
1025                 }
1026         }
1027         nmax++;
1028         isbuf = (nmax<=RBUFSIZE);
1029         if(isbuf)
1030                 b = (char*)x->buf;
1031         else
1032                 b = emalloc(nmax);
1033         r = fbufalloc();
1034         n = 0;
1035         for(j=0; j<row.ncol; j++){
1036                 c = row.col[j];
1037                 for(i=0; i<c->nw; i++){
1038                         w = c->w[i];
1039                         /* only show the currently active window of a set */
1040                         if(w->body.file->curtext != &w->body)
1041                                 continue;
1042                         winctlprint(w, b+n, 0);
1043                         n += Ctlsize;
1044                         m = min(RBUFSIZE, w->tag.file->nc);
1045                         bufread(w->tag.file, 0, r, m);
1046                         m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1047                         while(n<m && b[n]!='\n')
1048                                 n++;
1049                         b[n++] = '\n';
1050                 }
1051         }
1052         qunlock(&row);
1053         off = x->offset;
1054         cnt = x->count;
1055         if(off > n)
1056                 off = n;
1057         if(off+cnt > n)
1058                 cnt = n-off;
1059         fc.count = cnt;
1060         memmove(r, b+off, cnt);
1061         fc.data = (char*)r;
1062         if(!isbuf)
1063                 free(b);
1064         respond(x, &fc, nil);
1065         fbuffree(r);
1066 }