Blob


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