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