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