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