]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/acme/exec.c
rio: fix goodrect() bug (thanks mike)
[plan9front.git] / sys / src / cmd / acme / exec.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include "dat.h"
12 #include "fns.h"
13
14 Buffer  snarfbuf;
15
16 /*
17  * These functions get called as:
18  *
19  *      fn(et, t, argt, flag1, flag1, flag2, s, n);
20  *
21  * Where the arguments are:
22  *
23  *      et: the Text* in which the executing event (click) occurred
24  *      t: the Text* containing the current selection (Edit, Cut, Snarf, Paste)
25  *      argt: the Text* containing the argument for a 2-1 click.
26  *      e->flag1: from Exectab entry
27  *      e->flag2: from Exectab entry
28  *      s: the command line remainder (e.g., "x" if executing "Dump x")
29  *      n: length of s  (s is *not* NUL-terminated)
30  */
31
32 void    del(Text*, Text*, Text*, int, int, Rune*, int);
33 void    delcol(Text*, Text*, Text*, int, int, Rune*, int);
34 void    dump(Text*, Text*, Text*, int, int, Rune*, int);
35 void    edit(Text*, Text*, Text*, int, int, Rune*, int);
36 void    exit(Text*, Text*, Text*, int, int, Rune*, int);
37 void    fontx(Text*, Text*, Text*, int, int, Rune*, int);
38 void    get(Text*, Text*, Text*, int, int, Rune*, int);
39 void    id(Text*, Text*, Text*, int, int, Rune*, int);
40 void    incl(Text*, Text*, Text*, int, int, Rune*, int);
41 void    indent(Text*, Text*, Text*, int, int, Rune*, int);
42 void    kill(Text*, Text*, Text*, int, int, Rune*, int);
43 void    local(Text*, Text*, Text*, int, int, Rune*, int);
44 void    look(Text*, Text*, Text*, int, int, Rune*, int);
45 void    newcol(Text*, Text*, Text*, int, int, Rune*, int);
46 void    paste(Text*, Text*, Text*, int, int, Rune*, int);
47 void    put(Text*, Text*, Text*, int, int, Rune*, int);
48 void    putall(Text*, Text*, Text*, int, int, Rune*, int);
49 void    sendx(Text*, Text*, Text*, int, int, Rune*, int);
50 void    sort(Text*, Text*, Text*, int, int, Rune*, int);
51 void    tab(Text*, Text*, Text*, int, int, Rune*, int);
52 void    zeroxx(Text*, Text*, Text*, int, int, Rune*, int);
53
54 typedef struct Exectab Exectab;
55 struct Exectab
56 {
57         Rune    *name;
58         void    (*fn)(Text*, Text*, Text*, int, int, Rune*, int);
59         int             mark;
60         int             flag1;
61         int             flag2;
62 };
63
64 Exectab exectab[] = {
65         { L"Cut",               cut,            TRUE,   TRUE,   TRUE    },
66         { L"Del",               del,            FALSE,  FALSE,  XXX             },
67         { L"Delcol",    delcol, FALSE,  XXX,            XXX             },
68         { L"Delete",    del,            FALSE,  TRUE,   XXX             },
69         { L"Dump",      dump,   FALSE,  TRUE,   XXX             },
70         { L"Edit",              edit,           FALSE,  XXX,            XXX             },
71         { L"Exit",              exit,           FALSE,  XXX,            XXX             },
72         { L"Font",              fontx,  FALSE,  XXX,            XXX             },
73         { L"Get",               get,            FALSE,  TRUE,   XXX             },
74         { L"ID",                id,             FALSE,  XXX,            XXX             },
75         { L"Incl",              incl,           FALSE,  XXX,            XXX             },
76         { L"Indent",    indent,         FALSE,  AUTOINDENT,             XXX             },
77         { L"Kill",              kill,           FALSE,  XXX,            XXX             },
78         { L"Load",              dump,   FALSE,  FALSE,  XXX             },
79         { L"Local",             local,  FALSE,  XXX,            XXX             },
80         { L"Look",              look,           FALSE,  XXX,            XXX             },
81         { L"New",               new,            FALSE,  XXX,            XXX             },
82         { L"Newcol",    newcol, FALSE,  XXX,            XXX             },
83         { L"Paste",             paste,  TRUE,   TRUE,   XXX             },
84         { L"Put",               put,            FALSE,  XXX,            XXX             },
85         { L"Putall",            putall, FALSE,  XXX,            XXX             },
86         { L"Redo",              undo,   FALSE,  FALSE,  XXX             },
87         { L"Send",              sendx,  TRUE,   XXX,            XXX             },
88         { L"Snarf",             cut,            FALSE,  TRUE,   FALSE   },
89         { L"Sort",              sort,           FALSE,  XXX,            XXX             },
90         { L"Spaces",    indent,         FALSE,  SPACESINDENT,   XXX             },
91         { L"Tab",               tab,            FALSE,  XXX,            XXX             },
92         { L"Undo",              undo,   FALSE,  TRUE,   XXX             },
93         { L"Zerox",     zeroxx, FALSE,  XXX,            XXX             },
94         { nil,                  nil,            0,              0,              0               },
95 };
96
97 Exectab*
98 lookup(Rune *r, int n)
99 {
100         Exectab *e;
101         int nr;
102
103         r = skipbl(r, n, &n);
104         if(n == 0)
105                 return nil;
106         findbl(r, n, &nr);
107         nr = n-nr;
108         for(e=exectab; e->name; e++)
109                 if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE)
110                         return e;
111         return nil;
112 }
113
114 int
115 isexecc(int c)
116 {
117         if(isfilec(c))
118                 return 1;
119         return c=='<' || c=='|' || c=='>';
120 }
121
122 void
123 execute(Text *t, uint aq0, uint aq1, int external, Text *argt)
124 {
125         uint q0, q1;
126         Rune *r, *s;
127         char *b, *a, *aa;
128         Exectab *e;
129         int c, n, f;
130         Runestr dir;
131
132         q0 = aq0;
133         q1 = aq1;
134         if(q1 == q0){   /* expand to find word (actually file name) */
135                 /* if in selection, choose selection */
136                 if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
137                         q0 = t->q0;
138                         q1 = t->q1;
139                 }else{
140                         while(q1<t->file->nc && isexecc(c=textreadc(t, q1)) && c!=':')
141                                 q1++;
142                         while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':')
143                                 q0--;
144                         if(q1 == q0)
145                                 return;
146                 }
147         }
148         r = runemalloc(q1-q0);
149         bufread(t->file, q0, r, q1-q0);
150         free(delcmd);
151         delcmd = runesmprint("%.*S", q1-q0, r);
152         e = lookup(r, q1-q0);
153         if(!external && t->w!=nil && t->w->nopen[QWevent]>0){
154                 f = 0;
155                 if(e)
156                         f |= 1;
157                 if(q0!=aq0 || q1!=aq1){
158                         bufread(t->file, aq0, r, aq1-aq0);
159                         f |= 2;
160                 }
161                 aa = getbytearg(argt, TRUE, TRUE, &a);
162                 if(a){  
163                         if(strlen(a) > EVENTSIZE){      /* too big; too bad */
164                                 free(r);
165                                 free(aa);
166                                 free(a);
167                                 warning(nil, "`argument string too long\n");
168                                 return;
169                         }
170                         f |= 8;
171                 }
172                 c = 'x';
173                 if(t->what == Body)
174                         c = 'X';
175                 n = aq1-aq0;
176                 if(n <= EVENTSIZE)
177                         winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r);
178                 else
179                         winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f, n);
180                 if(q0!=aq0 || q1!=aq1){
181                         n = q1-q0;
182                         bufread(t->file, q0, r, n);
183                         if(n <= EVENTSIZE)
184                                 winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r);
185                         else
186                                 winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1, n);
187                 }
188                 if(a){
189                         winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a);
190                         if(aa)
191                                 winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa);
192                         else
193                                 winevent(t->w, "%c0 0 0 0 \n", c);
194                 }
195                 free(r);
196                 free(aa);
197                 free(a);
198                 return;
199         }
200         if(e){
201                 if(e->mark && seltext!=nil)
202                 if(seltext->what == Body){
203                         seq++;
204                         filemark(seltext->w->body.file);
205                 }
206                 s = skipbl(r, q1-q0, &n);
207                 s = findbl(s, n, &n);
208                 s = skipbl(s, n, &n);
209                 (*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n);
210                 free(r);
211                 return;
212         }
213
214         b = runetobyte(r, q1-q0);
215         free(r);
216         dir = dirname(t, nil, 0);
217         if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
218                 free(dir.r);
219                 dir.r = nil;
220                 dir.nr = 0;
221         }
222         aa = getbytearg(argt, TRUE, TRUE, &a);
223         if(t->w)
224                 incref(t->w);
225         run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE);
226 }
227
228 char*
229 printarg(Text *argt, uint q0, uint q1)
230 {
231         char *buf;
232
233         if(argt->what!=Body || argt->file->name==nil)
234                 return nil;
235         buf = emalloc(argt->file->nname+32);
236         if(q0 == q1)
237                 sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0);
238         else
239                 sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1);
240         return buf;
241 }
242
243 char*
244 getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp)
245 {
246         int n;
247         Expand e;
248         char *a;
249
250         *rp = nil;
251         *nrp = 0;
252         if(argt == nil)
253                 return nil;
254         a = nil;
255         textcommit(argt, TRUE);
256         if(expand(argt, argt->q0, argt->q1, &e)){
257                 free(e.bname);
258                 if(e.nname && dofile){
259                         e.name = runerealloc(e.name, e.nname+1);
260                         if(doaddr)
261                                 a = printarg(argt, e.q0, e.q1);
262                         *rp = e.name;
263                         *nrp = e.nname;
264                         return a;
265                 }
266                 free(e.name);
267         }else{
268                 e.q0 = argt->q0;
269                 e.q1 = argt->q1;
270         }
271         n = e.q1 - e.q0;
272         *rp = runemalloc(n+1);
273         bufread(argt->file, e.q0, *rp, n);
274         if(doaddr)
275                 a = printarg(argt, e.q0, e.q1);
276         *nrp = n;
277         return a;
278 }
279
280 char*
281 getbytearg(Text *argt, int doaddr, int dofile, char **bp)
282 {
283         Rune *r;
284         int n;
285         char *aa;
286
287         *bp = nil;
288         aa = getarg(argt, doaddr, dofile, &r, &n);
289         if(r == nil)
290                 return nil;
291         *bp = runetobyte(r, n);
292         free(r);
293         return aa;
294 }
295
296 void
297 newcol(Text *et, Text*, Text*, int, int, Rune*, int)
298 {
299         Column *c;
300
301         c = rowadd(et->row, nil, -1);
302         if(c)
303                 winsettag(coladd(c, nil, nil, -1));
304 }
305
306 void
307 delcol(Text *et, Text*, Text*, int, int, Rune*, int)
308 {
309         int i;
310         Column *c;
311         Window *w;
312
313         c = et->col;
314         if(c==nil || colclean(c)==0)
315                 return;
316         for(i=0; i<c->nw; i++){
317                 w = c->w[i];
318                 if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata]+w->nopen[QWxdata] > 0){
319                         warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name);
320                         return;
321                 }
322         }
323         rowclose(et->col->row, et->col, TRUE);
324 }
325
326 void
327 del(Text *et, Text*, Text *argt, int flag1, int, Rune *arg, int narg)
328 {
329         Window *w;
330         char *name, *p;
331         Plumbmsg *pm;
332
333         if(et->col==nil || et->w == nil)
334                 return;
335         if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE)){
336                 w = et->w;
337                 name = getname(&w->body, argt, arg, narg, TRUE);
338                 if(name && plumbsendfd >= 0){
339                         pm = emalloc(sizeof(Plumbmsg));
340                         pm->src = estrdup("acme");
341                         pm->dst = estrdup("close");
342                         pm->wdir = estrdup(name);
343                         if(p = strrchr(pm->wdir, '/'))
344                                 *p = '\0';
345                         pm->type = estrdup("text");
346                         pm->attr = nil;
347                         pm->data = estrdup(name);
348                         pm->ndata = strlen(pm->data);
349                         if(pm->ndata < messagesize-1024)
350                                 plumbsend(plumbsendfd, pm);
351                         else
352                                 plumbfree(pm);
353                 }
354                 colclose(et->col, et->w, TRUE);
355         }
356 }
357
358 void
359 sort(Text *et, Text*, Text*, int, int, Rune*, int)
360 {
361         if(et->col)
362                 colsort(et->col);
363 }
364
365 uint
366 seqof(Window *w, int isundo)
367 {
368         /* if it's undo, see who changed with us */
369         if(isundo)
370                 return w->body.file->seq;
371         /* if it's redo, see who we'll be sync'ed up with */
372         return fileredoseq(w->body.file);
373 }
374
375 void
376 undo(Text *et, Text*, Text*, int flag1, int, Rune*, int)
377 {
378         int i, j;
379         Column *c;
380         Window *w;
381         uint seq;
382
383         if(et==nil || et->w== nil)
384                 return;
385         seq = seqof(et->w, flag1);
386         if(seq == 0){
387                 /* nothing to undo */
388                 return;
389         }
390         /*
391          * Undo the executing window first. Its display will update. other windows
392          * in the same file will not call show() and jump to a different location in the file.
393          * Simultaneous changes to other files will be chaotic, however.
394          */
395         winundo(et->w, flag1);
396         for(i=0; i<row.ncol; i++){
397                 c = row.col[i];
398                 for(j=0; j<c->nw; j++){
399                         w = c->w[j];
400                         if(w == et->w)
401                                 continue;
402                         if(seqof(w, flag1) == seq)
403                                 winundo(w, flag1);
404                 }
405         }
406 }
407
408 char*
409 getname(Text *t, Text *argt, Rune *arg, int narg, int isput)
410 {
411         char *s;
412         Rune *r;
413         int i, n, promote;
414         Runestr dir;
415
416         getarg(argt, FALSE, TRUE, &r, &n);
417         promote = FALSE;
418         if(r == nil)
419                 promote = TRUE;
420         else if(isput){
421                 /* if are doing a Put, want to synthesize name even for non-existent file */
422                 /* best guess is that file name doesn't contain a slash */
423                 promote = TRUE;
424                 for(i=0; i<n; i++)
425                         if(r[i] == '/'){
426                                 promote = FALSE;
427                                 break;
428                         }
429                 if(promote){
430                         t = argt;
431                         arg = r;
432                         narg = n;
433                 }
434         }
435         if(promote){
436                 n = narg;
437                 if(n <= 0){
438                         s = runetobyte(t->file->name, t->file->nname);
439                         return s;
440                 }
441                 /* prefix with directory name if necessary */
442                 dir.r = nil;
443                 dir.nr = 0;
444                 if(n>0 && arg[0]!='/'){
445                         dir = dirname(t, nil, 0);
446                         if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
447                                 free(dir.r);
448                                 dir.r = nil;
449                                 dir.nr = 0;
450                         }
451                 }
452                 if(dir.r){
453                         r = runemalloc(dir.nr+n+1);
454                         runemove(r, dir.r, dir.nr);
455                         free(dir.r);
456                         if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/')
457                                 r[dir.nr++] = '/';
458                         runemove(r+dir.nr, arg, n);
459                         n += dir.nr;
460                 }else{
461                         r = runemalloc(n+1);
462                         runemove(r, arg, n);
463                 }
464         }
465         s = runetobyte(r, n);
466         free(r);
467         if(strlen(s) == 0){
468                 free(s);
469                 s = nil;
470         }
471         return s;
472 }
473
474 void
475 zeroxx(Text *et, Text *t, Text*, int, int, Rune*, int)
476 {
477         Window *nw;
478         int c, locked;
479
480         locked = FALSE;
481         if(t!=nil && t->w!=nil && t->w!=et->w){
482                 locked = TRUE;
483                 c = 'M';
484                 if(et->w)
485                         c = et->w->owner;
486                 winlock(t->w, c);
487         }
488         if(t == nil)
489                 t = et;
490         if(t==nil || t->w==nil)
491                 return;
492         t = &t->w->body;
493         if(t->w->isdir)
494                 warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name);
495         else{
496                 nw = coladd(t->w->col, nil, t->w, -1);
497                 /* ugly: fix locks so w->unlock works */
498                 winlock1(nw, t->w->owner);
499         }
500         if(locked)
501                 winunlock(t->w);
502 }
503
504 void
505 get(Text *et, Text *t, Text *argt, int flag1, int, Rune *arg, int narg)
506 {
507         char *name;
508         Rune *r;
509         int i, n, dirty, samename, isdir;
510         Window *w;
511         Text *u;
512         Dir *d;
513
514         if(flag1)
515                 if(et==nil || et->w==nil)
516                         return;
517         if(!et->w->isdir && (et->w->body.file->nc>0 && !winclean(et->w, TRUE)))
518                 return;
519         w = et->w;
520         t = &w->body;
521         name = getname(t, argt, arg, narg, FALSE);
522         if(name == nil){
523                 warning(nil, "no file name\n");
524                 return;
525         }
526         if(t->file->ntext>1){
527                 d = dirstat(name);
528                 isdir = (d!=nil && (d->qid.type & QTDIR));
529                 free(d);
530                 if(isdir){
531                         warning(nil, "%s is a directory; can't read with multiple windows on it\n", name);
532                         return;
533                 }
534         }
535         r = bytetorune(name, &n);
536         for(i=0; i<t->file->ntext; i++){
537                 u = t->file->text[i];
538                 /* second and subsequent calls with zero an already empty buffer, but OK */
539                 textreset(u);
540                 windirfree(u->w);
541         }
542         samename = runeeq(r, n, t->file->name, t->file->nname);
543         textload(t, 0, name, samename);
544         if(samename){
545                 t->file->mod = FALSE;
546                 dirty = FALSE;
547         }else{
548                 t->file->mod = TRUE;
549                 dirty = TRUE;
550         }
551         for(i=0; i<t->file->ntext; i++)
552                 t->file->text[i]->w->dirty = dirty;
553         free(name);
554         free(r);
555         winsettag(w);
556         t->file->unread = FALSE;
557         for(i=0; i<t->file->ntext; i++){
558                 u = t->file->text[i];
559                 textsetselect(&u->w->tag, u->w->tag.file->nc, u->w->tag.file->nc);
560                 textscrdraw(u);
561         }
562 }
563
564 void
565 putfile(File *f, int q0, int q1, Rune *namer, int nname)
566 {
567         uint n, m;
568         Rune *r;
569         char *s, *name, *p;
570         int i, fd, q;
571         Dir *d, *d1;
572         Window *w;
573         Plumbmsg *pm;
574         int isapp;
575
576         w = f->curtext->w;
577         name = runetobyte(namer, nname);
578         d = dirstat(name);
579         if(d!=nil && runeeq(namer, nname, f->name, f->nname)){
580                 /* f->mtime+1 because when talking over NFS it's often off by a second */
581                 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime+1<d->mtime){
582                         f->dev = d->dev;
583                         f->qidpath = d->qid.path;
584                         f->mtime = d->mtime;
585                         if(f->unread)
586                                 warning(nil, "%s not written; file already exists\n", name);
587                         else
588                                 warning(nil, "%s modified%s%s since last read\n", name, d->muid[0]?" by ":"", d->muid);
589                         goto Rescue1;
590                 }
591         }
592         fd = create(name, OWRITE, 0666);
593         if(fd < 0){
594                 warning(nil, "can't create file %s: %r\n", name);
595                 goto Rescue1;
596         }
597         r = fbufalloc();
598         s = fbufalloc();
599         free(d);
600         d = dirfstat(fd);
601         isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND));
602         if(isapp){
603                 warning(nil, "%s not written; file is append only\n", name);
604                 goto Rescue2;
605         }
606
607         for(q=q0; q<q1; q+=n){
608                 n = q1 - q;
609                 if(n > (BUFSIZE-1)/UTFmax)
610                         n = (BUFSIZE-1)/UTFmax;
611                 bufread(f, q, r, n);
612                 m = snprint(s, BUFSIZE, "%.*S", n, r);
613                 if(write(fd, s, m) != m){
614                         warning(nil, "can't write file %s: %r\n", name);
615                         goto Rescue2;
616                 }
617         }
618         if(runeeq(namer, nname, f->name, f->nname)){
619                 if(q0!=0 || q1!=f->nc){
620                         f->mod = TRUE;
621                         w->dirty = TRUE;
622                         f->unread = TRUE;
623                 }else{
624                         d1 = dirfstat(fd);
625                         if(d1 != nil){
626                                 free(d);
627                                 d = d1;
628                         }
629                         f->qidpath = d->qid.path;
630                         f->dev = d->dev;
631                         f->mtime = d->mtime;
632                         f->mod = FALSE;
633                         w->dirty = FALSE;
634                         f->unread = FALSE;
635                 }
636                 for(i=0; i<f->ntext; i++){
637                         f->text[i]->w->putseq = f->seq;
638                         f->text[i]->w->dirty = w->dirty;
639                 }
640         }
641         if(plumbsendfd >= 0){
642                 pm = emalloc(sizeof(Plumbmsg));
643                 pm->src = estrdup("acme");
644                 pm->dst = estrdup("put");
645                 pm->wdir = estrdup(name);
646                 if(p = strrchr(pm->wdir, '/'))
647                         *p = '\0';
648                 pm->type = estrdup("text");
649                 pm->attr = nil;
650                 pm->data = estrdup(name);
651                 pm->ndata = strlen(pm->data);
652                 if(pm->ndata < messagesize-1024)
653                         plumbsend(plumbsendfd, pm);
654                 else
655                         plumbfree(pm);
656         }
657         fbuffree(s);
658         fbuffree(r);
659         free(d);
660         free(namer);
661         free(name);
662         close(fd);
663         winsettag(w);
664         return;
665
666     Rescue2:
667         fbuffree(s);
668         fbuffree(r);
669         close(fd);
670         /* fall through */
671
672     Rescue1:
673         free(d);
674         free(namer);
675         free(name);
676 }
677
678 void
679 put(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
680 {
681         int nname;
682         Rune  *namer;
683         Window *w;
684         File *f;
685         char *name;
686
687         if(et==nil || et->w==nil || et->w->isdir)
688                 return;
689         w = et->w;
690         f = w->body.file;
691         name = getname(&w->body, argt, arg, narg, TRUE);
692         if(name == nil){
693                 warning(nil, "no file name\n");
694                 return;
695         }
696         namer = bytetorune(name, &nname);
697         putfile(f, 0, f->nc, namer, nname);
698         free(name);
699 }
700
701 void
702 dump(Text *, Text *, Text *argt, int isdump, int, Rune *arg, int narg)
703 {
704         char *name;
705
706         if(narg)
707                 name = runetobyte(arg, narg);
708         else
709                 getbytearg(argt, FALSE, TRUE, &name);
710         if(isdump)
711                 rowdump(&row, name);
712         else
713                 rowload(&row, name, FALSE);
714         free(name);
715 }
716
717 void
718 cut(Text *et, Text *t, Text*, int dosnarf, int docut, Rune*, int)
719 {
720         uint q0, q1, n, locked, c;
721         Rune *r;
722
723         /*
724          * if not executing a mouse chord (et != t) and snarfing (dosnarf)
725          * and executed Cut or Snarf in window tag (et->w != nil),
726          * then use the window body selection or the tag selection
727          * or do nothing at all.
728          */
729         if(et!=t && dosnarf && et->w!=nil){
730                 if(et->w->body.q1>et->w->body.q0){
731                         t = &et->w->body;
732                         if(docut)
733                                 filemark(t->file);      /* seq has been incremented by execute */
734                 }else if(et->w->tag.q1>et->w->tag.q0)
735                         t = &et->w->tag;
736                 else
737                         t = nil;
738         }
739         if(t == nil)    /* no selection */
740                 return;
741
742         locked = FALSE;
743         if(t->w!=nil && et->w!=t->w){
744                 locked = TRUE;
745                 c = 'M';
746                 if(et->w)
747                         c = et->w->owner;
748                 winlock(t->w, c);
749         }
750         if(t->q0 == t->q1){
751                 if(locked)
752                         winunlock(t->w);
753                 return;
754         }
755         if(dosnarf){
756                 q0 = t->q0;
757                 q1 = t->q1;
758                 bufdelete(&snarfbuf, 0, snarfbuf.nc);
759                 r = fbufalloc();
760                 while(q0 < q1){
761                         n = q1 - q0;
762                         if(n > RBUFSIZE)
763                                 n = RBUFSIZE;
764                         bufread(t->file, q0, r, n);
765                         bufinsert(&snarfbuf, snarfbuf.nc, r, n);
766                         q0 += n;
767                 }
768                 fbuffree(r);
769                 putsnarf();
770         }
771         if(docut){
772                 textdelete(t, t->q0, t->q1, TRUE);
773                 textsetselect(t, t->q0, t->q0);
774                 if(t->w){
775                         textscrdraw(t);
776                         winsettag(t->w);
777                 }
778         }else if(dosnarf)       /* Snarf command */
779                 argtext = t;
780         if(locked)
781                 winunlock(t->w);
782 }
783
784 void
785 paste(Text *et, Text *t, Text*, int selectall, int tobody, Rune*, int)
786 {
787         int c;
788         uint q, q0, q1, n;
789         Rune *r;
790
791         /* if(tobody), use body of executing window  (Paste or Send command) */
792         if(tobody && et!=nil && et->w!=nil){
793                 t = &et->w->body;
794                 filemark(t->file);      /* seq has been incremented by execute */
795         }
796         if(t == nil)
797                 return;
798
799         getsnarf();
800         if(t==nil || snarfbuf.nc==0)
801                 return;
802         if(t->w!=nil && et->w!=t->w){
803                 c = 'M';
804                 if(et->w)
805                         c = et->w->owner;
806                 winlock(t->w, c);
807         }
808         cut(t, t, nil, FALSE, TRUE, nil, 0);
809         q = 0;
810         q0 = t->q0;
811         q1 = t->q0+snarfbuf.nc;
812         r = fbufalloc();
813         while(q0 < q1){
814                 n = q1 - q0;
815                 if(n > RBUFSIZE)
816                         n = RBUFSIZE;
817                 if(r == nil)
818                         r = runemalloc(n);
819                 bufread(&snarfbuf, q, r, n);
820                 textinsert(t, q0, r, n, TRUE);
821                 q += n;
822                 q0 += n;
823         }
824         fbuffree(r);
825         if(selectall)
826                 textsetselect(t, t->q0, q1);
827         else
828                 textsetselect(t, q1, q1);
829         if(t->w){
830                 textscrdraw(t);
831                 winsettag(t->w);
832         }
833         if(t->w!=nil && et->w!=t->w)
834                 winunlock(t->w);
835 }
836
837 void
838 look(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg)
839 {
840         Rune *r;
841         int n;
842
843         if(et && et->w){
844                 t = &et->w->body;
845                 if(narg > 0){
846                         search(t, arg, narg);
847                         return;
848                 }
849                 getarg(argt, FALSE, FALSE, &r, &n);
850                 if(r == nil){
851                         n = t->q1-t->q0;
852                         r = runemalloc(n);
853                         bufread(t->file, t->q0, r, n);
854                 }
855                 search(t, r, n);
856                 free(r);
857         }
858 }
859
860 void
861 sendx(Text *et, Text *t, Text*, int, int, Rune*, int)
862 {
863         if(et->w==nil)
864                 return;
865         t = &et->w->body;
866         if(t->q0 != t->q1)
867                 cut(t, t, nil, TRUE, FALSE, nil, 0);
868         textsetselect(t, t->file->nc, t->file->nc);
869         paste(t, t, nil, TRUE, TRUE, nil, 0);
870         if(textreadc(t, t->file->nc-1) != '\n'){
871                 textinsert(t, t->file->nc, L"\n", 1, TRUE);
872                 textsetselect(t, t->file->nc, t->file->nc);
873         }
874 }
875
876 void
877 edit(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
878 {
879         Rune *r;
880         int len;
881
882         if(et == nil)
883                 return;
884         getarg(argt, FALSE, TRUE, &r, &len);
885         seq++;
886         if(r != nil){
887                 editcmd(et, r, len);
888                 free(r);
889         }else
890                 editcmd(et, arg, narg);
891 }
892
893 void
894 exit(Text*, Text*, Text*, int, int, Rune*, int)
895 {
896         if(rowclean(&row)){
897                 sendul(cexit, 0);
898                 threadexits(nil);
899         }
900 }
901
902 void
903 putall(Text*, Text*, Text*, int, int, Rune*, int)
904 {
905         int i, j, e;
906         Window *w;
907         Column *c;
908         char *a;
909
910         for(i=0; i<row.ncol; i++){
911                 c = row.col[i];
912                 for(j=0; j<c->nw; j++){
913                         w = c->w[j];
914                         if(w->isscratch || w->isdir || w->body.file->nname==0)
915                                 continue;
916                         if(w->nopen[QWevent] > 0)
917                                 continue;
918                         a = runetobyte(w->body.file->name, w->body.file->nname);
919                         e = access(a, 0);
920                         if(w->body.file->mod || w->body.ncache)
921                                 if(e < 0)
922                                         warning(nil, "no auto-Put of %s: %r\n", a);
923                                 else{
924                                         wincommit(w, &w->body);
925                                         put(&w->body, nil, nil, XXX, XXX, nil, 0);
926                                 }
927                         free(a);
928                 }
929         }
930 }
931
932
933 void
934 id(Text *et, Text*, Text*, int, int, Rune*, int)
935 {
936         if(et && et->w)
937                 warning(nil, "/mnt/acme/%d/\n", et->w->id);
938 }
939
940 void
941 local(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
942 {
943         char *a, *aa;
944         Runestr dir;
945
946         aa = getbytearg(argt, TRUE, TRUE, &a);
947
948         dir = dirname(et, nil, 0);
949         if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
950                 free(dir.r);
951                 dir.r = nil;
952                 dir.nr = 0;
953         }
954         run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE);
955 }
956
957 void
958 kill(Text*, Text*, Text *argt, int, int, Rune *arg, int narg)
959 {
960         Rune *a, *cmd, *r;
961         int na;
962
963         getarg(argt, FALSE, FALSE, &r, &na);
964         if(r)
965                 kill(nil, nil, nil, 0, 0, r, na);
966         /* loop condition: *arg is not a blank */
967         for(;;){
968                 a = findbl(arg, narg, &na);
969                 if(a == arg)
970                         break;
971                 cmd = runemalloc(narg-na+1);
972                 runemove(cmd, arg, narg-na);
973                 sendp(ckill, cmd);
974                 arg = skipbl(a, na, &narg);
975         }
976 }
977
978 void
979 fontx(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg)
980 {
981         Rune *a, *r, *flag, *file;
982         int na, nf;
983         char *aa;
984         Reffont *newfont;
985         Dirlist *dp;
986         int i, fix;
987
988         if(et==nil || et->w==nil)
989                 return;
990         t = &et->w->body;
991         flag = nil;
992         file = nil;
993         /* loop condition: *arg is not a blank */
994         nf = 0;
995         for(;;){
996                 a = findbl(arg, narg, &na);
997                 if(a == arg)
998                         break;
999                 r = runemalloc(narg-na+1);
1000                 runemove(r, arg, narg-na);
1001                 if(runeeq(r, narg-na, L"fix", 3) || runeeq(r, narg-na, L"var", 3)){
1002                         free(flag);
1003                         flag = r;
1004                 }else{
1005                         free(file);
1006                         file = r;
1007                         nf = narg-na;
1008                 }
1009                 arg = skipbl(a, na, &narg);
1010         }
1011         getarg(argt, FALSE, TRUE, &r, &na);
1012         if(r)
1013                 if(runeeq(r, na, L"fix", 3) || runeeq(r, na, L"var", 3)){
1014                         free(flag);
1015                         flag = r;
1016                 }else{
1017                         free(file);
1018                         file = r;
1019                         nf = na;
1020                 }
1021         fix = 1;
1022         if(flag)
1023                 fix = runeeq(flag, runestrlen(flag), L"fix", 3);
1024         else if(file == nil){
1025                 newfont = rfget(FALSE, FALSE, FALSE, nil);
1026                 if(newfont)
1027                         fix = strcmp(newfont->f->name, t->font->name)==0;
1028         }
1029         if(file){
1030                 aa = runetobyte(file, nf);
1031                 newfont = rfget(fix, flag!=nil, FALSE, aa);
1032                 free(aa);
1033         }else
1034                 newfont = rfget(fix, FALSE, FALSE, nil);
1035         if(newfont){
1036                 draw(screen, t->w->r, textcols[BACK], nil, ZP);
1037                 rfclose(t->reffont);
1038                 t->reffont = newfont;
1039                 t->font = newfont->f;
1040                 frinittick(t);
1041                 if(t->w->isdir){
1042                         t->all.min.x++; /* force recolumnation; disgusting! */
1043                         for(i=0; i<t->w->ndl; i++){
1044                                 dp = t->w->dlp[i];
1045                                 aa = runetobyte(dp->r, dp->nr);
1046                                 dp->wid = stringwidth(newfont->f, aa);
1047                                 free(aa);
1048                         }
1049                 }
1050                 /* avoid shrinking of window due to quantization */
1051                 colgrow(t->w->col, t->w, -1);
1052         }
1053         free(file);
1054         free(flag);
1055 }
1056
1057 void
1058 incl(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
1059 {
1060         Rune *a, *r;
1061         Window *w;
1062         int na, n, len;
1063
1064         if(et==nil || et->w==nil)
1065                 return;
1066         w = et->w;
1067         n = 0;
1068         getarg(argt, FALSE, TRUE, &r, &len);
1069         if(r){
1070                 n++;
1071                 winaddincl(w, r, len);
1072         }
1073         /* loop condition: *arg is not a blank */
1074         for(;;){
1075                 a = findbl(arg, narg, &na);
1076                 if(a == arg)
1077                         break;
1078                 r = runemalloc(narg-na+1);
1079                 runemove(r, arg, narg-na);
1080                 n++;
1081                 winaddincl(w, r, narg-na);
1082                 arg = skipbl(a, na, &narg);
1083         }
1084         if(n==0 && w->nincl){
1085                 for(n=w->nincl; --n>=0; )
1086                         warning(nil, "%S ", w->incl[n]);
1087                 warning(nil, "\n");
1088         }
1089 }
1090
1091 enum {
1092         IGlobal = -2,
1093         IError = -1,
1094 };
1095
1096 static int
1097 indentval(Rune *s, int n, int type)
1098 {
1099         static char *strs[] = {
1100                 [SPACESINDENT] "Spaces",
1101                 [AUTOINDENT] "Indent",
1102         };
1103
1104         if(n < 2)
1105                 return IError;
1106         if(runestrncmp(s, L"ON", n) == 0){
1107                 globalindent[type] = TRUE;
1108                 warning(nil, "%s ON\n", strs[type]);
1109                 return IGlobal;
1110         }
1111         if(runestrncmp(s, L"OFF", n) == 0){
1112                 globalindent[type] = FALSE;
1113                 warning(nil, "%s OFF\n", strs[type]);
1114                 return IGlobal;
1115         }
1116         if(runestrncmp(s, L"on", n) == 0)
1117                 return TRUE;
1118         if(runestrncmp(s, L"off", n) == 0)
1119                 return FALSE;
1120         return IError;
1121 }
1122
1123 static void
1124 fixindent(Window *w, void *v)
1125 {
1126         int t = *(int*)v;
1127         w->indent[t] = globalindent[t];
1128 }
1129
1130 void
1131 indent(Text *et, Text*, Text *argt, int type, int, Rune *arg, int narg)
1132 {
1133         Rune *a, *r;
1134         Window *w;
1135         int na, len, ival;
1136
1137         w = nil;
1138         if(et!=nil && et->w!=nil)
1139                 w = et->w;
1140         ival = IError;
1141         getarg(argt, FALSE, TRUE, &r, &len);
1142         if(r!=nil && len>0)
1143                 ival = indentval(r, len, type);
1144         else{
1145                 a = findbl(arg, narg, &na);
1146                 if(a != arg)
1147                         ival = indentval(arg, narg-na, type);
1148         }
1149         if(ival == IGlobal)
1150                 allwindows(fixindent, &type);
1151         else if(w != nil && ival >= 0)
1152                 w->indent[type] = ival;
1153 }
1154
1155 void
1156 tab(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
1157 {
1158         Rune *a, *r;
1159         Window *w;
1160         int na, len, tab;
1161         char *p;
1162
1163         if(et==nil || et->w==nil)
1164                 return;
1165         w = et->w;
1166         getarg(argt, FALSE, TRUE, &r, &len);
1167         tab = 0;
1168         if(r!=nil && len>0){
1169                 p = runetobyte(r, len);
1170                 if('0'<=p[0] && p[0]<='9')
1171                         tab = atoi(p);
1172                 free(p);
1173         }else{
1174                 a = findbl(arg, narg, &na);
1175                 if(a != arg){
1176                         p = runetobyte(arg, narg-na);
1177                         if('0'<=p[0] && p[0]<='9')
1178                                 tab = atoi(p);
1179                         free(p);
1180                 }
1181         }
1182         if(tab > 0){
1183                 if(w->body.tabstop != tab){
1184                         w->body.tabstop = tab;
1185                         winresize(w, w->r, 1);
1186                 }
1187         }else
1188                 warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop);
1189 }
1190
1191 void
1192 runproc(void *argvp)
1193 {
1194         /* args: */
1195                 Window *win;
1196                 char *s;
1197                 Rune *rdir;
1198                 int ndir;
1199                 int newns;
1200                 char *argaddr;
1201                 char *arg;
1202                 Command *c;
1203                 Channel *cpid;
1204                 int iseditcmd;
1205         /* end of args */
1206         char *e, *t, *name, *filename, *dir, **av, *news;
1207         Rune r, **incl;
1208         int ac, w, inarg, i, n, fd, nincl, winid;
1209         int pipechar;
1210         char buf[512];
1211         void **argv;
1212
1213         argv = argvp;
1214         win = argv[0];
1215         s = argv[1];
1216         rdir = argv[2];
1217         ndir = (uintptr)argv[3];
1218         newns = (uintptr)argv[4];
1219         argaddr = argv[5];
1220         arg = argv[6];
1221         c = argv[7];
1222         cpid = argv[8];
1223         iseditcmd = (uintptr)argv[9];
1224         free(argv);
1225
1226         t = s;
1227         while(*t==' ' || *t=='\n' || *t=='\t')
1228                 t++;
1229         for(e=t; *e; e++)
1230                 if(*e==' ' || *e=='\n' || *e=='\t' )
1231                         break;
1232         name = emalloc((e-t)+2);
1233         memmove(name, t, e-t);
1234         name[e-t] = 0;
1235         e = utfrrune(name, '/');
1236         if(e)
1237                 memmove(name, e+1, strlen(e+1)+1);      /* strcpy but overlaps */
1238         strcat(name, " ");      /* add blank here for ease in waittask */
1239         c->name = bytetorune(name, &c->nname);
1240         free(name);
1241         pipechar = 0;
1242         if(*t=='<' || *t=='|' || *t=='>')
1243                 pipechar = *t++;
1244         c->iseditcmd = iseditcmd;
1245         c->text = s;
1246         if(rdir != nil){
1247                 dir = runetobyte(rdir, ndir);
1248                 chdir(dir);     /* ignore error: probably app. window */
1249                 free(dir);
1250         }
1251         if(newns){
1252                 nincl = 0;
1253                 incl = nil;
1254                 if(win){
1255                         filename = smprint("%.*S", win->body.file->nname, win->body.file->name);
1256                         nincl = win->nincl;
1257                         if(nincl > 0){
1258                                 incl = emalloc(nincl*sizeof(Rune*));
1259                                 for(i=0; i<nincl; i++){
1260                                         n = runestrlen(win->incl[i]);
1261                                         incl[i] = runemalloc(n+1);
1262                                         runemove(incl[i], win->incl[i], n);
1263                                 }
1264                         }
1265                         winid = win->id;
1266                 }else{
1267                         filename = nil;
1268                         winid = 0;
1269                         if(activewin)
1270                                 winid = activewin->id;
1271                 }
1272                 rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG);
1273                 sprint(buf, "%d", winid);
1274                 putenv("winid", buf);
1275
1276                 if(filename){
1277                         putenv("%", filename);
1278                         free(filename);
1279                 }
1280                 c->md = fsysmount(rdir, ndir, incl, nincl);
1281                 if(c->md == nil){
1282                         fprint(2, "child: can't mount /dev/cons: %r\n");
1283                         threadexits("mount");
1284                 }
1285                 close(0);
1286                 if(winid>0 && (pipechar=='|' || pipechar=='>')){
1287                         sprint(buf, "/mnt/acme/%d/rdsel", winid);
1288                         open(buf, OREAD);
1289                 }else
1290                         open("/dev/null", OREAD);
1291                 close(1);
1292                 if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){
1293                         if(iseditcmd){
1294                                 if(winid > 0)
1295                                         sprint(buf, "/mnt/acme/%d/editout", winid);
1296                                 else
1297                                         sprint(buf, "/mnt/acme/editout");
1298                         }else
1299                                 sprint(buf, "/mnt/acme/%d/wrsel", winid);
1300                         open(buf, OWRITE);
1301                         close(2);
1302                         open("/dev/cons", OWRITE);
1303                 }else{
1304                         open("/dev/cons", OWRITE);
1305                         dup(1, 2);
1306                 }
1307         }else{
1308                 rfork(RFFDG|RFNOTEG);
1309                 fsysclose();
1310                 close(0);
1311                 open("/dev/null", OREAD);
1312                 close(1);
1313                 open(acmeerrorfile, OWRITE);
1314                 dup(1, 2);
1315         }
1316
1317         if(win)
1318                 winclose(win);
1319
1320         if(argaddr)
1321                 putenv("acmeaddr", argaddr);
1322         if(strlen(t) > sizeof buf-10)   /* may need to print into stack */
1323                 goto Hard;
1324         inarg = FALSE;
1325         for(e=t; *e; e+=w){
1326                 w = chartorune(&r, e);
1327                 if(r==' ' || r=='\t')
1328                         continue;
1329                 if(r < ' ')
1330                         goto Hard;
1331                 if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r))
1332                         goto Hard;
1333                 inarg = TRUE;
1334         }
1335         if(!inarg)
1336                 goto Fail;
1337
1338         ac = 0;
1339         av = nil;
1340         inarg = FALSE;
1341         for(e=t; *e; e+=w){
1342                 w = chartorune(&r, e);
1343                 if(r==' ' || r=='\t'){
1344                         inarg = FALSE;
1345                         *e = 0;
1346                         continue;
1347                 }
1348                 if(!inarg){
1349                         inarg = TRUE;
1350                         av = realloc(av, (ac+1)*sizeof(char**));
1351                         av[ac++] = e;
1352                 }
1353         }
1354         av = realloc(av, (ac+2)*sizeof(char**));
1355         av[ac++] = arg;
1356         av[ac] = nil;
1357         c->av = av;
1358         procexec(cpid, av[0], av);
1359         e = av[0];
1360         if(e[0]=='/' || (e[0]=='.' && e[1]=='/'))
1361                 goto Fail;
1362         if(cputype){
1363                 sprint(buf, "%s/%s", cputype, av[0]);
1364                 procexec(cpid, buf, av);
1365         }
1366         sprint(buf, "/bin/%s", av[0]);
1367         procexec(cpid, buf, av);
1368         goto Fail;
1369
1370 Hard:
1371
1372         /*
1373          * ugly: set path = (. $cputype /bin)
1374          * should honor $path if unusual.
1375          */
1376         if(cputype){
1377                 n = 0;
1378                 memmove(buf+n, ".", 2);
1379                 n += 2;
1380                 i = strlen(cputype)+1;
1381                 memmove(buf+n, cputype, i);
1382                 n += i;
1383                 memmove(buf+n, "/bin", 5);
1384                 n += 5;
1385                 fd = create("/env/path", OWRITE, 0666);
1386                 write(fd, buf, n);
1387                 close(fd);
1388         }
1389
1390         if(arg){
1391                 news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1);
1392                 if(news){
1393                         sprint(news, "%s '%s'", t, arg);        /* BUG: what if quote in arg? */
1394                         free(s);
1395                         t = news;
1396                         c->text = news;
1397                 }
1398         }
1399         procexecl(cpid, "/bin/rc", "rc", "-c", t, nil);
1400
1401    Fail:
1402         /* procexec hasn't happened, so send a zero */
1403         sendul(cpid, 0);
1404         threadexits(nil);
1405 }
1406
1407 void
1408 runwaittask(void *v)
1409 {
1410         Command *c;
1411         Channel *cpid;
1412         void **a;
1413
1414         threadsetname("runwaittask");
1415         a = v;
1416         c = a[0];
1417         cpid = a[1];
1418         free(a);
1419         do
1420                 c->pid = recvul(cpid);
1421         while(c->pid == ~0);
1422         free(c->av);
1423         if(c->pid != 0) /* successful exec */
1424                 sendp(ccommand, c);
1425         else{
1426                 if(c->iseditcmd)
1427                         sendul(cedit, 0);
1428                 free(c->name);
1429                 free(c->text);
1430                 free(c);
1431         }
1432         chanfree(cpid);
1433 }
1434
1435 void
1436 run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd)
1437 {
1438         void **arg;
1439         Command *c;
1440         Channel *cpid;
1441
1442         if(s == nil)
1443                 return;
1444
1445         arg = emalloc(10*sizeof(void*));
1446         c = emalloc(sizeof *c);
1447         cpid = chancreate(sizeof(ulong), 0);
1448         arg[0] = win;
1449         arg[1] = s;
1450         arg[2] = rdir;
1451         arg[3] = (void*)ndir;
1452         arg[4] = (void*)newns;
1453         arg[5] = argaddr;
1454         arg[6] = xarg;
1455         arg[7] = c;
1456         arg[8] = cpid;
1457         arg[9] = (void*)iseditcmd;
1458         proccreate(runproc, arg, STACK);
1459         /* mustn't block here because must be ready to answer mount() call in run() */
1460         arg = emalloc(2*sizeof(void*));
1461         arg[0] = c;
1462         arg[1] = cpid;
1463         threadcreate(runwaittask, arg, STACK);
1464 }