URI:
       texec.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
       ---
       texec.c (37425B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <draw.h>
            5 #include <thread.h>
            6 #include <cursor.h>
            7 #include <mouse.h>
            8 #include <keyboard.h>
            9 #include <frame.h>
           10 #include <fcall.h>
           11 #include <plumb.h>
           12 #include <libsec.h>
           13 #include <9pclient.h>
           14 #include "dat.h"
           15 #include "fns.h"
           16 
           17 Buffer        snarfbuf;
           18 
           19 /*
           20  * These functions get called as:
           21  *
           22  *        fn(et, t, argt, flag1, flag1, flag2, s, n);
           23  *
           24  * Where the arguments are:
           25  *
           26  *        et: the Text* in which the executing event (click) occurred
           27  *        t: the Text* containing the current selection (Edit, Cut, Snarf, Paste)
           28  *        argt: the Text* containing the argument for a 2-1 click.
           29  *        e->flag1: from Exectab entry
           30  *         e->flag2: from Exectab entry
           31  *        s: the command line remainder (e.g., "x" if executing "Dump x")
           32  *        n: length of s  (s is *not* NUL-terminated)
           33  */
           34 
           35 void doabort(Text*, Text*, Text*, int, int, Rune*, int);
           36 void        del(Text*, Text*, Text*, int, int, Rune*, int);
           37 void        delcol(Text*, Text*, Text*, int, int, Rune*, int);
           38 void        dotfiles(Text*, Text*, Text*, int, int, Rune*, int);
           39 void        dump(Text*, Text*, Text*, int, int, Rune*, int);
           40 void        edit(Text*, Text*, Text*, int, int, Rune*, int);
           41 void        xexit(Text*, Text*, Text*, int, int, Rune*, int);
           42 void        fontx(Text*, Text*, Text*, int, int, Rune*, int);
           43 void        get(Text*, Text*, Text*, int, int, Rune*, int);
           44 void        id(Text*, Text*, Text*, int, int, Rune*, int);
           45 void        incl(Text*, Text*, Text*, int, int, Rune*, int);
           46 void        indent(Text*, Text*, Text*, int, int, Rune*, int);
           47 void        xkill(Text*, Text*, Text*, int, int, Rune*, int);
           48 void        local(Text*, Text*, Text*, int, int, Rune*, int);
           49 void        look(Text*, Text*, Text*, int, int, Rune*, int);
           50 void        newcol(Text*, Text*, Text*, int, int, Rune*, int);
           51 void        paste(Text*, Text*, Text*, int, int, Rune*, int);
           52 void        put(Text*, Text*, Text*, int, int, Rune*, int);
           53 void        putall(Text*, Text*, Text*, int, int, Rune*, int);
           54 void        sendx(Text*, Text*, Text*, int, int, Rune*, int);
           55 void        sort(Text*, Text*, Text*, int, int, Rune*, int);
           56 void        tab(Text*, Text*, Text*, int, int, Rune*, int);
           57 void        zeroxx(Text*, Text*, Text*, int, int, Rune*, int);
           58 
           59 typedef struct Exectab Exectab;
           60 struct Exectab
           61 {
           62         Rune        *name;
           63         void        (*fn)(Text*, Text*, Text*, int, int, Rune*, int);
           64         int                mark;
           65         int                flag1;
           66         int                flag2;
           67 };
           68 
           69 static Rune LAbort[] = { 'A', 'b', 'o', 'r', 't', 0 };
           70 static Rune LCut[] = { 'C', 'u', 't', 0 };
           71 static Rune LDel[] = { 'D', 'e', 'l', 0 };
           72 static Rune LDelcol[] = { 'D', 'e', 'l', 'c', 'o', 'l', 0 };
           73 static Rune LDelete[] = { 'D', 'e', 'l', 'e', 't', 'e', 0 };
           74 static Rune LDump[] = { 'D', 'u', 'm', 'p', 0 };
           75 static Rune LEdit[] = { 'E', 'd', 'i', 't', 0 };
           76 static Rune LExit[] = { 'E', 'x', 'i', 't', 0 };
           77 static Rune LFont[] = { 'F', 'o', 'n', 't', 0 };
           78 static Rune LGet[] = { 'G', 'e', 't', 0 };
           79 static Rune LID[] = { 'I', 'D', 0 };
           80 static Rune LIncl[] = { 'I', 'n', 'c', 'l', 0 };
           81 static Rune LIndent[] = { 'I', 'n', 'd', 'e', 'n', 't', 0 };
           82 static Rune LKill[] = { 'K', 'i', 'l', 'l', 0 };
           83 static Rune LLoad[] = { 'L', 'o', 'a', 'd', 0 };
           84 static Rune LLocal[] = { 'L', 'o', 'c', 'a', 'l', 0 };
           85 static Rune LLook[] = { 'L', 'o', 'o', 'k', 0 };
           86 static Rune LNew[] = { 'N', 'e', 'w', 0 };
           87 static Rune LNewcol[] = { 'N', 'e', 'w', 'c', 'o', 'l', 0 };
           88 static Rune LPaste[] = { 'P', 'a', 's', 't', 'e', 0 };
           89 static Rune LPut[] = { 'P', 'u', 't', 0 };
           90 static Rune LPutall[] = { 'P', 'u', 't', 'a', 'l', 'l', 0 };
           91 static Rune LRedo[] = { 'R', 'e', 'd', 'o', 0 };
           92 static Rune LSend[] = { 'S', 'e', 'n', 'd', 0 };
           93 static Rune LSnarf[] = { 'S', 'n', 'a', 'r', 'f', 0 };
           94 static Rune LSort[] = { 'S', 'o', 'r', 't', 0 };
           95 static Rune LTab[] = { 'T', 'a', 'b', 0 };
           96 static Rune LUndo[] = { 'U', 'n', 'd', 'o', 0 };
           97 static Rune LZerox[] = { 'Z', 'e', 'r', 'o', 'x', 0 };
           98 
           99 Exectab exectab[] = {
          100         { LAbort,                doabort,        FALSE,        XXX,                XXX,                },
          101         { LCut,                cut,                TRUE,        TRUE,        TRUE        },
          102         { LDel,                del,                FALSE,        FALSE,        XXX                },
          103         { LDelcol,                delcol,        FALSE,        XXX,                XXX                },
          104         { LDelete,                del,                FALSE,        TRUE,        XXX                },
          105         { LDump,                dump,        FALSE,        TRUE,        XXX                },
          106         { LEdit,                edit,                FALSE,        XXX,                XXX                },
          107         { LExit,                xexit,        FALSE,        XXX,                XXX                },
          108         { LFont,                fontx,        FALSE,        XXX,                XXX                },
          109         { LGet,                get,                FALSE,        TRUE,        XXX                },
          110         { LID,                id,                FALSE,        XXX,                XXX                },
          111         { LIncl,                incl,                FALSE,        XXX,                XXX                },
          112         { LIndent,                indent,        FALSE,        XXX,                XXX                },
          113         { LKill,                xkill,                FALSE,        XXX,                XXX                },
          114         { LLoad,                dump,        FALSE,        FALSE,        XXX                },
          115         { LLocal,                local,        FALSE,        XXX,                XXX                },
          116         { LLook,                look,                FALSE,        XXX,                XXX                },
          117         { LNew,                new,                FALSE,        XXX,                XXX                },
          118         { LNewcol,        newcol,        FALSE,        XXX,                XXX                },
          119         { LPaste,                paste,        TRUE,        TRUE,        XXX                },
          120         { LPut,                put,                FALSE,        XXX,                XXX                },
          121         { LPutall,                putall,        FALSE,        XXX,                XXX                },
          122         { LRedo,                undo,        FALSE,        FALSE,        XXX                },
          123         { LSend,                sendx,        TRUE,        XXX,                XXX                },
          124         { LSnarf,                cut,                FALSE,        TRUE,        FALSE        },
          125         { LSort,                sort,                FALSE,        XXX,                XXX                },
          126         { LTab,                tab,                FALSE,        XXX,                XXX                },
          127         { LUndo,                undo,        FALSE,        TRUE,        XXX                },
          128         { LZerox,                zeroxx,        FALSE,        XXX,                XXX                },
          129         { nil,                         0,                0,                0,                0                }
          130 };
          131 
          132 Exectab*
          133 lookup(Rune *r, int n)
          134 {
          135         Exectab *e;
          136         int nr;
          137 
          138         r = skipbl(r, n, &n);
          139         if(n == 0)
          140                 return nil;
          141         findbl(r, n, &nr);
          142         nr = n-nr;
          143         for(e=exectab; e->name; e++)
          144                 if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE)
          145                         return e;
          146         return nil;
          147 }
          148 
          149 int
          150 isexecc(int c)
          151 {
          152         if(isfilec(c))
          153                 return 1;
          154         return c=='<' || c=='|' || c=='>';
          155 }
          156 
          157 void
          158 execute(Text *t, uint aq0, uint aq1, int external, Text *argt)
          159 {
          160         uint q0, q1;
          161         Rune *r, *s;
          162         char *b, *a, *aa;
          163         Exectab *e;
          164         int c, n, f;
          165         Runestr dir;
          166 
          167         q0 = aq0;
          168         q1 = aq1;
          169         if(q1 == q0){        /* expand to find word (actually file name) */
          170                 /* if in selection, choose selection */
          171                 if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
          172                         q0 = t->q0;
          173                         q1 = t->q1;
          174                 }else{
          175                         while(q1<t->file->b.nc && isexecc(c=textreadc(t, q1)) && c!=':')
          176                                 q1++;
          177                         while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':')
          178                                 q0--;
          179                         if(q1 == q0)
          180                                 return;
          181                 }
          182         }
          183         r = runemalloc(q1-q0);
          184         bufread(&t->file->b, q0, r, q1-q0);
          185         e = lookup(r, q1-q0);
          186         if(!external && t->w!=nil && t->w->nopen[QWevent]>0){
          187                 f = 0;
          188                 if(e)
          189                         f |= 1;
          190                 if(q0!=aq0 || q1!=aq1){
          191                         bufread(&t->file->b, aq0, r, aq1-aq0);
          192                         f |= 2;
          193                 }
          194                 aa = getbytearg(argt, TRUE, TRUE, &a);
          195                 if(a){
          196                         if(strlen(a) > EVENTSIZE){        /* too big; too bad */
          197                                 free(r);
          198                                 free(aa);
          199                                 free(a);
          200                                 warning(nil, "argument string too long\n");
          201                                 return;
          202                         }
          203                         f |= 8;
          204                 }
          205                 c = 'x';
          206                 if(t->what == Body)
          207                         c = 'X';
          208                 n = aq1-aq0;
          209                 if(n <= EVENTSIZE)
          210                         winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r);
          211                 else
          212                         winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f);
          213                 if(q0!=aq0 || q1!=aq1){
          214                         n = q1-q0;
          215                         bufread(&t->file->b, q0, r, n);
          216                         if(n <= EVENTSIZE)
          217                                 winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r);
          218                         else
          219                                 winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1);
          220                 }
          221                 if(a){
          222                         winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a);
          223                         if(aa)
          224                                 winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa);
          225                         else
          226                                 winevent(t->w, "%c0 0 0 0 \n", c);
          227                 }
          228                 free(r);
          229                 free(aa);
          230                 free(a);
          231                 return;
          232         }
          233         if(e){
          234                 if(e->mark && seltext!=nil)
          235                 if(seltext->what == Body){
          236                         seq++;
          237                         filemark(seltext->w->body.file);
          238                 }
          239                 s = skipbl(r, q1-q0, &n);
          240                 s = findbl(s, n, &n);
          241                 s = skipbl(s, n, &n);
          242                 (*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n);
          243                 free(r);
          244                 return;
          245         }
          246 
          247         b = runetobyte(r, q1-q0);
          248         free(r);
          249         dir = dirname(t, nil, 0);
          250         if(dir.nr==1 && dir.r[0]=='.'){        /* sigh */
          251                 free(dir.r);
          252                 dir.r = nil;
          253                 dir.nr = 0;
          254         }
          255         aa = getbytearg(argt, TRUE, TRUE, &a);
          256         if(t->w)
          257                 incref(&t->w->ref);
          258         run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE);
          259 }
          260 
          261 char*
          262 printarg(Text *argt, uint q0, uint q1)
          263 {
          264         char *buf;
          265 
          266         if(argt->what!=Body || argt->file->name==nil)
          267                 return nil;
          268         buf = emalloc(argt->file->nname+32);
          269         if(q0 == q1)
          270                 sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0);
          271         else
          272                 sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1);
          273         return buf;
          274 }
          275 
          276 char*
          277 getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp)
          278 {
          279         int n;
          280         Expand e;
          281         char *a;
          282 
          283         *rp = nil;
          284         *nrp = 0;
          285         if(argt == nil)
          286                 return nil;
          287         a = nil;
          288         textcommit(argt, TRUE);
          289         if(expand(argt, argt->q0, argt->q1, &e)){
          290                 free(e.bname);
          291                 if(e.nname && dofile){
          292                         e.name = runerealloc(e.name, e.nname+1);
          293                         if(doaddr)
          294                                 a = printarg(argt, e.q0, e.q1);
          295                         *rp = e.name;
          296                         *nrp = e.nname;
          297                         return a;
          298                 }
          299                 free(e.name);
          300         }else{
          301                 e.q0 = argt->q0;
          302                 e.q1 = argt->q1;
          303         }
          304         n = e.q1 - e.q0;
          305         *rp = runemalloc(n+1);
          306         bufread(&argt->file->b, e.q0, *rp, n);
          307         if(doaddr)
          308                 a = printarg(argt, e.q0, e.q1);
          309         *nrp = n;
          310         return a;
          311 }
          312 
          313 char*
          314 getbytearg(Text *argt, int doaddr, int dofile, char **bp)
          315 {
          316         Rune *r;
          317         int n;
          318         char *aa;
          319 
          320         *bp = nil;
          321         aa = getarg(argt, doaddr, dofile, &r, &n);
          322         if(r == nil)
          323                 return nil;
          324         *bp = runetobyte(r, n);
          325         free(r);
          326         return aa;
          327 }
          328 
          329 void
          330 doabort(Text *__0, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
          331 {
          332         static int n;
          333 
          334         USED(__0);
          335         USED(_0);
          336         USED(_1);
          337         USED(_2);
          338         USED(_3);
          339         USED(_4);
          340         USED(_5);
          341 
          342         if(n++ == 0)
          343                 warning(nil, "executing Abort again will call abort()\n");
          344         else
          345                 abort();
          346 }
          347 
          348 void
          349 newcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
          350 {
          351         Column *c;
          352         Window *w;
          353 
          354         USED(_0);
          355         USED(_1);
          356         USED(_2);
          357         USED(_3);
          358         USED(_4);
          359         USED(_5);
          360 
          361         c = rowadd(et->row, nil, -1);
          362         if(c) {
          363                 w = coladd(c, nil, nil, -1);
          364                 winsettag(w);
          365                 xfidlog(w, "new");
          366         }
          367 }
          368 
          369 void
          370 delcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
          371 {
          372         int i;
          373         Column *c;
          374         Window *w;
          375 
          376         USED(_0);
          377         USED(_1);
          378         USED(_2);
          379         USED(_3);
          380         USED(_4);
          381         USED(_5);
          382 
          383         c = et->col;
          384         if(c==nil || colclean(c)==0)
          385                 return;
          386         for(i=0; i<c->nw; i++){
          387                 w = c->w[i];
          388                 if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata]+w->nopen[QWxdata] > 0){
          389                         warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name);
          390                         return;
          391                 }
          392         }
          393         rowclose(et->col->row, et->col, TRUE);
          394 }
          395 
          396 void
          397 del(Text *et, Text *_0, Text *_1, int flag1, int _2, Rune *_3, int _4)
          398 {
          399         USED(_0);
          400         USED(_1);
          401         USED(_2);
          402         USED(_3);
          403         USED(_4);
          404 
          405         if(et->col==nil || et->w == nil)
          406                 return;
          407         if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE))
          408                 colclose(et->col, et->w, TRUE);
          409 }
          410 
          411 void
          412 sort(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
          413 {
          414         USED(_0);
          415         USED(_1);
          416         USED(_2);
          417         USED(_3);
          418         USED(_4);
          419         USED(_5);
          420 
          421         if(et->col)
          422                 colsort(et->col);
          423 }
          424 
          425 uint
          426 seqof(Window *w, int isundo)
          427 {
          428         /* if it's undo, see who changed with us */
          429         if(isundo)
          430                 return w->body.file->seq;
          431         /* if it's redo, see who we'll be sync'ed up with */
          432         return fileredoseq(w->body.file);
          433 }
          434 
          435 void
          436 undo(Text *et, Text *_0, Text *_1, int flag1, int _2, Rune *_3, int _4)
          437 {
          438         int i, j;
          439         Column *c;
          440         Window *w;
          441         uint seq;
          442 
          443         USED(_0);
          444         USED(_1);
          445         USED(_2);
          446         USED(_3);
          447         USED(_4);
          448 
          449         if(et==nil || et->w== nil)
          450                 return;
          451         seq = seqof(et->w, flag1);
          452         if(seq == 0){
          453                 /* nothing to undo */
          454                 return;
          455         }
          456         /*
          457          * Undo the executing window first. Its display will update. other windows
          458          * in the same file will not call show() and jump to a different location in the file.
          459          * Simultaneous changes to other files will be chaotic, however.
          460          */
          461         winundo(et->w, flag1);
          462         for(i=0; i<row.ncol; i++){
          463                 c = row.col[i];
          464                 for(j=0; j<c->nw; j++){
          465                         w = c->w[j];
          466                         if(w == et->w)
          467                                 continue;
          468                         if(seqof(w, flag1) == seq)
          469                                 winundo(w, flag1);
          470                 }
          471         }
          472 }
          473 
          474 char*
          475 getname(Text *t, Text *argt, Rune *arg, int narg, int isput)
          476 {
          477         char *s;
          478         Rune *r;
          479         int i, n, promote;
          480         Runestr dir;
          481 
          482         getarg(argt, FALSE, TRUE, &r, &n);
          483         promote = FALSE;
          484         if(r == nil)
          485                 promote = TRUE;
          486         else if(isput){
          487                 /* if are doing a Put, want to synthesize name even for non-existent file */
          488                 /* best guess is that file name doesn't contain a slash */
          489                 promote = TRUE;
          490                 for(i=0; i<n; i++)
          491                         if(r[i] == '/'){
          492                                 promote = FALSE;
          493                                 break;
          494                         }
          495                 if(promote){
          496                         t = argt;
          497                         arg = r;
          498                         narg = n;
          499                 }
          500         }
          501         if(promote){
          502                 n = narg;
          503                 if(n <= 0){
          504                         s = runetobyte(t->file->name, t->file->nname);
          505                         return s;
          506                 }
          507                 /* prefix with directory name if necessary */
          508                 dir.r = nil;
          509                 dir.nr = 0;
          510                 if(n>0 && arg[0]!='/'){
          511                         dir = dirname(t, nil, 0);
          512                         if(dir.nr==1 && dir.r[0]=='.'){        /* sigh */
          513                                 free(dir.r);
          514                                 dir.r = nil;
          515                                 dir.nr = 0;
          516                         }
          517                 }
          518                 if(dir.r){
          519                         r = runemalloc(dir.nr+n+1);
          520                         runemove(r, dir.r, dir.nr);
          521                         free(dir.r);
          522                         if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/')
          523                                 r[dir.nr++] = '/';
          524                         runemove(r+dir.nr, arg, n);
          525                         n += dir.nr;
          526                 }else{
          527                         r = runemalloc(n+1);
          528                         runemove(r, arg, n);
          529                 }
          530         }
          531         s = runetobyte(r, n);
          532         free(r);
          533         if(strlen(s) == 0){
          534                 free(s);
          535                 s = nil;
          536         }
          537         return s;
          538 }
          539 
          540 void
          541 zeroxx(Text *et, Text *t, Text *_1, int _2, int _3, Rune *_4, int _5)
          542 {
          543         Window *nw;
          544         int c, locked;
          545 
          546         USED(_1);
          547         USED(_2);
          548         USED(_3);
          549         USED(_4);
          550         USED(_5);
          551 
          552         locked = FALSE;
          553         if(t!=nil && t->w!=nil && t->w!=et->w){
          554                 locked = TRUE;
          555                 c = 'M';
          556                 if(et->w)
          557                         c = et->w->owner;
          558                 winlock(t->w, c);
          559         }
          560         if(t == nil)
          561                 t = et;
          562         if(t==nil || t->w==nil)
          563                 return;
          564         t = &t->w->body;
          565         if(t->w->isdir)
          566                 warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name);
          567         else{
          568                 nw = coladd(t->w->col, nil, t->w, -1);
          569                 /* ugly: fix locks so w->unlock works */
          570                 winlock1(nw, t->w->owner);
          571                 xfidlog(nw, "zerox");
          572         }
          573         if(locked)
          574                 winunlock(t->w);
          575 }
          576 
          577 typedef struct TextAddr TextAddr;
          578 struct TextAddr {
          579         long lorigin; // line+rune for origin
          580         long rorigin;
          581         long lq0; // line+rune for q0
          582         long rq0;
          583         long lq1; // line+rune for q1
          584         long rq1;
          585 };
          586 
          587 void
          588 get(Text *et, Text *t, Text *argt, int flag1, int _0, Rune *arg, int narg)
          589 {
          590         char *name;
          591         Rune *r;
          592         int i, n, dirty, samename, isdir;
          593         TextAddr *addr, *a;
          594         Window *w;
          595         Text *u;
          596         Dir *d;
          597         long q0, q1;
          598 
          599         USED(_0);
          600 
          601         if(flag1)
          602                 if(et==nil || et->w==nil)
          603                         return;
          604         if(!et->w->isdir && (et->w->body.file->b.nc>0 && !winclean(et->w, TRUE)))
          605                 return;
          606         w = et->w;
          607         t = &w->body;
          608         name = getname(t, argt, arg, narg, FALSE);
          609         if(name == nil){
          610                 warning(nil, "no file name\n");
          611                 return;
          612         }
          613         if(t->file->ntext>1){
          614                 d = dirstat(name);
          615                 isdir = (d!=nil && (d->qid.type & QTDIR));
          616                 free(d);
          617                 if(isdir){
          618                         warning(nil, "%s is a directory; can't read with multiple windows on it\n", name);
          619                         return;
          620                 }
          621         }
          622         addr = emalloc((t->file->ntext)*sizeof(TextAddr));
          623         for(i=0; i<t->file->ntext; i++) {
          624                 a = &addr[i];
          625                 u = t->file->text[i];
          626                 a->lorigin = nlcount(u, 0, u->org, &a->rorigin);
          627                 a->lq0 = nlcount(u, 0, u->q0, &a->rq0);
          628                 a->lq1 = nlcount(u, u->q0, u->q1, &a->rq1);
          629         }
          630         r = bytetorune(name, &n);
          631         for(i=0; i<t->file->ntext; i++){
          632                 u = t->file->text[i];
          633                 /* second and subsequent calls with zero an already empty buffer, but OK */
          634                 textreset(u);
          635                 windirfree(u->w);
          636         }
          637         samename = runeeq(r, n, t->file->name, t->file->nname);
          638         textload(t, 0, name, samename);
          639         if(samename){
          640                 t->file->mod = FALSE;
          641                 dirty = FALSE;
          642         }else{
          643                 t->file->mod = TRUE;
          644                 dirty = TRUE;
          645         }
          646         for(i=0; i<t->file->ntext; i++)
          647                 t->file->text[i]->w->dirty = dirty;
          648         free(name);
          649         free(r);
          650         winsettag(w);
          651         t->file->unread = FALSE;
          652         for(i=0; i<t->file->ntext; i++){
          653                 u = t->file->text[i];
          654                 textsetselect(&u->w->tag, u->w->tag.file->b.nc, u->w->tag.file->b.nc);
          655                 if(samename) {
          656                         a = &addr[i];
          657                         // warning(nil, "%d %d %d %d %d %d\n", a->lorigin, a->rorigin, a->lq0, a->rq0, a->lq1, a->rq1);
          658                         q0 = nlcounttopos(u, 0, a->lq0, a->rq0);
          659                         q1 = nlcounttopos(u, q0, a->lq1, a->rq1);
          660                         textsetselect(u, q0, q1);
          661                         q0 = nlcounttopos(u, 0, a->lorigin, a->rorigin);
          662                         textsetorigin(u, q0, FALSE);
          663                 }
          664                 textscrdraw(u);
          665         }
          666         free(addr);
          667         xfidlog(w, "get");
          668 }
          669 
          670 static void
          671 checksha1(char *name, File *f, Dir *d)
          672 {
          673         int fd, n;
          674         DigestState *h;
          675         uchar out[20];
          676         uchar *buf;
          677 
          678         fd = open(name, OREAD);
          679         if(fd < 0)
          680                 return;
          681         h = sha1(nil, 0, nil, nil);
          682         buf = emalloc(8192);
          683         while((n = read(fd, buf, 8192)) > 0)
          684                 sha1(buf, n, nil, h);
          685         free(buf);
          686         close(fd);
          687         sha1(nil, 0, out, h);
          688         if(memcmp(out, f->sha1, sizeof out) == 0) {
          689                 f->dev = d->dev;
          690                 f->qidpath = d->qid.path;
          691                 f->mtime = d->mtime;
          692         }
          693 }
          694 
          695 void
          696 putfile(File *f, int q0, int q1, Rune *namer, int nname)
          697 {
          698         uint n, m;
          699         Rune *r;
          700         Biobuf *b;
          701         char *s, *name;
          702         int i, fd, q, ret, retc;
          703         Dir *d, *d1;
          704         Window *w;
          705         int isapp;
          706         DigestState *h;
          707 
          708         w = f->curtext->w;
          709         name = runetobyte(namer, nname);
          710         d = dirstat(name);
          711         if(d!=nil && runeeq(namer, nname, f->name, f->nname)){
          712                 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime != d->mtime)
          713                         checksha1(name, f, d);
          714                 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime != d->mtime) {
          715                         if(f->unread)
          716                                 warning(nil, "%s not written; file already exists\n", name);
          717                         else
          718                                 warning(nil, "%s modified%s%s since last read\n\twas %t; now %t\n", name, d->muid[0]?" by ":"", d->muid, f->mtime, d->mtime);
          719                         f->dev = d->dev;
          720                         f->qidpath = d->qid.path;
          721                         f->mtime = d->mtime;
          722                         goto Rescue1;
          723                 }
          724         }
          725 
          726         fd = create(name, OWRITE, 0666);
          727         if(fd < 0){
          728                 warning(nil, "can't create file %s: %r\n", name);
          729                 goto Rescue1;
          730         }
          731         // Use bio in order to force the writes to be large and
          732         // block-aligned (bio's default is 8K). This is not strictly
          733         // necessary; it works around some buggy underlying
          734         // file systems that mishandle unaligned writes.
          735         // https://codereview.appspot.com/89550043/
          736         b = emalloc(sizeof *b);
          737         Binit(b, fd, OWRITE);
          738         r = fbufalloc();
          739         s = fbufalloc();
          740         free(d);
          741         d = dirfstat(fd);
          742         h = sha1(nil, 0, nil, nil);
          743         isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND));
          744         if(isapp){
          745                 warning(nil, "%s not written; file is append only\n", name);
          746                 goto Rescue2;
          747         }
          748 
          749         for(q=q0; q<q1; q+=n){
          750                 n = q1 - q;
          751                 if(n > BUFSIZE/UTFmax)
          752                         n = BUFSIZE/UTFmax;
          753                 bufread(&f->b, q, r, n);
          754                 m = snprint(s, BUFSIZE+1, "%.*S", n, r);
          755                 sha1((uchar*)s, m, nil, h);
          756                 if(Bwrite(b, s, m) != m){
          757                         warning(nil, "can't write file %s: %r\n", name);
          758                         goto Rescue2;
          759                 }
          760         }
          761         if(Bflush(b) < 0) {
          762                 warning(nil, "can't write file %s: %r\n", name);
          763                 goto Rescue2;
          764         }
          765         ret = Bterm(b);
          766         retc = close(fd);
          767         free(b);
          768         b = nil;
          769         if(ret < 0 || retc < 0) {
          770                 warning(nil, "can't write file %s: %r\n", name);
          771                 goto Rescue2; // flush or close failed
          772         }
          773         if(runeeq(namer, nname, f->name, f->nname)){
          774                 if(q0!=0 || q1!=f->b.nc){
          775                         f->mod = TRUE;
          776                         w->dirty = TRUE;
          777                         f->unread = TRUE;
          778                 }else{
          779                         // In case the file is on NFS, reopen the fd
          780                         // before dirfstat to cause the attribute cache
          781                         // to be updated (otherwise the mtime in the
          782                         // dirfstat below will be stale and not match
          783                         // what NFS sees).  The file is already written,
          784                         // so this should be a no-op when not on NFS.
          785                         // Opening for OWRITE (but no truncation)
          786                         // in case we don't have read permission.
          787                         // (The create above worked, so we probably
          788                         // still have write permission.)
          789                         fd = open(name, OWRITE);
          790                         d1 = dirfstat(fd);
          791                         close(fd);
          792                         if(d1 != nil){
          793                                 free(d);
          794                                 d = d1;
          795                         }
          796                         f->qidpath = d->qid.path;
          797                         f->dev = d->dev;
          798                         f->mtime = d->mtime;
          799                         sha1(nil, 0, f->sha1, h);
          800                         h = nil;
          801                         f->mod = FALSE;
          802                         w->dirty = FALSE;
          803                         f->unread = FALSE;
          804                 }
          805                 for(i=0; i<f->ntext; i++){
          806                         f->text[i]->w->putseq = f->seq;
          807                         f->text[i]->w->dirty = w->dirty;
          808                 }
          809         }
          810         fbuffree(s);
          811         fbuffree(r);
          812         free(h);
          813         free(d);
          814         free(namer);
          815         free(name);
          816         close(fd);
          817         winsettag(w);
          818         return;
          819 
          820     Rescue2:
          821         if(b != nil) {
          822                 Bterm(b);
          823                 free(b);
          824                 close(fd);
          825         }
          826         free(h);
          827         fbuffree(s);
          828         fbuffree(r);
          829         /* fall through */
          830 
          831     Rescue1:
          832         free(d);
          833         free(namer);
          834         free(name);
          835 }
          836 
          837 static void
          838 trimspaces(Text *et)
          839 {
          840         File *f;
          841         Rune *r;
          842         Text *t;
          843         uint q0, n, delstart;
          844         int c, i, marked;
          845 
          846         t = &et->w->body;
          847         f = t->file;
          848         marked = 0;
          849 
          850         if(t->w!=nil && et->w!=t->w){
          851                 /* can this happen when t == &et->w->body? */
          852                 c = 'M';
          853                 if(et->w)
          854                         c = et->w->owner;
          855                 winlock(t->w, c);
          856         }
          857 
          858         r = fbufalloc();
          859         q0 = f->b.nc;
          860         delstart = q0; /* end of current space run, or 0 if no active run; = q0 to delete spaces before EOF */
          861         while(q0 > 0) {
          862                 n = RBUFSIZE;
          863                 if(n > q0)
          864                         n = q0;
          865                 q0 -= n;
          866                 bufread(&f->b, q0, r, n);
          867                 for(i=n; ; i--) {
          868                         if(i == 0 || (r[i-1] != ' ' && r[i-1] != '\t')) {
          869                                 // Found non-space or start of buffer. Delete active space run.
          870                                 if(q0+i < delstart) {
          871                                         if(!marked) {
          872                                                 marked = 1;
          873                                                 seq++;
          874                                                 filemark(f);
          875                                         }
          876                                         textdelete(t, q0+i, delstart, TRUE);
          877                                 }
          878                                 if(i == 0) {
          879                                         /* keep run active into tail of next buffer */
          880                                         if(delstart > 0)
          881                                                 delstart = q0;
          882                                         break;
          883                                 }
          884                                 delstart = 0;
          885                                 if(r[i-1] == '\n')
          886                                         delstart = q0+i-1; /* delete spaces before this newline */
          887                         }
          888                 }
          889         }
          890         fbuffree(r);
          891 
          892         if(t->w!=nil && et->w!=t->w)
          893                 winunlock(t->w);
          894 }
          895 
          896 void
          897 put(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
          898 {
          899         int nname;
          900         Rune  *namer;
          901         Window *w;
          902         File *f;
          903         char *name;
          904 
          905         USED(_0);
          906         USED(_1);
          907         USED(_2);
          908 
          909         if(et==nil || et->w==nil || et->w->isdir)
          910                 return;
          911         w = et->w;
          912         f = w->body.file;
          913         name = getname(&w->body, argt, arg, narg, TRUE);
          914         if(name == nil){
          915                 warning(nil, "no file name\n");
          916                 return;
          917         }
          918         if(w->autoindent)
          919                 trimspaces(et);
          920         namer = bytetorune(name, &nname);
          921         putfile(f, 0, f->b.nc, namer, nname);
          922         xfidlog(w, "put");
          923         free(name);
          924 }
          925 
          926 void
          927 dump(Text *_0, Text *_1, Text *argt, int isdump, int _2, Rune *arg, int narg)
          928 {
          929         char *name;
          930 
          931         USED(_0);
          932         USED(_1);
          933         USED(_2);
          934 
          935         if(narg)
          936                 name = runetobyte(arg, narg);
          937         else
          938                 getbytearg(argt, FALSE, TRUE, &name);
          939         if(isdump)
          940                 rowdump(&row, name);
          941         else
          942                 rowload(&row, name, FALSE);
          943         free(name);
          944 }
          945 
          946 void
          947 cut(Text *et, Text *t, Text *_0, int dosnarf, int docut, Rune *_2, int _3)
          948 {
          949         uint q0, q1, n, locked, c;
          950         Rune *r;
          951 
          952         USED(_0);
          953         USED(_2);
          954         USED(_3);
          955 
          956         /*
          957          * if not executing a mouse chord (et != t) and snarfing (dosnarf)
          958          * and executed Cut or Snarf in window tag (et->w != nil),
          959          * then use the window body selection or the tag selection
          960          * or do nothing at all.
          961          */
          962         if(et!=t && dosnarf && et->w!=nil){
          963                 if(et->w->body.q1>et->w->body.q0){
          964                         t = &et->w->body;
          965                         if(docut)
          966                                 filemark(t->file);        /* seq has been incremented by execute */
          967                 }else if(et->w->tag.q1>et->w->tag.q0)
          968                         t = &et->w->tag;
          969                 else
          970                         t = nil;
          971         }
          972         if(t == nil)        /* no selection */
          973                 return;
          974 
          975         locked = FALSE;
          976         if(t->w!=nil && et->w!=t->w){
          977                 locked = TRUE;
          978                 c = 'M';
          979                 if(et->w)
          980                         c = et->w->owner;
          981                 winlock(t->w, c);
          982         }
          983         if(t->q0 == t->q1){
          984                 if(locked)
          985                         winunlock(t->w);
          986                 return;
          987         }
          988         if(dosnarf){
          989                 q0 = t->q0;
          990                 q1 = t->q1;
          991                 bufdelete(&snarfbuf, 0, snarfbuf.nc);
          992                 r = fbufalloc();
          993                 while(q0 < q1){
          994                         n = q1 - q0;
          995                         if(n > RBUFSIZE)
          996                                 n = RBUFSIZE;
          997                         bufread(&t->file->b, q0, r, n);
          998                         bufinsert(&snarfbuf, snarfbuf.nc, r, n);
          999                         q0 += n;
         1000                 }
         1001                 fbuffree(r);
         1002                 acmeputsnarf();
         1003         }
         1004         if(docut){
         1005                 textdelete(t, t->q0, t->q1, TRUE);
         1006                 textsetselect(t, t->q0, t->q0);
         1007                 if(t->w){
         1008                         textscrdraw(t);
         1009                         winsettag(t->w);
         1010                 }
         1011         }else if(dosnarf)        /* Snarf command */
         1012                 argtext = t;
         1013         if(locked)
         1014                 winunlock(t->w);
         1015 }
         1016 
         1017 void
         1018 paste(Text *et, Text *t, Text *_0, int selectall, int tobody, Rune *_1, int _2)
         1019 {
         1020         int c;
         1021         uint q, q0, q1, n;
         1022         Rune *r;
         1023 
         1024         USED(_0);
         1025         USED(_1);
         1026         USED(_2);
         1027 
         1028         /* if(tobody), use body of executing window  (Paste or Send command) */
         1029         if(tobody && et!=nil && et->w!=nil){
         1030                 t = &et->w->body;
         1031                 filemark(t->file);        /* seq has been incremented by execute */
         1032         }
         1033         if(t == nil)
         1034                 return;
         1035 
         1036         acmegetsnarf();
         1037         if(t==nil || snarfbuf.nc==0)
         1038                 return;
         1039         if(t->w!=nil && et->w!=t->w){
         1040                 c = 'M';
         1041                 if(et->w)
         1042                         c = et->w->owner;
         1043                 winlock(t->w, c);
         1044         }
         1045         cut(t, t, nil, FALSE, TRUE, nil, 0);
         1046         q = 0;
         1047         q0 = t->q0;
         1048         q1 = t->q0+snarfbuf.nc;
         1049         r = fbufalloc();
         1050         while(q0 < q1){
         1051                 n = q1 - q0;
         1052                 if(n > RBUFSIZE)
         1053                         n = RBUFSIZE;
         1054                 if(r == nil)
         1055                         r = runemalloc(n);
         1056                 bufread(&snarfbuf, q, r, n);
         1057                 textinsert(t, q0, r, n, TRUE);
         1058                 q += n;
         1059                 q0 += n;
         1060         }
         1061         fbuffree(r);
         1062         if(selectall)
         1063                 textsetselect(t, t->q0, q1);
         1064         else
         1065                 textsetselect(t, q1, q1);
         1066         if(t->w){
         1067                 textscrdraw(t);
         1068                 winsettag(t->w);
         1069         }
         1070         if(t->w!=nil && et->w!=t->w)
         1071                 winunlock(t->w);
         1072 }
         1073 
         1074 void
         1075 look(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg)
         1076 {
         1077         Rune *r;
         1078         int n;
         1079 
         1080         USED(_0);
         1081         USED(_1);
         1082 
         1083         if(et && et->w){
         1084                 t = &et->w->body;
         1085                 if(narg > 0){
         1086                         search(t, arg, narg);
         1087                         return;
         1088                 }
         1089                 getarg(argt, FALSE, FALSE, &r, &n);
         1090                 if(r == nil){
         1091                         n = t->q1-t->q0;
         1092                         r = runemalloc(n);
         1093                         bufread(&t->file->b, t->q0, r, n);
         1094                 }
         1095                 search(t, r, n);
         1096                 free(r);
         1097         }
         1098 }
         1099 
         1100 static Rune Lnl[] = { '\n', 0 };
         1101 
         1102 void
         1103 sendx(Text *et, Text *t, Text *_0, int _1, int _2, Rune *_3, int _4)
         1104 {
         1105         USED(_0);
         1106         USED(_1);
         1107         USED(_2);
         1108         USED(_3);
         1109         USED(_4);
         1110 
         1111         if(et->w==nil)
         1112                 return;
         1113         t = &et->w->body;
         1114         if(t->q0 != t->q1)
         1115                 cut(t, t, nil, TRUE, FALSE, nil, 0);
         1116         textsetselect(t, t->file->b.nc, t->file->b.nc);
         1117         paste(t, t, nil, TRUE, TRUE, nil, 0);
         1118         if(textreadc(t, t->file->b.nc-1) != '\n'){
         1119                 textinsert(t, t->file->b.nc, Lnl, 1, TRUE);
         1120                 textsetselect(t, t->file->b.nc, t->file->b.nc);
         1121         }
         1122         t->iq1 = t->q1;
         1123         textshow(t, t->q1, t->q1, 1);
         1124 }
         1125 
         1126 void
         1127 edit(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
         1128 {
         1129         Rune *r;
         1130         int len;
         1131 
         1132         USED(_0);
         1133         USED(_1);
         1134         USED(_2);
         1135 
         1136         if(et == nil)
         1137                 return;
         1138         getarg(argt, FALSE, TRUE, &r, &len);
         1139         seq++;
         1140         if(r != nil){
         1141                 editcmd(et, r, len);
         1142                 free(r);
         1143         }else
         1144                 editcmd(et, arg, narg);
         1145 }
         1146 
         1147 void
         1148 xexit(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
         1149 {
         1150         USED(et);
         1151         USED(_0);
         1152         USED(_1);
         1153         USED(_2);
         1154         USED(_3);
         1155         USED(_4);
         1156         USED(_5);
         1157 
         1158         if(rowclean(&row)){
         1159                 sendul(cexit, 0);
         1160                 threadexits(nil);
         1161         }
         1162 }
         1163 
         1164 void
         1165 putall(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
         1166 {
         1167         int i, j, e;
         1168         Window *w;
         1169         Column *c;
         1170         char *a;
         1171 
         1172         USED(et);
         1173         USED(_0);
         1174         USED(_1);
         1175         USED(_2);
         1176         USED(_3);
         1177         USED(_4);
         1178         USED(_5);
         1179 
         1180         for(i=0; i<row.ncol; i++){
         1181                 c = row.col[i];
         1182                 for(j=0; j<c->nw; j++){
         1183                         w = c->w[j];
         1184                         if(w->isscratch || w->isdir || w->body.file->nname==0)
         1185                                 continue;
         1186                         if(w->nopen[QWevent] > 0)
         1187                                 continue;
         1188                         a = runetobyte(w->body.file->name, w->body.file->nname);
         1189                         e = access(a, 0);
         1190                         if(w->body.file->mod || w->body.ncache)
         1191                                 if(e < 0)
         1192                                         warning(nil, "no auto-Put of %s: %r\n", a);
         1193                                 else{
         1194                                         wincommit(w, &w->body);
         1195                                         put(&w->body, nil, nil, XXX, XXX, nil, 0);
         1196                                 }
         1197                         free(a);
         1198                 }
         1199         }
         1200 }
         1201 
         1202 
         1203 void
         1204 id(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
         1205 {
         1206         USED(_0);
         1207         USED(_1);
         1208         USED(_2);
         1209         USED(_3);
         1210         USED(_4);
         1211         USED(_5);
         1212 
         1213         if(et && et->w)
         1214                 warning(nil, "/mnt/acme/%d/\n", et->w->id);
         1215 }
         1216 
         1217 void
         1218 local(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
         1219 {
         1220         char *a, *aa;
         1221         Runestr dir;
         1222 
         1223         USED(_0);
         1224         USED(_1);
         1225         USED(_2);
         1226 
         1227         aa = getbytearg(argt, TRUE, TRUE, &a);
         1228 
         1229         dir = dirname(et, nil, 0);
         1230         if(dir.nr==1 && dir.r[0]=='.'){        /* sigh */
         1231                 free(dir.r);
         1232                 dir.r = nil;
         1233                 dir.nr = 0;
         1234         }
         1235         run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE);
         1236 }
         1237 
         1238 void
         1239 xkill(Text *_0, Text *_1, Text *argt, int _2, int _3, Rune *arg, int narg)
         1240 {
         1241         Rune *a, *cmd, *r;
         1242         int na;
         1243 
         1244         USED(_0);
         1245         USED(_1);
         1246         USED(_2);
         1247         USED(_3);
         1248 
         1249         getarg(argt, FALSE, FALSE, &r, &na);
         1250         if(r)
         1251                 xkill(nil, nil, nil, 0, 0, r, na);
         1252         /* loop condition: *arg is not a blank */
         1253         for(;;){
         1254                 a = findbl(arg, narg, &na);
         1255                 if(a == arg)
         1256                         break;
         1257                 cmd = runemalloc(narg-na+1);
         1258                 runemove(cmd, arg, narg-na);
         1259                 sendp(ckill, cmd);
         1260                 arg = skipbl(a, na, &narg);
         1261         }
         1262 }
         1263 
         1264 static Rune Lfix[] = { 'f', 'i', 'x', 0 };
         1265 static Rune Lvar[] = { 'v', 'a', 'r', 0 };
         1266 
         1267 void
         1268 fontx(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg)
         1269 {
         1270         Rune *a, *r, *flag, *file;
         1271         int na, nf;
         1272         char *aa;
         1273         Reffont *newfont;
         1274         Dirlist *dp;
         1275         int i, fix;
         1276 
         1277         USED(_0);
         1278         USED(_1);
         1279 
         1280         if(et==nil || et->w==nil)
         1281                 return;
         1282         t = &et->w->body;
         1283         flag = nil;
         1284         file = nil;
         1285         /* loop condition: *arg is not a blank */
         1286         nf = 0;
         1287         for(;;){
         1288                 a = findbl(arg, narg, &na);
         1289                 if(a == arg)
         1290                         break;
         1291                 r = runemalloc(narg-na+1);
         1292                 runemove(r, arg, narg-na);
         1293                 if(runeeq(r, narg-na, Lfix, 3) || runeeq(r, narg-na, Lvar, 3)){
         1294                         free(flag);
         1295                         flag = r;
         1296                 }else{
         1297                         free(file);
         1298                         file = r;
         1299                         nf = narg-na;
         1300                 }
         1301                 arg = skipbl(a, na, &narg);
         1302         }
         1303         getarg(argt, FALSE, TRUE, &r, &na);
         1304         if(r)
         1305                 if(runeeq(r, na, Lfix, 3) || runeeq(r, na, Lvar, 3)){
         1306                         free(flag);
         1307                         flag = r;
         1308                 }else{
         1309                         free(file);
         1310                         file = r;
         1311                         nf = na;
         1312                 }
         1313         fix = 1;
         1314         if(flag)
         1315                 fix = runeeq(flag, runestrlen(flag), Lfix, 3);
         1316         else if(file == nil){
         1317                 newfont = rfget(FALSE, FALSE, FALSE, nil);
         1318                 if(newfont)
         1319                         fix = strcmp(newfont->f->name, t->fr.font->name)==0;
         1320         }
         1321         if(file){
         1322                 aa = runetobyte(file, nf);
         1323                 newfont = rfget(fix, flag!=nil, FALSE, aa);
         1324                 free(aa);
         1325         }else
         1326                 newfont = rfget(fix, FALSE, FALSE, nil);
         1327         if(newfont){
         1328                 draw(screen, t->w->r, textcols[BACK], nil, ZP);
         1329                 rfclose(t->reffont);
         1330                 t->reffont = newfont;
         1331                 t->fr.font = newfont->f;
         1332                 frinittick(&t->fr);
         1333                 if(t->w->isdir){
         1334                         t->all.min.x++;        /* force recolumnation; disgusting! */
         1335                         for(i=0; i<t->w->ndl; i++){
         1336                                 dp = t->w->dlp[i];
         1337                                 aa = runetobyte(dp->r, dp->nr);
         1338                                 dp->wid = stringwidth(newfont->f, aa);
         1339                                 free(aa);
         1340                         }
         1341                 }
         1342                 /* avoid shrinking of window due to quantization */
         1343                 colgrow(t->w->col, t->w, -1);
         1344         }
         1345         free(file);
         1346         free(flag);
         1347 }
         1348 
         1349 void
         1350 incl(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
         1351 {
         1352         Rune *a, *r;
         1353         Window *w;
         1354         int na, n, len;
         1355 
         1356         USED(_0);
         1357         USED(_1);
         1358         USED(_2);
         1359 
         1360         if(et==nil || et->w==nil)
         1361                 return;
         1362         w = et->w;
         1363         n = 0;
         1364         getarg(argt, FALSE, TRUE, &r, &len);
         1365         if(r){
         1366                 n++;
         1367                 winaddincl(w, r, len);
         1368         }
         1369         /* loop condition: *arg is not a blank */
         1370         for(;;){
         1371                 a = findbl(arg, narg, &na);
         1372                 if(a == arg)
         1373                         break;
         1374                 r = runemalloc(narg-na+1);
         1375                 runemove(r, arg, narg-na);
         1376                 n++;
         1377                 winaddincl(w, r, narg-na);
         1378                 arg = skipbl(a, na, &narg);
         1379         }
         1380         if(n==0 && w->nincl){
         1381                 for(n=w->nincl; --n>=0; )
         1382                         warning(nil, "%S ", w->incl[n]);
         1383                 warning(nil, "\n");
         1384         }
         1385 }
         1386 
         1387 static Rune LON[] = { 'O', 'N', 0 };
         1388 static Rune LOFF[] = { 'O', 'F', 'F', 0 };
         1389 static Rune Lon[] = { 'o', 'n', 0 };
         1390 
         1391 enum {
         1392         IGlobal = -2,
         1393         IError = -1,
         1394         Ion = 0,
         1395         Ioff = 1
         1396 };
         1397 
         1398 static int
         1399 indentval(Rune *s, int n)
         1400 {
         1401         if(n < 2)
         1402                 return IError;
         1403         if(runestrncmp(s, LON, n) == 0){
         1404                 globalautoindent = TRUE;
         1405                 warning(nil, "Indent ON\n");
         1406                 return IGlobal;
         1407         }
         1408         if(runestrncmp(s, LOFF, n) == 0){
         1409                 globalautoindent = FALSE;
         1410                 warning(nil, "Indent OFF\n");
         1411                 return IGlobal;
         1412         }
         1413         return runestrncmp(s, Lon, n) == 0;
         1414 }
         1415 
         1416 static void
         1417 fixindent(Window *w, void *arg)
         1418 {
         1419         USED(arg);
         1420         w->autoindent = globalautoindent;
         1421 }
         1422 
         1423 void
         1424 indent(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
         1425 {
         1426         Rune *a, *r;
         1427         Window *w;
         1428         int na, len, autoindent;
         1429 
         1430         USED(_0);
         1431         USED(_1);
         1432         USED(_2);
         1433 
         1434         w = nil;
         1435         if(et!=nil && et->w!=nil)
         1436                 w = et->w;
         1437         autoindent = IError;
         1438         getarg(argt, FALSE, TRUE, &r, &len);
         1439         if(r!=nil && len>0)
         1440                 autoindent = indentval(r, len);
         1441         else{
         1442                 a = findbl(arg, narg, &na);
         1443                 if(a != arg)
         1444                         autoindent = indentval(arg, narg-na);
         1445         }
         1446         if(autoindent == IGlobal)
         1447                 allwindows(fixindent, nil);
         1448         else if(w != nil && autoindent >= 0)
         1449                 w->autoindent = autoindent;
         1450 }
         1451 
         1452 void
         1453 tab(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
         1454 {
         1455         Rune *a, *r;
         1456         Window *w;
         1457         int na, len, tab;
         1458         char *p;
         1459 
         1460         USED(_0);
         1461         USED(_1);
         1462         USED(_2);
         1463 
         1464         if(et==nil || et->w==nil)
         1465                 return;
         1466         w = et->w;
         1467         getarg(argt, FALSE, TRUE, &r, &len);
         1468         tab = 0;
         1469         if(r!=nil && len>0){
         1470                 p = runetobyte(r, len);
         1471                 if('0'<=p[0] && p[0]<='9')
         1472                         tab = atoi(p);
         1473                 free(p);
         1474         }else{
         1475                 a = findbl(arg, narg, &na);
         1476                 if(a != arg){
         1477                         p = runetobyte(arg, narg-na);
         1478                         if('0'<=p[0] && p[0]<='9')
         1479                                 tab = atoi(p);
         1480                         free(p);
         1481                 }
         1482         }
         1483         if(tab > 0){
         1484                 if(w->body.tabstop != tab){
         1485                         w->body.tabstop = tab;
         1486                         winresize(w, w->r, FALSE, TRUE);
         1487                 }
         1488         }else
         1489                 warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop);
         1490 }
         1491 
         1492 void
         1493 runproc(void *argvp)
         1494 {
         1495         /* args: */
         1496                 Window *win;
         1497                 char *s;
         1498                 Rune *rdir;
         1499                 int ndir;
         1500                 int newns;
         1501                 char *argaddr;
         1502                 char *arg;
         1503                 Command *c;
         1504                 Channel *cpid;
         1505                 int iseditcmd;
         1506         /* end of args */
         1507         char *e, *t, *name, *filename, *dir, **av, *news;
         1508         Rune r, **incl;
         1509         int ac, w, inarg, i, n, fd, nincl, winid;
         1510         int sfd[3];
         1511         int pipechar;
         1512         char buf[512];
         1513         int ret;
         1514         /*static void *parg[2]; */
         1515         char *rcarg[4];
         1516         void **argv;
         1517         CFsys *fs;
         1518         char *shell;
         1519 
         1520         threadsetname("runproc");
         1521 
         1522         argv = argvp;
         1523         win = argv[0];
         1524         s = argv[1];
         1525         rdir = argv[2];
         1526         ndir = (uintptr)argv[3];
         1527         newns = (uintptr)argv[4];
         1528         argaddr = argv[5];
         1529         arg = argv[6];
         1530         c = argv[7];
         1531         cpid = argv[8];
         1532         iseditcmd = (uintptr)argv[9];
         1533         free(argv);
         1534 
         1535         t = s;
         1536         while(*t==' ' || *t=='\n' || *t=='\t')
         1537                 t++;
         1538         for(e=t; *e; e++)
         1539                 if(*e==' ' || *e=='\n' || *e=='\t' )
         1540                         break;
         1541         name = emalloc((e-t)+2);
         1542         memmove(name, t, e-t);
         1543         name[e-t] = 0;
         1544         e = utfrrune(name, '/');
         1545         if(e)
         1546                 memmove(name, e+1, strlen(e+1)+1);        /* strcpy but overlaps */
         1547         strcat(name, " ");        /* add blank here for ease in waittask */
         1548         c->name = bytetorune(name, &c->nname);
         1549         free(name);
         1550         pipechar = 0;
         1551         if(*t=='<' || *t=='|' || *t=='>')
         1552                 pipechar = *t++;
         1553         c->iseditcmd = iseditcmd;
         1554         c->text = s;
         1555         if(newns){
         1556                 nincl = 0;
         1557                 incl = nil;
         1558                 if(win){
         1559                         filename = smprint("%.*S", win->body.file->nname, win->body.file->name);
         1560                         nincl = win->nincl;
         1561                         if(nincl > 0){
         1562                                 incl = emalloc(nincl*sizeof(Rune*));
         1563                                 for(i=0; i<nincl; i++){
         1564                                         n = runestrlen(win->incl[i]);
         1565                                         incl[i] = runemalloc(n+1);
         1566                                         runemove(incl[i], win->incl[i], n);
         1567                                 }
         1568                         }
         1569                         winid = win->id;
         1570                 }else{
         1571                         filename = nil;
         1572                         winid = 0;
         1573                         if(activewin)
         1574                                 winid = activewin->id;
         1575                 }
         1576                 rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG);
         1577                 sprint(buf, "%d", winid);
         1578                 putenv("winid", buf);
         1579 
         1580                 if(filename){
         1581                         putenv("%", filename);
         1582                         putenv("samfile", filename);
         1583                         free(filename);
         1584                 }
         1585                 c->md = fsysmount(rdir, ndir, incl, nincl);
         1586                 if(c->md == nil){
         1587                         fprint(2, "child: can't allocate mntdir: %r\n");
         1588                         threadexits("fsysmount");
         1589                 }
         1590                 sprint(buf, "%d", c->md->id);
         1591                 if((fs = nsmount("acme", buf)) == nil){
         1592                         fprint(2, "child: can't mount acme: %r\n");
         1593                         fsysdelid(c->md);
         1594                         c->md = nil;
         1595                         threadexits("nsmount");
         1596                 }
         1597                 if(winid>0 && (pipechar=='|' || pipechar=='>')){
         1598                         sprint(buf, "%d/rdsel", winid);
         1599                         sfd[0] = fsopenfd(fs, buf, OREAD);
         1600                 }else
         1601                         sfd[0] = open("/dev/null", OREAD);
         1602                 if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){
         1603                         if(iseditcmd){
         1604                                 if(winid > 0)
         1605                                         sprint(buf, "%d/editout", winid);
         1606                                 else
         1607                                         sprint(buf, "editout");
         1608                         }else
         1609                                 sprint(buf, "%d/wrsel", winid);
         1610                         sfd[1] = fsopenfd(fs, buf, OWRITE);
         1611                         sfd[2] = fsopenfd(fs, "cons", OWRITE);
         1612                 }else{
         1613                         sfd[1] = fsopenfd(fs, "cons", OWRITE);
         1614                         sfd[2] = sfd[1];
         1615                 }
         1616                 fsunmount(fs);
         1617         }else{
         1618                 rfork(RFFDG|RFNOTEG);
         1619                 fsysclose();
         1620                 sfd[0] = open("/dev/null", OREAD);
         1621                 sfd[1] = open("/dev/null", OWRITE);
         1622                 sfd[2] = dup(erroutfd, -1);
         1623         }
         1624         if(win)
         1625                 winclose(win);
         1626 
         1627         if(argaddr)
         1628                 putenv("acmeaddr", argaddr);
         1629         if(acmeshell != nil)
         1630                 goto Hard;
         1631         if(strlen(t) > sizeof buf-10)        /* may need to print into stack */
         1632                 goto Hard;
         1633         inarg = FALSE;
         1634         for(e=t; *e; e+=w){
         1635                 w = chartorune(&r, e);
         1636                 if(r==' ' || r=='\t')
         1637                         continue;
         1638                 if(r < ' ')
         1639                         goto Hard;
         1640                 if(utfrune("#;&|^$=`'{}()<>[]*?^~`/", r))
         1641                         goto Hard;
         1642                 inarg = TRUE;
         1643         }
         1644         if(!inarg)
         1645                 goto Fail;
         1646 
         1647         ac = 0;
         1648         av = nil;
         1649         inarg = FALSE;
         1650         for(e=t; *e; e+=w){
         1651                 w = chartorune(&r, e);
         1652                 if(r==' ' || r=='\t'){
         1653                         inarg = FALSE;
         1654                         *e = 0;
         1655                         continue;
         1656                 }
         1657                 if(!inarg){
         1658                         inarg = TRUE;
         1659                         av = realloc(av, (ac+1)*sizeof(char**));
         1660                         av[ac++] = e;
         1661                 }
         1662         }
         1663         av = realloc(av, (ac+2)*sizeof(char**));
         1664         av[ac++] = arg;
         1665         av[ac] = nil;
         1666         c->av = av;
         1667 
         1668         dir = nil;
         1669         if(rdir != nil)
         1670                 dir = runetobyte(rdir, ndir);
         1671         ret = threadspawnd(sfd, av[0], av, dir);
         1672         free(dir);
         1673         if(ret >= 0){
         1674                 if(cpid)
         1675                         sendul(cpid, ret);
         1676                 threadexits("");
         1677         }
         1678 /* libthread uses execvp so no need to do this */
         1679 #if 0
         1680         e = av[0];
         1681         if(e[0]=='/' || (e[0]=='.' && e[1]=='/'))
         1682                 goto Fail;
         1683         if(cputype){
         1684                 sprint(buf, "%s/%s", cputype, av[0]);
         1685                 procexec(cpid, sfd, buf, av);
         1686         }
         1687         sprint(buf, "/bin/%s", av[0]);
         1688         procexec(cpid, sfd, buf, av);
         1689 #endif
         1690         goto Fail;
         1691 
         1692 Hard:
         1693         /*
         1694          * ugly: set path = (. $cputype /bin)
         1695          * should honor $path if unusual.
         1696          */
         1697         if(cputype){
         1698                 n = 0;
         1699                 memmove(buf+n, ".", 2);
         1700                 n += 2;
         1701                 i = strlen(cputype)+1;
         1702                 memmove(buf+n, cputype, i);
         1703                 n += i;
         1704                 memmove(buf+n, "/bin", 5);
         1705                 n += 5;
         1706                 fd = create("/env/path", OWRITE, 0666);
         1707                 write(fd, buf, n);
         1708                 close(fd);
         1709         }
         1710 
         1711         if(arg){
         1712                 news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1);
         1713                 if(news){
         1714                         sprint(news, "%s '%s'", t, arg);        /* BUG: what if quote in arg? */
         1715                         free(s);
         1716                         t = news;
         1717                         c->text = news;
         1718                 }
         1719         }
         1720         dir = nil;
         1721         if(rdir != nil)
         1722                 dir = runetobyte(rdir, ndir);
         1723         shell = acmeshell;
         1724         if(shell == nil)
         1725                 shell = "rc";
         1726         rcarg[0] = shell;
         1727         rcarg[1] = "-c";
         1728         rcarg[2] = t;
         1729         rcarg[3] = nil;
         1730         ret = threadspawnd(sfd, rcarg[0], rcarg, dir);
         1731         free(dir);
         1732         if(ret >= 0){
         1733                 if(cpid)
         1734                         sendul(cpid, ret);
         1735                 threadexits(nil);
         1736         }
         1737         warning(nil, "exec %s: %r\n", shell);
         1738 
         1739    Fail:
         1740         /* threadexec hasn't happened, so send a zero */
         1741         close(sfd[0]);
         1742         close(sfd[1]);
         1743         if(sfd[2] != sfd[1])
         1744                 close(sfd[2]);
         1745         sendul(cpid, 0);
         1746         threadexits(nil);
         1747 }
         1748 
         1749 void
         1750 runwaittask(void *v)
         1751 {
         1752         Command *c;
         1753         Channel *cpid;
         1754         void **a;
         1755 
         1756         threadsetname("runwaittask");
         1757         a = v;
         1758         c = a[0];
         1759         cpid = a[1];
         1760         free(a);
         1761         do
         1762                 c->pid = recvul(cpid);
         1763         while(c->pid == ~0);
         1764         free(c->av);
         1765         if(c->pid != 0)        /* successful exec */
         1766                 sendp(ccommand, c);
         1767         else{
         1768                 if(c->iseditcmd)
         1769                         sendul(cedit, 0);
         1770                 free(c->name);
         1771                 free(c->text);
         1772                 free(c);
         1773         }
         1774         chanfree(cpid);
         1775 }
         1776 
         1777 void
         1778 run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd)
         1779 {
         1780         void **arg;
         1781         Command *c;
         1782         Channel *cpid;
         1783 
         1784         if(s == nil)
         1785                 return;
         1786 
         1787         arg = emalloc(10*sizeof(void*));
         1788         c = emalloc(sizeof *c);
         1789         cpid = chancreate(sizeof(ulong), 0);
         1790         chansetname(cpid, "cpid %s", s);
         1791         arg[0] = win;
         1792         arg[1] = s;
         1793         arg[2] = rdir;
         1794         arg[3] = (void*)(uintptr)ndir;
         1795         arg[4] = (void*)(uintptr)newns;
         1796         arg[5] = argaddr;
         1797         arg[6] = xarg;
         1798         arg[7] = c;
         1799         arg[8] = cpid;
         1800         arg[9] = (void*)(uintptr)iseditcmd;
         1801         threadcreate(runproc, arg, STACK);
         1802         /* mustn't block here because must be ready to answer mount() call in run() */
         1803         arg = emalloc(2*sizeof(void*));
         1804         arg[0] = c;
         1805         arg[1] = cpid;
         1806         threadcreate(runwaittask, arg, STACK);
         1807 }