Blob


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 <9pclient.h>
12 #include "dat.h"
13 #include "fns.h"
15 Buffer snarfbuf;
17 void doabort(Text*, Text*, Text*, int, int, Rune*, int);
18 void del(Text*, Text*, Text*, int, int, Rune*, int);
19 void delcol(Text*, Text*, Text*, int, int, Rune*, int);
20 void dotfiles(Text*, Text*, Text*, int, int, Rune*, int);
21 void dump(Text*, Text*, Text*, int, int, Rune*, int);
22 void edit(Text*, Text*, Text*, int, int, Rune*, int);
23 void xexit(Text*, Text*, Text*, int, int, Rune*, int);
24 void fontx(Text*, Text*, Text*, int, int, Rune*, int);
25 void get(Text*, Text*, Text*, int, int, Rune*, int);
26 void id(Text*, Text*, Text*, int, int, Rune*, int);
27 void incl(Text*, Text*, Text*, int, int, Rune*, int);
28 void indent(Text*, Text*, Text*, int, int, Rune*, int);
29 void xkill(Text*, Text*, Text*, int, int, Rune*, int);
30 void local(Text*, Text*, Text*, int, int, Rune*, int);
31 void look(Text*, Text*, Text*, int, int, Rune*, int);
32 void newcol(Text*, Text*, Text*, int, int, Rune*, int);
33 void paste(Text*, Text*, Text*, int, int, Rune*, int);
34 void put(Text*, Text*, Text*, int, int, Rune*, int);
35 void putall(Text*, Text*, Text*, int, int, Rune*, int);
36 void sendx(Text*, Text*, Text*, int, int, Rune*, int);
37 void sort(Text*, Text*, Text*, int, int, Rune*, int);
38 void tab(Text*, Text*, Text*, int, int, Rune*, int);
39 void zeroxx(Text*, Text*, Text*, int, int, Rune*, int);
41 typedef struct Exectab Exectab;
42 struct Exectab
43 {
44 Rune *name;
45 void (*fn)(Text*, Text*, Text*, int, int, Rune*, int);
46 int mark;
47 int flag1;
48 int flag2;
49 };
51 static Rune LAbort[] = { 'A', 'b', 'o', 'r', 't', 0 };
52 static Rune LCut[] = { 'C', 'u', 't', 0 };
53 static Rune LDel[] = { 'D', 'e', 'l', 0 };
54 static Rune LDelcol[] = { 'D', 'e', 'l', 'c', 'o', 'l', 0 };
55 static Rune LDelete[] = { 'D', 'e', 'l', 'e', 't', 'e', 0 };
56 static Rune LDump[] = { 'D', 'u', 'm', 'p', 0 };
57 static Rune LEdit[] = { 'E', 'd', 'i', 't', 0 };
58 static Rune LExit[] = { 'E', 'x', 'i', 't', 0 };
59 static Rune LFont[] = { 'F', 'o', 'n', 't', 0 };
60 static Rune LGet[] = { 'G', 'e', 't', 0 };
61 static Rune LID[] = { 'I', 'D', 0 };
62 static Rune LIncl[] = { 'I', 'n', 'c', 'l', 0 };
63 static Rune LIndent[] = { 'I', 'n', 'd', 'e', 'n', 't', 0 };
64 static Rune LKill[] = { 'K', 'i', 'l', 'l', 0 };
65 static Rune LLoad[] = { 'L', 'o', 'a', 'd', 0 };
66 static Rune LLocal[] = { 'L', 'o', 'c', 'a', 'l', 0 };
67 static Rune LLook[] = { 'L', 'o', 'o', 'k', 0 };
68 static Rune LNew[] = { 'N', 'e', 'w', 0 };
69 static Rune LNewcol[] = { 'N', 'e', 'w', 'c', 'o', 'l', 0 };
70 static Rune LPaste[] = { 'P', 'a', 's', 't', 'e', 0 };
71 static Rune LPut[] = { 'P', 'u', 't', 0 };
72 static Rune LPutall[] = { 'P', 'u', 't', 'a', 'l', 'l', 0 };
73 static Rune LRedo[] = { 'R', 'e', 'd', 'o', 0 };
74 static Rune LSend[] = { 'S', 'e', 'n', 'd', 0 };
75 static Rune LSnarf[] = { 'S', 'n', 'a', 'r', 'f', 0 };
76 static Rune LSort[] = { 'S', 'o', 'r', 't', 0 };
77 static Rune LTab[] = { 'T', 'a', 'b', 0 };
78 static Rune LUndo[] = { 'U', 'n', 'd', 'o', 0 };
79 static Rune LZerox[] = { 'Z', 'e', 'r', 'o', 'x', 0 };
81 Exectab exectab[] = {
82 { LAbort, doabort, FALSE, XXX, XXX, },
83 { LCut, cut, TRUE, TRUE, TRUE },
84 { LDel, del, FALSE, FALSE, XXX },
85 { LDelcol, delcol, FALSE, XXX, XXX },
86 { LDelete, del, FALSE, TRUE, XXX },
87 { LDump, dump, FALSE, TRUE, XXX },
88 { LEdit, edit, FALSE, XXX, XXX },
89 { LExit, xexit, FALSE, XXX, XXX },
90 { LFont, fontx, FALSE, XXX, XXX },
91 { LGet, get, FALSE, TRUE, XXX },
92 { LID, id, FALSE, XXX, XXX },
93 { LIncl, incl, FALSE, XXX, XXX },
94 { LIndent, indent, FALSE, XXX, XXX },
95 { LKill, xkill, FALSE, XXX, XXX },
96 { LLoad, dump, FALSE, FALSE, XXX },
97 { LLocal, local, FALSE, XXX, XXX },
98 { LLook, look, FALSE, XXX, XXX },
99 { LNew, new, FALSE, XXX, XXX },
100 { LNewcol, newcol, FALSE, XXX, XXX },
101 { LPaste, paste, TRUE, TRUE, XXX },
102 { LPut, put, FALSE, XXX, XXX },
103 { LPutall, putall, FALSE, XXX, XXX },
104 { LRedo, undo, FALSE, FALSE, XXX },
105 { LSend, sendx, TRUE, XXX, XXX },
106 { LSnarf, cut, FALSE, TRUE, FALSE },
107 { LSort, sort, FALSE, XXX, XXX },
108 { LTab, tab, FALSE, XXX, XXX },
109 { LUndo, undo, FALSE, TRUE, XXX },
110 { LZerox, zeroxx, FALSE, XXX, XXX },
111 { nil, 0, 0, 0, 0 }
112 };
114 Exectab*
115 lookup(Rune *r, int n)
117 Exectab *e;
118 int nr;
120 r = skipbl(r, n, &n);
121 if(n == 0)
122 return nil;
123 findbl(r, n, &nr);
124 nr = n-nr;
125 for(e=exectab; e->name; e++)
126 if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE)
127 return e;
128 return nil;
131 int
132 isexecc(int c)
134 if(isfilec(c))
135 return 1;
136 return c=='<' || c=='|' || c=='>';
139 void
140 execute(Text *t, uint aq0, uint aq1, int external, Text *argt)
142 uint q0, q1;
143 Rune *r, *s;
144 char *b, *a, *aa;
145 Exectab *e;
146 int c, n, f;
147 Runestr dir;
149 q0 = aq0;
150 q1 = aq1;
151 if(q1 == q0){ /* expand to find word (actually file name) */
152 /* if in selection, choose selection */
153 if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
154 q0 = t->q0;
155 q1 = t->q1;
156 }else{
157 while(q1<t->file->b.nc && isexecc(c=textreadc(t, q1)) && c!=':')
158 q1++;
159 while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':')
160 q0--;
161 if(q1 == q0)
162 return;
165 r = runemalloc(q1-q0);
166 bufread(&t->file->b, q0, r, q1-q0);
167 e = lookup(r, q1-q0);
168 if(!external && t->w!=nil && t->w->nopen[QWevent]>0){
169 f = 0;
170 if(e)
171 f |= 1;
172 if(q0!=aq0 || q1!=aq1){
173 bufread(&t->file->b, aq0, r, aq1-aq0);
174 f |= 2;
176 aa = getbytearg(argt, TRUE, TRUE, &a);
177 if(a){
178 if(strlen(a) > EVENTSIZE){ /* too big; too bad */
179 free(aa);
180 free(a);
181 warning(nil, "`argument string too long\n");
182 return;
184 f |= 8;
186 c = 'x';
187 if(t->what == Body)
188 c = 'X';
189 n = aq1-aq0;
190 if(n <= EVENTSIZE)
191 winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r);
192 else
193 winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f, n);
194 if(q0!=aq0 || q1!=aq1){
195 n = q1-q0;
196 bufread(&t->file->b, q0, r, n);
197 if(n <= EVENTSIZE)
198 winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r);
199 else
200 winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1, n);
202 if(a){
203 winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a);
204 if(aa)
205 winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa);
206 else
207 winevent(t->w, "%c0 0 0 0 \n", c);
209 free(r);
210 free(aa);
211 free(a);
212 return;
214 if(e){
215 if(e->mark && seltext!=nil)
216 if(seltext->what == Body){
217 seq++;
218 filemark(seltext->w->body.file);
220 s = skipbl(r, q1-q0, &n);
221 s = findbl(s, n, &n);
222 s = skipbl(s, n, &n);
223 (*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n);
224 free(r);
225 return;
228 b = runetobyte(r, q1-q0);
229 free(r);
230 dir = dirname(t, nil, 0);
231 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
232 free(dir.r);
233 dir.r = nil;
234 dir.nr = 0;
236 aa = getbytearg(argt, TRUE, TRUE, &a);
237 if(t->w)
238 incref(&t->w->ref);
239 run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE);
242 char*
243 printarg(Text *argt, uint q0, uint q1)
245 char *buf;
247 if(argt->what!=Body || argt->file->name==nil)
248 return nil;
249 buf = emalloc(argt->file->nname+32);
250 if(q0 == q1)
251 sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0);
252 else
253 sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1);
254 return buf;
257 char*
258 getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp)
260 int n;
261 Expand e;
262 char *a;
264 *rp = nil;
265 *nrp = 0;
266 if(argt == nil)
267 return nil;
268 a = nil;
269 textcommit(argt, TRUE);
270 if(expand(argt, argt->q0, argt->q1, &e)){
271 free(e.bname);
272 if(e.nname && dofile){
273 e.name = runerealloc(e.name, e.nname+1);
274 if(doaddr)
275 a = printarg(argt, e.q0, e.q1);
276 *rp = e.name;
277 *nrp = e.nname;
278 return a;
280 free(e.name);
281 }else{
282 e.q0 = argt->q0;
283 e.q1 = argt->q1;
285 n = e.q1 - e.q0;
286 *rp = runemalloc(n+1);
287 bufread(&argt->file->b, e.q0, *rp, n);
288 if(doaddr)
289 a = printarg(argt, e.q0, e.q1);
290 *nrp = n;
291 return a;
294 char*
295 getbytearg(Text *argt, int doaddr, int dofile, char **bp)
297 Rune *r;
298 int n;
299 char *aa;
301 *bp = nil;
302 aa = getarg(argt, doaddr, dofile, &r, &n);
303 if(r == nil)
304 return nil;
305 *bp = runetobyte(r, n);
306 free(r);
307 return aa;
310 void
311 doabort(Text *__0, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
313 static int n;
315 USED(__0);
316 USED(_0);
317 USED(_1);
318 USED(_2);
319 USED(_3);
320 USED(_4);
321 USED(_5);
323 if(n++ == 0)
324 warning(nil, "executing Abort again will call abort()\n");
325 else
326 abort();
329 void
330 newcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
332 Column *c;
334 USED(_0);
335 USED(_1);
336 USED(_2);
337 USED(_3);
338 USED(_4);
339 USED(_5);
341 c = rowadd(et->row, nil, -1);
342 if(c)
343 winsettag(coladd(c, nil, nil, -1));
346 void
347 delcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
349 int i;
350 Column *c;
351 Window *w;
353 USED(_0);
354 USED(_1);
355 USED(_2);
356 USED(_3);
357 USED(_4);
358 USED(_5);
360 c = et->col;
361 if(c==nil || colclean(c)==0)
362 return;
363 for(i=0; i<c->nw; i++){
364 w = c->w[i];
365 if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata]+w->nopen[QWxdata] > 0){
366 warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name);
367 return;
370 rowclose(et->col->row, et->col, TRUE);
373 void
374 del(Text *et, Text *_0, Text *_1, int flag1, int _2, Rune *_3, int _4)
376 USED(_0);
377 USED(_1);
378 USED(_2);
379 USED(_3);
380 USED(_4);
382 if(et->col==nil || et->w == nil)
383 return;
384 if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE))
385 colclose(et->col, et->w, TRUE);
388 void
389 sort(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
391 USED(_0);
392 USED(_1);
393 USED(_2);
394 USED(_3);
395 USED(_4);
396 USED(_5);
398 if(et->col)
399 colsort(et->col);
402 uint
403 seqof(Window *w, int isundo)
405 /* if it's undo, see who changed with us */
406 if(isundo)
407 return w->body.file->seq;
408 /* if it's redo, see who we'll be sync'ed up with */
409 return fileredoseq(w->body.file);
412 void
413 undo(Text *et, Text *_0, Text *_1, int flag1, int _2, Rune *_3, int _4)
415 int i, j;
416 Column *c;
417 Window *w;
418 uint seq;
420 USED(_0);
421 USED(_1);
422 USED(_2);
423 USED(_3);
424 USED(_4);
426 if(et==nil || et->w== nil)
427 return;
428 seq = seqof(et->w, flag1);
429 if(seq == 0){
430 /* nothing to undo */
431 return;
433 /*
434 * Undo the executing window first. Its display will update. other windows
435 * in the same file will not call show() and jump to a different location in the file.
436 * Simultaneous changes to other files will be chaotic, however.
437 */
438 winundo(et->w, flag1);
439 for(i=0; i<row.ncol; i++){
440 c = row.col[i];
441 for(j=0; j<c->nw; j++){
442 w = c->w[j];
443 if(w == et->w)
444 continue;
445 if(seqof(w, flag1) == seq)
446 winundo(w, flag1);
451 char*
452 getname(Text *t, Text *argt, Rune *arg, int narg, int isput)
454 char *s;
455 Rune *r;
456 int i, n, promote;
457 Runestr dir;
459 getarg(argt, FALSE, TRUE, &r, &n);
460 promote = FALSE;
461 if(r == nil)
462 promote = TRUE;
463 else if(isput){
464 /* if are doing a Put, want to synthesize name even for non-existent file */
465 /* best guess is that file name doesn't contain a slash */
466 promote = TRUE;
467 for(i=0; i<n; i++)
468 if(r[i] == '/'){
469 promote = FALSE;
470 break;
472 if(promote){
473 t = argt;
474 arg = r;
475 narg = n;
478 if(promote){
479 n = narg;
480 if(n <= 0){
481 s = runetobyte(t->file->name, t->file->nname);
482 return s;
484 /* prefix with directory name if necessary */
485 dir.r = nil;
486 dir.nr = 0;
487 if(n>0 && arg[0]!='/'){
488 dir = dirname(t, nil, 0);
489 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
490 free(dir.r);
491 dir.r = nil;
492 dir.nr = 0;
495 if(dir.r){
496 r = runemalloc(dir.nr+n+1);
497 runemove(r, dir.r, dir.nr);
498 free(dir.r);
499 if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/')
500 r[dir.nr++] = '/';
501 runemove(r+dir.nr, arg, n);
502 n += dir.nr;
503 }else{
504 r = runemalloc(n+1);
505 runemove(r, arg, n);
508 s = runetobyte(r, n);
509 free(r);
510 if(strlen(s) == 0){
511 free(s);
512 s = nil;
514 return s;
517 void
518 zeroxx(Text *et, Text *t, Text *_1, int _2, int _3, Rune *_4, int _5)
520 Window *nw;
521 int c, locked;
523 USED(_1);
524 USED(_2);
525 USED(_3);
526 USED(_4);
527 USED(_5);
529 locked = FALSE;
530 if(t!=nil && t->w!=nil && t->w!=et->w){
531 locked = TRUE;
532 c = 'M';
533 if(et->w)
534 c = et->w->owner;
535 winlock(t->w, c);
537 if(t == nil)
538 t = et;
539 if(t==nil || t->w==nil)
540 return;
541 t = &t->w->body;
542 if(t->w->isdir)
543 warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name);
544 else{
545 nw = coladd(t->w->col, nil, t->w, -1);
546 /* ugly: fix locks so w->unlock works */
547 winlock1(nw, t->w->owner);
549 if(locked)
550 winunlock(t->w);
553 void
554 get(Text *et, Text *t, Text *argt, int flag1, int _0, Rune *arg, int narg)
556 char *name;
557 Rune *r;
558 int i, n, dirty, samename, isdir;
559 Window *w;
560 Text *u;
561 Dir *d;
563 USED(_0);
565 if(flag1)
566 if(et==nil || et->w==nil)
567 return;
568 if(!et->w->isdir && (et->w->body.file->b.nc>0 && !winclean(et->w, TRUE)))
569 return;
570 w = et->w;
571 t = &w->body;
572 name = getname(t, argt, arg, narg, FALSE);
573 if(name == nil){
574 warning(nil, "no file name\n");
575 return;
577 if(t->file->ntext>1){
578 d = dirstat(name);
579 isdir = (d!=nil && (d->qid.type & QTDIR));
580 free(d);
581 if(isdir){
582 warning(nil, "%s is a directory; can't read with multiple windows on it\n", name);
583 return;
586 r = bytetorune(name, &n);
587 for(i=0; i<t->file->ntext; i++){
588 u = t->file->text[i];
589 /* second and subsequent calls with zero an already empty buffer, but OK */
590 textreset(u);
591 windirfree(u->w);
593 samename = runeeq(r, n, t->file->name, t->file->nname);
594 textload(t, 0, name, samename);
595 if(samename){
596 t->file->mod = FALSE;
597 dirty = FALSE;
598 }else{
599 t->file->mod = TRUE;
600 dirty = TRUE;
602 for(i=0; i<t->file->ntext; i++)
603 t->file->text[i]->w->dirty = dirty;
604 free(name);
605 free(r);
606 winsettag(w);
607 t->file->unread = FALSE;
608 for(i=0; i<t->file->ntext; i++){
609 u = t->file->text[i];
610 textsetselect(&u->w->tag, u->w->tag.file->b.nc, u->w->tag.file->b.nc);
611 textscrdraw(u);
615 void
616 putfile(File *f, int q0, int q1, Rune *namer, int nname)
618 uint n, m;
619 Rune *r;
620 char *s, *name;
621 int i, fd, q;
622 Dir *d, *d1;
623 Window *w;
624 int isapp;
626 w = f->curtext->w;
627 name = runetobyte(namer, nname);
628 d = dirstat(name);
629 if(d!=nil && runeeq(namer, nname, f->name, f->nname)){
630 /* f->mtime+1 because when talking over NFS it's often off by a second */
631 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime+1<d->mtime){
632 f->dev = d->dev;
633 f->qidpath = d->qid.path;
634 f->mtime = d->mtime;
635 if(f->unread)
636 warning(nil, "%s not written; file already exists\n", name);
637 else
638 warning(nil, "%s modified%s%s since last read\n", name, d->muid[0]?" by ":"", d->muid);
639 goto Rescue1;
642 fd = create(name, OWRITE, 0666);
643 if(fd < 0){
644 warning(nil, "can't create file %s: %r\n", name);
645 goto Rescue1;
647 r = fbufalloc();
648 s = fbufalloc();
649 free(d);
650 d = dirfstat(fd);
651 isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND));
652 if(isapp){
653 warning(nil, "%s not written; file is append only\n", name);
654 goto Rescue2;
657 for(q=q0; q<q1; q+=n){
658 n = q1 - q;
659 if(n > BUFSIZE/UTFmax)
660 n = BUFSIZE/UTFmax;
661 bufread(&f->b, q, r, n);
662 m = snprint(s, BUFSIZE+1, "%.*S", n, r);
663 if(write(fd, s, m) != m){
664 warning(nil, "can't write file %s: %r\n", name);
665 goto Rescue2;
668 if(runeeq(namer, nname, f->name, f->nname)){
669 if(q0!=0 || q1!=f->b.nc){
670 f->mod = TRUE;
671 w->dirty = TRUE;
672 f->unread = TRUE;
673 }else{
674 d1 = dirfstat(fd);
675 if(d1 != nil){
676 free(d);
677 d = d1;
679 f->qidpath = d->qid.path;
680 f->dev = d->dev;
681 f->mtime = d->mtime;
682 f->mod = FALSE;
683 w->dirty = FALSE;
684 f->unread = FALSE;
686 for(i=0; i<f->ntext; i++){
687 f->text[i]->w->putseq = f->seq;
688 f->text[i]->w->dirty = w->dirty;
691 fbuffree(s);
692 fbuffree(r);
693 free(d);
694 free(namer);
695 free(name);
696 close(fd);
697 winsettag(w);
698 return;
700 Rescue2:
701 fbuffree(s);
702 fbuffree(r);
703 close(fd);
704 /* fall through */
706 Rescue1:
707 free(d);
708 free(namer);
709 free(name);
712 void
713 put(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
715 int nname;
716 Rune *namer;
717 Window *w;
718 File *f;
719 char *name;
721 USED(_0);
722 USED(_1);
723 USED(_2);
725 if(et==nil || et->w==nil || et->w->isdir)
726 return;
727 w = et->w;
728 f = w->body.file;
729 name = getname(&w->body, argt, arg, narg, TRUE);
730 if(name == nil){
731 warning(nil, "no file name\n");
732 return;
734 namer = bytetorune(name, &nname);
735 putfile(f, 0, f->b.nc, namer, nname);
736 free(name);
739 void
740 dump(Text *_0, Text *_1, Text *argt, int isdump, int _2, Rune *arg, int narg)
742 char *name;
744 USED(_0);
745 USED(_1);
746 USED(_2);
748 if(narg)
749 name = runetobyte(arg, narg);
750 else
751 getbytearg(argt, FALSE, TRUE, &name);
752 if(isdump)
753 rowdump(&row, name);
754 else
755 rowload(&row, name, FALSE);
756 free(name);
759 void
760 cut(Text *et, Text *t, Text *_0, int dosnarf, int docut, Rune *_2, int _3)
762 uint q0, q1, n, locked, c;
763 Rune *r;
765 USED(_0);
766 USED(_2);
767 USED(_3);
769 /* use current window if snarfing and its selection is non-null */
770 if(et!=t && dosnarf && et->w!=nil){
771 if(et->w->body.q1>et->w->body.q0){
772 t = &et->w->body;
773 if(docut)
774 filemark(t->file); /* seq has been incremented by execute */
775 }else if(et->w->tag.q1>et->w->tag.q0)
776 t = &et->w->tag;
778 if(t == nil){
779 /* can only happen if seltext == nil */
780 return;
782 locked = FALSE;
783 if(t->w!=nil && et->w!=t->w){
784 locked = TRUE;
785 c = 'M';
786 if(et->w)
787 c = et->w->owner;
788 winlock(t->w, c);
790 if(t->q0 == t->q1){
791 if(locked)
792 winunlock(t->w);
793 return;
795 if(dosnarf){
796 q0 = t->q0;
797 q1 = t->q1;
798 bufdelete(&snarfbuf, 0, snarfbuf.nc);
799 r = fbufalloc();
800 while(q0 < q1){
801 n = q1 - q0;
802 if(n > RBUFSIZE)
803 n = RBUFSIZE;
804 bufread(&t->file->b, q0, r, n);
805 bufinsert(&snarfbuf, snarfbuf.nc, r, n);
806 q0 += n;
808 fbuffree(r);
809 acmeputsnarf();
811 if(docut){
812 textdelete(t, t->q0, t->q1, TRUE);
813 textsetselect(t, t->q0, t->q0);
814 if(t->w){
815 textscrdraw(t);
816 winsettag(t->w);
818 }else if(dosnarf) /* Snarf command */
819 argtext = t;
820 if(locked)
821 winunlock(t->w);
824 void
825 paste(Text *et, Text *t, Text *_0, int selectall, int tobody, Rune *_1, int _2)
827 int c;
828 uint q, q0, q1, n;
829 Rune *r;
831 USED(_0);
832 USED(_1);
833 USED(_2);
835 /* if(tobody), use body of executing window (Paste or Send command) */
836 if(tobody && et!=nil && et->w!=nil){
837 t = &et->w->body;
838 filemark(t->file); /* seq has been incremented by execute */
840 if(t == nil)
841 return;
843 acmegetsnarf();
844 if(t==nil || snarfbuf.nc==0)
845 return;
846 if(t->w!=nil && et->w!=t->w){
847 c = 'M';
848 if(et->w)
849 c = et->w->owner;
850 winlock(t->w, c);
852 cut(t, t, nil, FALSE, TRUE, nil, 0);
853 q = 0;
854 q0 = t->q0;
855 q1 = t->q0+snarfbuf.nc;
856 r = fbufalloc();
857 while(q0 < q1){
858 n = q1 - q0;
859 if(n > RBUFSIZE)
860 n = RBUFSIZE;
861 if(r == nil)
862 r = runemalloc(n);
863 bufread(&snarfbuf, q, r, n);
864 textinsert(t, q0, r, n, TRUE);
865 q += n;
866 q0 += n;
868 fbuffree(r);
869 if(selectall)
870 textsetselect(t, t->q0, q1);
871 else
872 textsetselect(t, q1, q1);
873 if(t->w){
874 textscrdraw(t);
875 winsettag(t->w);
877 if(t->w!=nil && et->w!=t->w)
878 winunlock(t->w);
881 void
882 look(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg)
884 Rune *r;
885 int n;
887 USED(_0);
888 USED(_1);
890 if(et && et->w){
891 t = &et->w->body;
892 if(narg > 0){
893 search(t, arg, narg);
894 return;
896 getarg(argt, FALSE, FALSE, &r, &n);
897 if(r == nil){
898 n = t->q1-t->q0;
899 r = runemalloc(n);
900 bufread(&t->file->b, t->q0, r, n);
902 search(t, r, n);
903 free(r);
907 static Rune Lnl[] = { '\n', 0 };
909 void
910 sendx(Text *et, Text *t, Text *_0, int _1, int _2, Rune *_3, int _4)
912 USED(_0);
913 USED(_1);
914 USED(_2);
915 USED(_3);
916 USED(_4);
918 if(et->w==nil)
919 return;
920 t = &et->w->body;
921 if(t->q0 != t->q1)
922 cut(t, t, nil, TRUE, FALSE, nil, 0);
923 textsetselect(t, t->file->b.nc, t->file->b.nc);
924 paste(t, t, nil, TRUE, TRUE, nil, 0);
925 if(textreadc(t, t->file->b.nc-1) != '\n'){
926 textinsert(t, t->file->b.nc, Lnl, 1, TRUE);
927 textsetselect(t, t->file->b.nc, t->file->b.nc);
931 void
932 edit(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
934 Rune *r;
935 int len;
937 USED(_0);
938 USED(_1);
939 USED(_2);
941 if(et == nil)
942 return;
943 getarg(argt, FALSE, TRUE, &r, &len);
944 seq++;
945 if(r != nil){
946 editcmd(et, r, len);
947 free(r);
948 }else
949 editcmd(et, arg, narg);
952 void
953 xexit(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
955 USED(et);
956 USED(_0);
957 USED(_1);
958 USED(_2);
959 USED(_3);
960 USED(_4);
961 USED(_5);
963 if(rowclean(&row)){
964 sendul(cexit, 0);
965 threadexits(nil);
969 void
970 putall(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
972 int i, j, e;
973 Window *w;
974 Column *c;
975 char *a;
977 USED(et);
978 USED(_0);
979 USED(_1);
980 USED(_2);
981 USED(_3);
982 USED(_4);
983 USED(_5);
985 for(i=0; i<row.ncol; i++){
986 c = row.col[i];
987 for(j=0; j<c->nw; j++){
988 w = c->w[j];
989 if(w->isscratch || w->isdir || w->body.file->nname==0)
990 continue;
991 if(w->nopen[QWevent] > 0)
992 continue;
993 a = runetobyte(w->body.file->name, w->body.file->nname);
994 e = access(a, 0);
995 if(w->body.file->mod || w->body.ncache)
996 if(e < 0)
997 warning(nil, "no auto-Put of %s: %r\n", a);
998 else{
999 wincommit(w, &w->body);
1000 put(&w->body, nil, nil, XXX, XXX, nil, 0);
1002 free(a);
1008 void
1009 id(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
1011 USED(et);
1012 USED(_0);
1013 USED(_1);
1014 USED(_2);
1015 USED(_3);
1016 USED(_4);
1017 USED(_5);
1019 if(et && et->w)
1020 warning(nil, "/mnt/acme/%d/\n", et->w->id);
1023 void
1024 local(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
1026 char *a, *aa;
1027 Runestr dir;
1029 USED(_0);
1030 USED(_1);
1031 USED(_2);
1033 aa = getbytearg(argt, TRUE, TRUE, &a);
1035 dir = dirname(et, nil, 0);
1036 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
1037 free(dir.r);
1038 dir.r = nil;
1039 dir.nr = 0;
1041 run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE);
1044 void
1045 xkill(Text *_0, Text *_1, Text *argt, int _2, int _3, Rune *arg, int narg)
1047 Rune *a, *cmd, *r;
1048 int na;
1050 USED(_0);
1051 USED(_1);
1052 USED(_2);
1053 USED(_3);
1055 getarg(argt, FALSE, FALSE, &r, &na);
1056 if(r)
1057 xkill(nil, nil, nil, 0, 0, r, na);
1058 /* loop condition: *arg is not a blank */
1059 for(;;){
1060 a = findbl(arg, narg, &na);
1061 if(a == arg)
1062 break;
1063 cmd = runemalloc(narg-na+1);
1064 runemove(cmd, arg, narg-na);
1065 sendp(ckill, cmd);
1066 arg = skipbl(a, na, &narg);
1070 static Rune Lfix[] = { 'f', 'i', 'x', 0 };
1071 static Rune Lvar[] = { 'v', 'a', 'r', 0 };
1073 void
1074 fontx(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg)
1076 Rune *a, *r, *flag, *file;
1077 int na, nf;
1078 char *aa;
1079 Reffont *newfont;
1080 Dirlist *dp;
1081 int i, fix;
1083 USED(_0);
1084 USED(_1);
1086 if(et==nil || et->w==nil)
1087 return;
1088 t = &et->w->body;
1089 flag = nil;
1090 file = nil;
1091 /* loop condition: *arg is not a blank */
1092 nf = 0;
1093 for(;;){
1094 a = findbl(arg, narg, &na);
1095 if(a == arg)
1096 break;
1097 r = runemalloc(narg-na+1);
1098 runemove(r, arg, narg-na);
1099 if(runeeq(r, narg-na, Lfix, 3) || runeeq(r, narg-na, Lvar, 3)){
1100 free(flag);
1101 flag = r;
1102 }else{
1103 free(file);
1104 file = r;
1105 nf = narg-na;
1107 arg = skipbl(a, na, &narg);
1109 getarg(argt, FALSE, TRUE, &r, &na);
1110 if(r)
1111 if(runeeq(r, na, Lfix, 3) || runeeq(r, na, Lvar, 3)){
1112 free(flag);
1113 flag = r;
1114 }else{
1115 free(file);
1116 file = r;
1117 nf = na;
1119 fix = 1;
1120 if(flag)
1121 fix = runeeq(flag, runestrlen(flag), Lfix, 3);
1122 else if(file == nil){
1123 newfont = rfget(FALSE, FALSE, FALSE, nil);
1124 if(newfont)
1125 fix = strcmp(newfont->f->name, t->fr.font->name)==0;
1127 if(file){
1128 aa = runetobyte(file, nf);
1129 newfont = rfget(fix, flag!=nil, FALSE, aa);
1130 free(aa);
1131 }else
1132 newfont = rfget(fix, FALSE, FALSE, nil);
1133 if(newfont){
1134 draw(screen, t->w->r, textcols[BACK], nil, ZP);
1135 rfclose(t->reffont);
1136 t->reffont = newfont;
1137 t->fr.font = newfont->f;
1138 frinittick(&t->fr);
1139 if(t->w->isdir){
1140 t->all.min.x++; /* force recolumnation; disgusting! */
1141 for(i=0; i<t->w->ndl; i++){
1142 dp = t->w->dlp[i];
1143 aa = runetobyte(dp->r, dp->nr);
1144 dp->wid = stringwidth(newfont->f, aa);
1145 free(aa);
1148 /* avoid shrinking of window due to quantization */
1149 colgrow(t->w->col, t->w, -1);
1151 free(file);
1152 free(flag);
1155 void
1156 incl(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
1158 Rune *a, *r;
1159 Window *w;
1160 int na, n, len;
1162 USED(_0);
1163 USED(_1);
1164 USED(_2);
1166 if(et==nil || et->w==nil)
1167 return;
1168 w = et->w;
1169 n = 0;
1170 getarg(argt, FALSE, TRUE, &r, &len);
1171 if(r){
1172 n++;
1173 winaddincl(w, r, len);
1175 /* loop condition: *arg is not a blank */
1176 for(;;){
1177 a = findbl(arg, narg, &na);
1178 if(a == arg)
1179 break;
1180 r = runemalloc(narg-na+1);
1181 runemove(r, arg, narg-na);
1182 n++;
1183 winaddincl(w, r, narg-na);
1184 arg = skipbl(a, na, &narg);
1186 if(n==0 && w->nincl){
1187 for(n=w->nincl; --n>=0; )
1188 warning(nil, "%S ", w->incl[n]);
1189 warning(nil, "\n");
1193 static Rune LON[] = { 'O', 'N', 0 };
1194 static Rune LOFF[] = { 'O', 'F', 'F', 0 };
1195 static Rune Lon[] = { 'o', 'n', 0 };
1197 enum {
1198 IGlobal = -2,
1199 IError = -1,
1200 Ion = 0,
1201 Ioff = 1
1204 static int
1205 indentval(Rune *s, int n)
1207 if(n < 2)
1208 return IError;
1209 if(runestrncmp(s, LON, n) == 0){
1210 globalautoindent = TRUE;
1211 warning(nil, "Indent ON\n");
1212 return IGlobal;
1214 if(runestrncmp(s, LOFF, n) == 0){
1215 globalautoindent = FALSE;
1216 warning(nil, "Indent OFF\n");
1217 return IGlobal;
1219 return runestrncmp(s, Lon, n) == 0;
1222 void
1223 indent(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
1225 Rune *a, *r;
1226 Window *w;
1227 int na, len, autoindent;
1229 USED(_0);
1230 USED(_1);
1231 USED(_2);
1233 w = nil;
1234 if(et!=nil && et->w!=nil)
1235 w = et->w;
1236 autoindent = IError;
1237 getarg(argt, FALSE, TRUE, &r, &len);
1238 if(r!=nil && len>0)
1239 autoindent = indentval(r, len);
1240 else{
1241 a = findbl(arg, narg, &na);
1242 if(a != arg)
1243 autoindent = indentval(arg, narg-na);
1245 if(w != nil){
1246 switch(autoindent){
1247 case Ion:
1248 case Ioff:
1249 w->autoindent = autoindent;
1250 break;
1251 case IGlobal:
1252 w->autoindent = globalautoindent;
1253 break;
1258 void
1259 tab(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
1261 Rune *a, *r;
1262 Window *w;
1263 int na, len, tab;
1264 char *p;
1266 USED(_0);
1267 USED(_1);
1268 USED(_2);
1270 if(et==nil || et->w==nil)
1271 return;
1272 w = et->w;
1273 getarg(argt, FALSE, TRUE, &r, &len);
1274 tab = 0;
1275 if(r!=nil && len>0){
1276 p = runetobyte(r, len);
1277 if('0'<=p[0] && p[0]<='9')
1278 tab = atoi(p);
1279 free(p);
1280 }else{
1281 a = findbl(arg, narg, &na);
1282 if(a != arg){
1283 p = runetobyte(arg, narg-na);
1284 if('0'<=p[0] && p[0]<='9')
1285 tab = atoi(p);
1286 free(p);
1289 if(tab > 0){
1290 if(w->body.tabstop != tab){
1291 w->body.tabstop = tab;
1292 winresize(w, w->r, TRUE, TRUE);
1294 }else
1295 warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop);
1298 void
1299 runproc(void *argvp)
1301 /* args: */
1302 Window *win;
1303 char *s;
1304 Rune *rdir;
1305 int ndir;
1306 int newns;
1307 char *argaddr;
1308 char *arg;
1309 Command *c;
1310 Channel *cpid;
1311 int iseditcmd;
1312 /* end of args */
1313 char *e, *t, *name, *filename, *dir, **av, *news;
1314 Rune r, **incl;
1315 int ac, w, inarg, i, n, fd, nincl, winid;
1316 int sfd[3];
1317 int pipechar;
1318 char buf[512];
1319 int olddir;
1320 int ret;
1321 /*static void *parg[2]; */
1322 char *rcarg[4];
1323 void **argv;
1324 CFsys *fs;
1326 threadsetname("runproc");
1328 argv = argvp;
1329 win = argv[0];
1330 s = argv[1];
1331 rdir = argv[2];
1332 ndir = (uintptr)argv[3];
1333 newns = (uintptr)argv[4];
1334 argaddr = argv[5];
1335 arg = argv[6];
1336 c = argv[7];
1337 cpid = argv[8];
1338 iseditcmd = (uintptr)argv[9];
1339 free(argv);
1341 t = s;
1342 while(*t==' ' || *t=='\n' || *t=='\t')
1343 t++;
1344 for(e=t; *e; e++)
1345 if(*e==' ' || *e=='\n' || *e=='\t' )
1346 break;
1347 name = emalloc((e-t)+2);
1348 memmove(name, t, e-t);
1349 name[e-t] = 0;
1350 e = utfrrune(name, '/');
1351 if(e)
1352 memmove(name, e+1, strlen(e+1)+1); /* strcpy but overlaps */
1353 strcat(name, " "); /* add blank here for ease in waittask */
1354 c->name = bytetorune(name, &c->nname);
1355 free(name);
1356 pipechar = 0;
1357 if(*t=='<' || *t=='|' || *t=='>')
1358 pipechar = *t++;
1359 c->iseditcmd = iseditcmd;
1360 c->text = s;
1361 if(newns){
1362 nincl = 0;
1363 incl = nil;
1364 if(win){
1365 filename = smprint("%.*S", win->body.file->nname, win->body.file->name);
1366 nincl = win->nincl;
1367 if(nincl > 0){
1368 incl = emalloc(nincl*sizeof(Rune*));
1369 for(i=0; i<nincl; i++){
1370 n = runestrlen(win->incl[i]);
1371 incl[i] = runemalloc(n+1);
1372 runemove(incl[i], win->incl[i], n);
1375 winid = win->id;
1376 }else{
1377 filename = nil;
1378 winid = 0;
1379 if(activewin)
1380 winid = activewin->id;
1382 rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG);
1383 sprint(buf, "%d", winid);
1384 putenv("winid", buf);
1386 if(filename){
1387 putenv("%", filename);
1388 free(filename);
1390 c->md = fsysmount(rdir, ndir, incl, nincl);
1391 if(c->md == nil){
1392 fprint(2, "child: can't allocate mntdir: %r\n");
1393 threadexits("fsysmount");
1395 sprint(buf, "%d", c->md->id);
1396 if((fs = nsmount("acme", buf)) == nil){
1397 fprint(2, "child: can't mount acme: %r\n");
1398 fsysdelid(c->md);
1399 c->md = nil;
1400 threadexits("nsmount");
1402 if(winid>0 && (pipechar=='|' || pipechar=='>')){
1403 sprint(buf, "%d/rdsel", winid);
1404 sfd[0] = fsopenfd(fs, buf, OREAD);
1405 }else
1406 sfd[0] = open("/dev/null", OREAD);
1407 if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){
1408 if(iseditcmd){
1409 if(winid > 0)
1410 sprint(buf, "%d/editout", winid);
1411 else
1412 sprint(buf, "editout");
1413 }else
1414 sprint(buf, "%d/wrsel", winid);
1415 sfd[1] = fsopenfd(fs, buf, OWRITE);
1416 sfd[2] = fsopenfd(fs, "cons", OWRITE);
1417 }else{
1418 sfd[1] = fsopenfd(fs, "cons", OWRITE);
1419 sfd[2] = sfd[1];
1421 fsunmount(fs);
1422 }else{
1423 rfork(RFFDG|RFNOTEG);
1424 fsysclose();
1425 sfd[0] = open("/dev/null", OREAD);
1426 sfd[1] = open("/dev/null", OWRITE);
1427 sfd[2] = dup(erroutfd, -1);
1429 if(win)
1430 winclose(win);
1432 if(argaddr)
1433 putenv("acmeaddr", argaddr);
1434 if(strlen(t) > sizeof buf-10) /* may need to print into stack */
1435 goto Hard;
1436 inarg = FALSE;
1437 for(e=t; *e; e+=w){
1438 w = chartorune(&r, e);
1439 if(r==' ' || r=='\t')
1440 continue;
1441 if(r < ' ')
1442 goto Hard;
1443 if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r))
1444 goto Hard;
1445 inarg = TRUE;
1447 if(!inarg)
1448 goto Fail;
1450 ac = 0;
1451 av = nil;
1452 inarg = FALSE;
1453 for(e=t; *e; e+=w){
1454 w = chartorune(&r, e);
1455 if(r==' ' || r=='\t'){
1456 inarg = FALSE;
1457 *e = 0;
1458 continue;
1460 if(!inarg){
1461 inarg = TRUE;
1462 av = realloc(av, (ac+1)*sizeof(char**));
1463 av[ac++] = e;
1466 av = realloc(av, (ac+2)*sizeof(char**));
1467 av[ac++] = arg;
1468 av[ac] = nil;
1469 c->av = av;
1472 * clumsy -- we're not running in a separate thread
1473 * so we have to save the current directory and put
1474 * it back when we're done. if this gets to be a regular
1475 * thing we could change threadexec to take a directory too.
1477 olddir = -1;
1478 if(rdir != nil){
1479 olddir = open(".", OREAD);
1480 dir = runetobyte(rdir, ndir);
1481 chdir(dir); /* ignore error: probably app. window */
1482 free(dir);
1484 ret = threadspawn(sfd, av[0], av);
1485 if(olddir >= 0){
1486 fchdir(olddir);
1487 close(olddir);
1489 if(ret >= 0){
1490 if(cpid)
1491 sendul(cpid, ret);
1492 threadexits("");
1494 /* libthread uses execvp so no need to do this */
1495 #if 0
1496 e = av[0];
1497 if(e[0]=='/' || (e[0]=='.' && e[1]=='/'))
1498 goto Fail;
1499 if(cputype){
1500 sprint(buf, "%s/%s", cputype, av[0]);
1501 procexec(cpid, sfd, buf, av);
1503 sprint(buf, "/bin/%s", av[0]);
1504 procexec(cpid, sfd, buf, av);
1505 #endif
1506 goto Fail;
1508 Hard:
1510 * ugly: set path = (. $cputype /bin)
1511 * should honor $path if unusual.
1513 if(cputype){
1514 n = 0;
1515 memmove(buf+n, ".", 2);
1516 n += 2;
1517 i = strlen(cputype)+1;
1518 memmove(buf+n, cputype, i);
1519 n += i;
1520 memmove(buf+n, "/bin", 5);
1521 n += 5;
1522 fd = create("/env/path", OWRITE, 0666);
1523 write(fd, buf, n);
1524 close(fd);
1527 if(arg){
1528 news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1);
1529 if(news){
1530 sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */
1531 free(s);
1532 t = news;
1533 c->text = news;
1536 olddir = -1;
1537 if(rdir != nil){
1538 olddir = open(".", OREAD);
1539 dir = runetobyte(rdir, ndir);
1540 chdir(dir); /* ignore error: probably app. window */
1541 free(dir);
1543 rcarg[0] = "rc";
1544 rcarg[1] = "-c";
1545 rcarg[2] = t;
1546 rcarg[3] = nil;
1547 ret = threadspawn(sfd, rcarg[0], rcarg);
1548 if(olddir >= 0){
1549 fchdir(olddir);
1550 close(olddir);
1552 if(ret >= 0){
1553 if(cpid)
1554 sendul(cpid, ret);
1555 threadexits(nil);
1557 warning(nil, "exec rc: %r\n");
1559 Fail:
1560 /* threadexec hasn't happened, so send a zero */
1561 close(sfd[0]);
1562 close(sfd[1]);
1563 if(sfd[2] != sfd[1])
1564 close(sfd[2]);
1565 sendul(cpid, 0);
1566 threadexits(nil);
1569 void
1570 runwaittask(void *v)
1572 Command *c;
1573 Channel *cpid;
1574 void **a;
1576 threadsetname("runwaittask");
1577 a = v;
1578 c = a[0];
1579 cpid = a[1];
1580 free(a);
1582 c->pid = recvul(cpid);
1583 while(c->pid == ~0);
1584 free(c->av);
1585 if(c->pid != 0) /* successful exec */
1586 sendp(ccommand, c);
1587 else{
1588 if(c->iseditcmd)
1589 sendul(cedit, 0);
1590 free(c->name);
1591 free(c->text);
1592 free(c);
1594 chanfree(cpid);
1597 void
1598 run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd)
1600 void **arg;
1601 Command *c;
1602 Channel *cpid;
1604 if(s == nil)
1605 return;
1607 arg = emalloc(10*sizeof(void*));
1608 c = emalloc(sizeof *c);
1609 cpid = chancreate(sizeof(ulong), 0);
1610 chansetname(cpid, "cpid %s", s);
1611 arg[0] = win;
1612 arg[1] = s;
1613 arg[2] = rdir;
1614 arg[3] = (void*)(uintptr)ndir;
1615 arg[4] = (void*)(uintptr)newns;
1616 arg[5] = argaddr;
1617 arg[6] = xarg;
1618 arg[7] = c;
1619 arg[8] = cpid;
1620 arg[9] = (void*)(uintptr)iseditcmd;
1621 threadcreate(runproc, arg, STACK);
1622 /* mustn't block here because must be ready to answer mount() call in run() */
1623 arg = emalloc(2*sizeof(void*));
1624 arg[0] = c;
1625 arg[1] = cpid;
1626 threadcreate(runwaittask, arg, STACK);