plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

exec.c (37635B)


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