xref: /inferno-os/appl/cmd/sh/std.b (revision 66f5808b81b1df84bc57c4f7b9d487201bc162fb)
137da2899SCharles.Forsythimplement Shellbuiltin;
237da2899SCharles.Forsyth
337da2899SCharles.Forsythinclude "sys.m";
437da2899SCharles.Forsyth	sys: Sys;
537da2899SCharles.Forsythinclude "draw.m";
637da2899SCharles.Forsythinclude "sh.m";
737da2899SCharles.Forsyth	sh: Sh;
837da2899SCharles.Forsyth	Listnode, Context: import sh;
937da2899SCharles.Forsyth	myself: Shellbuiltin;
1037da2899SCharles.Forsythinclude "filepat.m";
1137da2899SCharles.Forsyth	filepat: Filepat;
1237da2899SCharles.Forsythinclude "bufio.m";
1337da2899SCharles.Forsyth	bufio: Bufio;
1437da2899SCharles.Forsyth	Iobuf: import bufio;
1537da2899SCharles.Forsyth
1637da2899SCharles.Forsythbuiltinnames := array[] of {
1737da2899SCharles.Forsyth	"if", "while", "~", "!", "apply", "for",
1837da2899SCharles.Forsyth	"status", "pctl", "fn", "subfn", "and", "or",
1937da2899SCharles.Forsyth	"raise", "rescue", "flag", "getlines", "no",
2037da2899SCharles.Forsyth};
2137da2899SCharles.Forsyth
2237da2899SCharles.Forsythsbuiltinnames := array[] of {
2337da2899SCharles.Forsyth	"hd", "tl", "index", "split", "join", "pid", "parse", "env", "pipe",
2437da2899SCharles.Forsyth};
2537da2899SCharles.Forsyth
2637da2899SCharles.Forsythinitbuiltin(ctxt: ref Context, shmod: Sh): string
2737da2899SCharles.Forsyth{
2837da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
2937da2899SCharles.Forsyth	sh = shmod;
3037da2899SCharles.Forsyth	myself = load Shellbuiltin "$self";
3137da2899SCharles.Forsyth	if (myself == nil)
3237da2899SCharles.Forsyth		ctxt.fail("bad module", sys->sprint("std: cannot load self: %r"));
3337da2899SCharles.Forsyth	filepat = load Filepat Filepat->PATH;
3437da2899SCharles.Forsyth	if (filepat == nil)
3537da2899SCharles.Forsyth		ctxt.fail("bad module",
3637da2899SCharles.Forsyth			sys->sprint("std: cannot load: %s: %r", Filepat->PATH));
3737da2899SCharles.Forsyth	bufio = load Bufio Bufio->PATH;
3837da2899SCharles.Forsyth	if (bufio == nil)
3937da2899SCharles.Forsyth		ctxt.fail("bad module",
4037da2899SCharles.Forsyth			sys->sprint("std: cannot load: %s: %r", Bufio->PATH));
4137da2899SCharles.Forsyth	names := builtinnames;
4237da2899SCharles.Forsyth 	for (i := 0; i < len names; i++)
4337da2899SCharles.Forsyth		ctxt.addbuiltin(names[i], myself);
4437da2899SCharles.Forsyth	names = sbuiltinnames;
4537da2899SCharles.Forsyth	for (i = 0; i < len names; i++)
4637da2899SCharles.Forsyth		ctxt.addsbuiltin(names[i], myself);
4737da2899SCharles.Forsyth	env := ctxt.envlist();
4837da2899SCharles.Forsyth	for (; env != nil; env = tl env) {
4937da2899SCharles.Forsyth		(name, val) := hd env;
5037da2899SCharles.Forsyth		if (len name > 3 && name[0:3] == "fn-")
5137da2899SCharles.Forsyth			fndef(ctxt, name[3:], val, 0);
5237da2899SCharles.Forsyth		if (len name > 4 && name[0:4] == "sfn-")
5337da2899SCharles.Forsyth			fndef(ctxt, name[4:], val, 1);
5437da2899SCharles.Forsyth	}
5537da2899SCharles.Forsyth	return nil;
5637da2899SCharles.Forsyth}
5737da2899SCharles.Forsyth
5837da2899SCharles.Forsythwhatis(c: ref Sh->Context, sh: Sh, name: string, wtype: int): string
5937da2899SCharles.Forsyth{
6037da2899SCharles.Forsyth	ename, fname: string;
6137da2899SCharles.Forsyth	case wtype {
6237da2899SCharles.Forsyth	BUILTIN =>
6337da2899SCharles.Forsyth		(ename, fname) = ("fn-", "fn ");
6437da2899SCharles.Forsyth	SBUILTIN =>
6537da2899SCharles.Forsyth		(ename, fname) = ("sfn-", "subfn ");
6637da2899SCharles.Forsyth	OTHER =>
6737da2899SCharles.Forsyth		return nil;
6837da2899SCharles.Forsyth	}
6937da2899SCharles.Forsyth
7037da2899SCharles.Forsyth	val := c.get(ename + name);
7137da2899SCharles.Forsyth	if (val != nil)
7237da2899SCharles.Forsyth		return fname + name + " " + sh->quoted(hd val :: nil, 0);
7337da2899SCharles.Forsyth	return nil;
7437da2899SCharles.Forsyth}
7537da2899SCharles.Forsyth
7637da2899SCharles.Forsythgetself(): Shellbuiltin
7737da2899SCharles.Forsyth{
7837da2899SCharles.Forsyth	return myself;
7937da2899SCharles.Forsyth}
8037da2899SCharles.Forsyth
8137da2899SCharles.Forsythrunbuiltin(c: ref Sh->Context, nil: Sh,
8237da2899SCharles.Forsyth			cmd: list of ref Sh->Listnode, last: int): string
8337da2899SCharles.Forsyth{
8437da2899SCharles.Forsyth	status: string;
8537da2899SCharles.Forsyth	name := (hd cmd).word;
8637da2899SCharles.Forsyth	val := c.get("fn-" + name);
8737da2899SCharles.Forsyth	if (val != nil)
8837da2899SCharles.Forsyth		return c.run(hd val :: tl cmd, last);
8937da2899SCharles.Forsyth	case name {
9037da2899SCharles.Forsyth	"if" =>		status = builtin_if(c, cmd, last);
9137da2899SCharles.Forsyth	"while" =>		status = builtin_while(c, cmd, last);
9237da2899SCharles.Forsyth	"and" =>		status = builtin_and(c, cmd, last);
9337da2899SCharles.Forsyth	"apply" =>	status = builtin_apply(c, cmd, last);
9437da2899SCharles.Forsyth	"for" =>		status = builtin_for(c, cmd, last);
9537da2899SCharles.Forsyth	"or" =>		status = builtin_or(c, cmd, last);
9637da2899SCharles.Forsyth	"!" =>		status = builtin_not(c, cmd, last);
9737da2899SCharles.Forsyth	"fn" =>		status = builtin_fn(c, cmd, last, 0);
9837da2899SCharles.Forsyth	"subfn" =>	status = builtin_fn(c, cmd, last, 1);
9937da2899SCharles.Forsyth	"~" =>		status = builtin_twiddle(c, cmd, last);
10037da2899SCharles.Forsyth	"status" =>	status = builtin_status(c, cmd, last);
10137da2899SCharles.Forsyth	"pctl" =>		status = builtin_pctl(c, cmd, last);
10237da2899SCharles.Forsyth	"raise" =>		status = builtin_raise(c, cmd, last);
10337da2899SCharles.Forsyth	"rescue" =>	status = builtin_rescue(c, cmd, last);
10437da2899SCharles.Forsyth	"flag" =>		status = builtin_flag(c, cmd, last);
10537da2899SCharles.Forsyth	"getlines" =>	status = builtin_getlines(c, cmd, last);
10637da2899SCharles.Forsyth	"no" =>		status = builtin_no(c, cmd, last);
10737da2899SCharles.Forsyth	}
10837da2899SCharles.Forsyth	return status;
10937da2899SCharles.Forsyth}
11037da2899SCharles.Forsyth
11137da2899SCharles.Forsythrunsbuiltin(c: ref Sh->Context, nil: Sh,
11237da2899SCharles.Forsyth			cmd: list of ref Sh->Listnode): list of ref Listnode
11337da2899SCharles.Forsyth{
11437da2899SCharles.Forsyth	name := (hd cmd).word;
11537da2899SCharles.Forsyth	val := c.get("sfn-" + name);
11637da2899SCharles.Forsyth	if (val != nil)
11737da2899SCharles.Forsyth		return runsubfn(c, val, tl cmd);
11837da2899SCharles.Forsyth	case name {
11937da2899SCharles.Forsyth	"pid" =>
12037da2899SCharles.Forsyth		return ref Listnode(nil, string sys->pctl(0, nil)) :: nil;
12137da2899SCharles.Forsyth	"hd" =>
12237da2899SCharles.Forsyth		if (tl cmd == nil)
12337da2899SCharles.Forsyth			return nil;
12437da2899SCharles.Forsyth		return hd tl cmd :: nil;
12537da2899SCharles.Forsyth	"tl" =>
12637da2899SCharles.Forsyth		if (tl cmd == nil)
12737da2899SCharles.Forsyth			return nil;
12837da2899SCharles.Forsyth		return tl tl cmd;
12937da2899SCharles.Forsyth	"index" =>
13037da2899SCharles.Forsyth		return sbuiltin_index(c, cmd);
13137da2899SCharles.Forsyth	"split" =>
13237da2899SCharles.Forsyth		return sbuiltin_split(c, cmd);
13337da2899SCharles.Forsyth	"join" =>
13437da2899SCharles.Forsyth		return sbuiltin_join(c, cmd);
13537da2899SCharles.Forsyth	"parse" =>
13637da2899SCharles.Forsyth		return sbuiltin_parse(c, cmd);
13737da2899SCharles.Forsyth	"env" =>
13837da2899SCharles.Forsyth		return sbuiltin_env(c, cmd);
13937da2899SCharles.Forsyth	"pipe" =>
14037da2899SCharles.Forsyth		return sbuiltin_pipe(c, cmd);
14137da2899SCharles.Forsyth	}
14237da2899SCharles.Forsyth	return nil;
14337da2899SCharles.Forsyth}
14437da2899SCharles.Forsyth
14537da2899SCharles.Forsythrunsubfn(ctxt: ref Context, body, args: list of ref Listnode): list of ref Listnode
14637da2899SCharles.Forsyth{
14737da2899SCharles.Forsyth	if (body == nil)
14837da2899SCharles.Forsyth		return nil;
14937da2899SCharles.Forsyth	ctxt.push();
15037da2899SCharles.Forsyth	{
15137da2899SCharles.Forsyth		ctxt.setlocal("result", nil);
15237da2899SCharles.Forsyth		ctxt.run(hd body :: args, 0);
15337da2899SCharles.Forsyth		result := ctxt.get("result");
15437da2899SCharles.Forsyth		ctxt.pop();
15537da2899SCharles.Forsyth		return result;
15637da2899SCharles.Forsyth	} exception e {
15737da2899SCharles.Forsyth	"fail:*" =>
15837da2899SCharles.Forsyth		ctxt.pop();
15937da2899SCharles.Forsyth		raise e;
16037da2899SCharles.Forsyth	}
16137da2899SCharles.Forsyth}
16237da2899SCharles.Forsyth
16337da2899SCharles.Forsythsbuiltin_index(ctxt: ref Context, val: list of ref Listnode): list of ref Listnode
16437da2899SCharles.Forsyth{
16537da2899SCharles.Forsyth	if (len val < 2 || (hd tl val).word == nil)
16637da2899SCharles.Forsyth		builtinusage(ctxt, "index num list");
16737da2899SCharles.Forsyth	k := int (hd tl val).word - 1;
16837da2899SCharles.Forsyth	val = tl tl val;
16937da2899SCharles.Forsyth	for (; k > 0 && val != nil; k--)
17037da2899SCharles.Forsyth		val = tl val;
17137da2899SCharles.Forsyth	if (val != nil)
17237da2899SCharles.Forsyth		val = hd val :: nil;
17337da2899SCharles.Forsyth	return val;
17437da2899SCharles.Forsyth}
17537da2899SCharles.Forsyth
17637da2899SCharles.Forsyth# return a parsed version of a string, raising a "parse error" exception if
17737da2899SCharles.Forsyth# it fails. the string must be a braced command block.
17837da2899SCharles.Forsythsbuiltin_parse(ctxt: ref Context, args: list of ref Listnode): list of ref Listnode
17937da2899SCharles.Forsyth{
18037da2899SCharles.Forsyth	if (len args != 2)
18137da2899SCharles.Forsyth		builtinusage(ctxt, "parse arg");
18237da2899SCharles.Forsyth	args = tl args;
18337da2899SCharles.Forsyth	if ((hd args).cmd != nil)
18437da2899SCharles.Forsyth		return ref Listnode((hd args).cmd, nil) :: nil;
18537da2899SCharles.Forsyth	w := (hd args).word;
18637da2899SCharles.Forsyth	if (w == nil || w[0] != '{')	#}
18737da2899SCharles.Forsyth		ctxt.fail("parse error", "parse: argument must be a braced block");
18837da2899SCharles.Forsyth	(n, err) := sh->parse(w);
18937da2899SCharles.Forsyth	if (err != nil)
19037da2899SCharles.Forsyth		ctxt.fail("parse error", "parse: " + err);
19137da2899SCharles.Forsyth	return ref Listnode(n, nil) :: nil;
19237da2899SCharles.Forsyth}
19337da2899SCharles.Forsyth
19437da2899SCharles.Forsythsbuiltin_env(ctxt: ref Context, nil: list of ref Listnode): list of ref Listnode
19537da2899SCharles.Forsyth{
19637da2899SCharles.Forsyth	vl: list of string;
19737da2899SCharles.Forsyth	for (e := ctxt.envlist(); e != nil; e = tl e) {
19837da2899SCharles.Forsyth		(n, v) := hd e;
19937da2899SCharles.Forsyth		if (v != nil)		# XXX this is debatable... someone might want to see null local vars.
20037da2899SCharles.Forsyth			vl = n :: vl;
20137da2899SCharles.Forsyth	}
20237da2899SCharles.Forsyth	return sh->stringlist2list(vl);
20337da2899SCharles.Forsyth}
20437da2899SCharles.Forsyth
20537da2899SCharles.Forsythword(n: ref Listnode): string
20637da2899SCharles.Forsyth{
20737da2899SCharles.Forsyth	if (n.word != nil)
20837da2899SCharles.Forsyth		return n.word;
20937da2899SCharles.Forsyth	if (n.cmd != nil)
21037da2899SCharles.Forsyth		n.word = sh->cmd2string(n.cmd);
21137da2899SCharles.Forsyth	return n.word;
21237da2899SCharles.Forsyth}
21337da2899SCharles.Forsyth
21437da2899SCharles.Forsyth# usage: split [separators] value
21537da2899SCharles.Forsythsbuiltin_split(ctxt: ref Context, args: list of ref Listnode): list of ref Listnode
21637da2899SCharles.Forsyth{
21737da2899SCharles.Forsyth	n := len args;
21837da2899SCharles.Forsyth	if (n < 2  || n > 3)
21937da2899SCharles.Forsyth		builtinusage(ctxt, "split [separators] value");
22037da2899SCharles.Forsyth	seps: string;
22137da2899SCharles.Forsyth	if (n == 2) {
22237da2899SCharles.Forsyth		ifs := ctxt.get("ifs");
22337da2899SCharles.Forsyth		if (ifs == nil)
22437da2899SCharles.Forsyth			ctxt.fail("usage", "split: $ifs not set");
22537da2899SCharles.Forsyth		seps = word(hd ifs);
22637da2899SCharles.Forsyth	} else {
22737da2899SCharles.Forsyth		args = tl args;
22837da2899SCharles.Forsyth		seps = word(hd args);
22937da2899SCharles.Forsyth	}
23037da2899SCharles.Forsyth	(nil, toks) := sys->tokenize(word(hd tl args), seps);
23137da2899SCharles.Forsyth	return sh->stringlist2list(toks);
23237da2899SCharles.Forsyth}
23337da2899SCharles.Forsyth
23437da2899SCharles.Forsythsbuiltin_join(ctxt: ref Context, args: list of ref Listnode): list of ref Listnode
23537da2899SCharles.Forsyth{
23637da2899SCharles.Forsyth	args = tl args;
23737da2899SCharles.Forsyth	if (args == nil)
23837da2899SCharles.Forsyth		builtinusage(ctxt, "join separator [arg...]");
23937da2899SCharles.Forsyth	seps := word(hd args);
24037da2899SCharles.Forsyth	if (tl args == nil)
24137da2899SCharles.Forsyth		return ref Listnode(nil, nil) :: nil;
24237da2899SCharles.Forsyth	s := word(hd tl args);
24337da2899SCharles.Forsyth	for (args = tl tl args; args != nil; args = tl args)
24437da2899SCharles.Forsyth		s += seps + word(hd args);
24537da2899SCharles.Forsyth	return ref Listnode(nil, s) :: nil;
24637da2899SCharles.Forsyth}
24737da2899SCharles.Forsyth
24837da2899SCharles.Forsythbuiltin_fn(ctxt: ref Context, args: list of ref Listnode, nil: int, issub: int): string
24937da2899SCharles.Forsyth{
25037da2899SCharles.Forsyth	n := len args;
25137da2899SCharles.Forsyth	title := (hd args).word;
25237da2899SCharles.Forsyth	if (n < 2)
25337da2899SCharles.Forsyth		builtinusage(ctxt, title + " [name...] [{body}]");
25437da2899SCharles.Forsyth	for (al := tl args; tl al != nil; al = tl al)
25537da2899SCharles.Forsyth		if ((hd al).cmd != nil)
25637da2899SCharles.Forsyth			builtinusage(ctxt, title + " [name...] [{body}]");
25737da2899SCharles.Forsyth	if ((hd al).cmd != nil) {
25837da2899SCharles.Forsyth		cmd := hd al :: nil;
25937da2899SCharles.Forsyth		for (al = tl args; tl al != nil; al = tl al)
26037da2899SCharles.Forsyth			fndef(ctxt, (hd al).word, cmd, issub);
26137da2899SCharles.Forsyth	} else {
26237da2899SCharles.Forsyth		for (al = tl args; al != nil; al = tl al)
26337da2899SCharles.Forsyth			fnundef(ctxt, (hd al).word, issub);
26437da2899SCharles.Forsyth	}
26537da2899SCharles.Forsyth	return nil;
26637da2899SCharles.Forsyth}
26737da2899SCharles.Forsyth
26837da2899SCharles.Forsythfndef(ctxt: ref Context, name: string, cmd: list of ref Listnode, issub: int)
26937da2899SCharles.Forsyth{
27037da2899SCharles.Forsyth	if (cmd == nil)
27137da2899SCharles.Forsyth		return;
27237da2899SCharles.Forsyth	if (issub) {
27337da2899SCharles.Forsyth		ctxt.set("sfn-" + name, cmd);
27437da2899SCharles.Forsyth		ctxt.addsbuiltin(name, myself);
27537da2899SCharles.Forsyth	} else {
27637da2899SCharles.Forsyth		ctxt.set("fn-" + name, cmd);
27737da2899SCharles.Forsyth		ctxt.addbuiltin(name, myself);
27837da2899SCharles.Forsyth	}
27937da2899SCharles.Forsyth}
28037da2899SCharles.Forsyth
28137da2899SCharles.Forsythfnundef(ctxt: ref Context, name: string, issub: int)
28237da2899SCharles.Forsyth{
28337da2899SCharles.Forsyth	if (issub) {
28437da2899SCharles.Forsyth		ctxt.set("sfn-" + name, nil);
28537da2899SCharles.Forsyth		ctxt.removesbuiltin(name, myself);
28637da2899SCharles.Forsyth	} else {
28737da2899SCharles.Forsyth		ctxt.set("fn-" + name, nil);
28837da2899SCharles.Forsyth		ctxt.removebuiltin(name, myself);
28937da2899SCharles.Forsyth	}
29037da2899SCharles.Forsyth}
29137da2899SCharles.Forsyth
29237da2899SCharles.Forsythbuiltin_flag(ctxt: ref Context, args: list of ref Listnode, nil: int): string
29337da2899SCharles.Forsyth{
29437da2899SCharles.Forsyth	n := len args;
29537da2899SCharles.Forsyth	if (n < 2 || n > 3 || len (hd tl args).word != 1)
29637da2899SCharles.Forsyth		builtinusage(ctxt, "flag [vxei] [+-]");
29737da2899SCharles.Forsyth	flag := (hd tl args).word[0];
29837da2899SCharles.Forsyth	p := "";
29937da2899SCharles.Forsyth	if (n == 3)
30037da2899SCharles.Forsyth		p = (hd tl tl args).word;
30137da2899SCharles.Forsyth	mask := 0;
30237da2899SCharles.Forsyth	case flag {
30337da2899SCharles.Forsyth	'v' =>	mask = Context.VERBOSE;
30437da2899SCharles.Forsyth	'x' =>	mask = Context.EXECPRINT;
30537da2899SCharles.Forsyth	'e' =>	mask = Context.ERROREXIT;
30637da2899SCharles.Forsyth	'i' =>		mask = Context.INTERACTIVE;
30737da2899SCharles.Forsyth	* =>		builtinusage(ctxt, "flag [vxei] [+-]");
30837da2899SCharles.Forsyth	}
30937da2899SCharles.Forsyth	case p {
31037da2899SCharles.Forsyth	"" =>		if (ctxt.options() & mask)
31137da2899SCharles.Forsyth				return nil;
31237da2899SCharles.Forsyth			return "not set";
31337da2899SCharles.Forsyth	"-" =>	ctxt.setoptions(mask, 0);
31437da2899SCharles.Forsyth	"+" =>	ctxt.setoptions(mask, 1);
31537da2899SCharles.Forsyth	* =>		builtinusage(ctxt, "flag [vxei] [+-]");
31637da2899SCharles.Forsyth	}
31737da2899SCharles.Forsyth	return nil;
31837da2899SCharles.Forsyth}
31937da2899SCharles.Forsyth
32037da2899SCharles.Forsythbuiltin_no(nil: ref Context, args: list of ref Listnode, nil: int): string
32137da2899SCharles.Forsyth{
32237da2899SCharles.Forsyth	if (tl args != nil)
32337da2899SCharles.Forsyth		return "yes";
32437da2899SCharles.Forsyth	return nil;
32537da2899SCharles.Forsyth}
32637da2899SCharles.Forsyth
32737da2899SCharles.Forsythiscmd(n: ref Listnode): int
32837da2899SCharles.Forsyth{
32937da2899SCharles.Forsyth	return n.cmd != nil || (n.word != nil && n.word[0] == '{');
33037da2899SCharles.Forsyth}
33137da2899SCharles.Forsyth
33237da2899SCharles.Forsythbuiltin_if(ctxt: ref Context, args: list of ref Listnode, nil: int): string
33337da2899SCharles.Forsyth{
33437da2899SCharles.Forsyth	args = tl args;
33537da2899SCharles.Forsyth	nargs := len args;
33637da2899SCharles.Forsyth	if (nargs < 2)
33737da2899SCharles.Forsyth		builtinusage(ctxt, "if {cond} {action} [{cond} {action}]... [{elseaction}]");
33837da2899SCharles.Forsyth
33937da2899SCharles.Forsyth	status: string;
34037da2899SCharles.Forsyth	dolstar := ctxt.get("*");
34137da2899SCharles.Forsyth	while (args != nil) {
34237da2899SCharles.Forsyth		cmd: ref Listnode = nil;
34337da2899SCharles.Forsyth		if (tl args == nil) {
34437da2899SCharles.Forsyth			cmd = hd args;
34537da2899SCharles.Forsyth			args = tl args;
34637da2899SCharles.Forsyth		} else {
34737da2899SCharles.Forsyth			if (!iscmd(hd args))
34837da2899SCharles.Forsyth				builtinusage(ctxt, "if [{cond} {action}]... [{elseaction}]");
34937da2899SCharles.Forsyth
35037da2899SCharles.Forsyth			status = ctxt.run(hd args :: dolstar, 0);
35137da2899SCharles.Forsyth			if (status == nil) {
35237da2899SCharles.Forsyth				cmd = hd tl args;
35337da2899SCharles.Forsyth				args = nil;
35437da2899SCharles.Forsyth			} else
35537da2899SCharles.Forsyth				args = tl tl args;
35637da2899SCharles.Forsyth			setstatus(ctxt, status);
35737da2899SCharles.Forsyth		}
35837da2899SCharles.Forsyth		if (cmd != nil) {
35937da2899SCharles.Forsyth			if (!iscmd(cmd))
36037da2899SCharles.Forsyth				builtinusage(ctxt, "if [{cond} {action}]... [{elseaction}]");
36137da2899SCharles.Forsyth
36237da2899SCharles.Forsyth			status = ctxt.run(cmd :: dolstar, 0);
36337da2899SCharles.Forsyth		}
36437da2899SCharles.Forsyth	}
36537da2899SCharles.Forsyth	return status;
36637da2899SCharles.Forsyth}
36737da2899SCharles.Forsyth
36837da2899SCharles.Forsythbuiltin_or(ctxt: ref Context, args: list of ref Listnode, nil: int): string
36937da2899SCharles.Forsyth{
37037da2899SCharles.Forsyth	s: string;
37137da2899SCharles.Forsyth	dolstar := ctxt.get("*");
37237da2899SCharles.Forsyth	for (args = tl args; args != nil; args = tl args) {
37337da2899SCharles.Forsyth		if (!iscmd(hd args))
37437da2899SCharles.Forsyth			builtinusage(ctxt, "or [{cmd} ...]");
37537da2899SCharles.Forsyth		if ((s = ctxt.run(hd args :: dolstar, 0)) == nil)
37637da2899SCharles.Forsyth			return nil;
37737da2899SCharles.Forsyth		else
37837da2899SCharles.Forsyth			setstatus(ctxt, s);
37937da2899SCharles.Forsyth	}
38037da2899SCharles.Forsyth	return s;
38137da2899SCharles.Forsyth}
38237da2899SCharles.Forsyth
38337da2899SCharles.Forsythbuiltin_and(ctxt: ref Context, args: list of ref Listnode, nil: int): string
38437da2899SCharles.Forsyth{
38537da2899SCharles.Forsyth	dolstar := ctxt.get("*");
38637da2899SCharles.Forsyth	for (args = tl args; args != nil; args = tl args) {
38737da2899SCharles.Forsyth		if (!iscmd(hd args))
38837da2899SCharles.Forsyth			builtinusage(ctxt, "and [{cmd} ...]");
38937da2899SCharles.Forsyth		if ((s := ctxt.run(hd args :: dolstar, 0)) != nil)
39037da2899SCharles.Forsyth			return s;
39137da2899SCharles.Forsyth		else
39237da2899SCharles.Forsyth			setstatus(ctxt, nil);
39337da2899SCharles.Forsyth	}
39437da2899SCharles.Forsyth	return nil;
39537da2899SCharles.Forsyth}
39637da2899SCharles.Forsyth
39737da2899SCharles.Forsythbuiltin_while(ctxt: ref Context, args: list of ref Listnode, nil: int) : string
39837da2899SCharles.Forsyth{
39937da2899SCharles.Forsyth	args = tl args;
40037da2899SCharles.Forsyth	if (len args != 2 || !iscmd(hd args) || !iscmd(hd tl args))
40137da2899SCharles.Forsyth		builtinusage(ctxt, "while {condition} {cmd}");
40237da2899SCharles.Forsyth
40337da2899SCharles.Forsyth	dolstar := ctxt.get("*");
40437da2899SCharles.Forsyth	cond := hd args :: dolstar;
40537da2899SCharles.Forsyth	action := hd tl args :: dolstar;
40637da2899SCharles.Forsyth	status := "";
40737da2899SCharles.Forsyth
40837da2899SCharles.Forsyth	for(;;){
40937da2899SCharles.Forsyth		{
41037da2899SCharles.Forsyth			while (ctxt.run(cond, 0) == nil)
41137da2899SCharles.Forsyth				status = setstatus(ctxt, ctxt.run(action, 0));
41237da2899SCharles.Forsyth			return status;
41337da2899SCharles.Forsyth		} exception e{
41437da2899SCharles.Forsyth		"fail:*" =>
41537da2899SCharles.Forsyth			if (loopexcept(e) == BREAK)
41637da2899SCharles.Forsyth				return status;
41737da2899SCharles.Forsyth		}
41837da2899SCharles.Forsyth	}
41937da2899SCharles.Forsyth}
42037da2899SCharles.Forsyth
42137da2899SCharles.Forsythbuiltin_getlines(ctxt: ref Context, argv: list of ref Listnode, nil: int) : string
42237da2899SCharles.Forsyth{
42337da2899SCharles.Forsyth	n := len argv;
42437da2899SCharles.Forsyth	if (n < 2  || n > 3)
42537da2899SCharles.Forsyth		builtinusage(ctxt, "getlines [separators] {cmd}");
42637da2899SCharles.Forsyth	argv = tl argv;
42737da2899SCharles.Forsyth	seps := "\n";
42837da2899SCharles.Forsyth	if (n == 3) {
42937da2899SCharles.Forsyth		seps = word(hd argv);
43037da2899SCharles.Forsyth		argv = tl argv;
43137da2899SCharles.Forsyth	}
43237da2899SCharles.Forsyth	if (len seps == 0)
43337da2899SCharles.Forsyth		builtinusage(ctxt, "getlines [separators] {cmd}");
43437da2899SCharles.Forsyth	if (!iscmd(hd argv))
43537da2899SCharles.Forsyth		builtinusage(ctxt, "getlines [separators] {cmd}");
43637da2899SCharles.Forsyth	cmd := hd argv :: ctxt.get("*");
43737da2899SCharles.Forsyth	stdin := bufio->fopen(sys->fildes(0), Sys->OREAD);
43837da2899SCharles.Forsyth	if (stdin == nil)
43937da2899SCharles.Forsyth		ctxt.fail("bad input", sys->sprint("getlines: cannot open stdin: %r"));
44037da2899SCharles.Forsyth	status := "";
44137da2899SCharles.Forsyth	ctxt.push();
44237da2899SCharles.Forsyth	for(;;){
44337da2899SCharles.Forsyth		{
44437da2899SCharles.Forsyth			for (;;) {
44537da2899SCharles.Forsyth				s: string;
44637da2899SCharles.Forsyth				if (len seps == 1)
44737da2899SCharles.Forsyth					s = stdin.gets(seps[0]);
44837da2899SCharles.Forsyth				else
44937da2899SCharles.Forsyth					s = stdin.gett(seps);
45037da2899SCharles.Forsyth				if (s == nil)
45137da2899SCharles.Forsyth					break;
45237da2899SCharles.Forsyth				# make sure we don't lose the last unterminated line
45337da2899SCharles.Forsyth				lastc := s[len s - 1];
45437da2899SCharles.Forsyth				if (lastc == seps[0])
45537da2899SCharles.Forsyth					s = s[0:len s - 1];
45637da2899SCharles.Forsyth				else for (i := 1; i < len seps; i++) {
45737da2899SCharles.Forsyth					if (lastc == seps[i]) {
45837da2899SCharles.Forsyth						s = s[0:len s - 1];
45937da2899SCharles.Forsyth						break;
46037da2899SCharles.Forsyth					}
46137da2899SCharles.Forsyth				}
46237da2899SCharles.Forsyth				ctxt.setlocal("line", ref Listnode(nil, s) :: nil);
46337da2899SCharles.Forsyth				status = setstatus(ctxt, ctxt.run(cmd, 0));
46437da2899SCharles.Forsyth			}
46537da2899SCharles.Forsyth			ctxt.pop();
46637da2899SCharles.Forsyth			return status;
46737da2899SCharles.Forsyth		} exception e {
46837da2899SCharles.Forsyth		"fail:*" =>
46937da2899SCharles.Forsyth			ctxt.pop();
47037da2899SCharles.Forsyth			if (loopexcept(e) == BREAK)
47137da2899SCharles.Forsyth				return status;
47237da2899SCharles.Forsyth			ctxt.push();
47337da2899SCharles.Forsyth		}
47437da2899SCharles.Forsyth	}
47537da2899SCharles.Forsyth}
47637da2899SCharles.Forsyth
47737da2899SCharles.Forsyth# usage: raise [name]
47837da2899SCharles.Forsythbuiltin_raise(ctxt: ref Context, args: list of ref Listnode, nil: int) : string
47937da2899SCharles.Forsyth{
48037da2899SCharles.Forsyth	ename: ref Listnode;
48137da2899SCharles.Forsyth	if (tl args == nil) {
48237da2899SCharles.Forsyth		e := ctxt.get("exception");
48337da2899SCharles.Forsyth		if (e == nil)
48437da2899SCharles.Forsyth			ctxt.fail("bad raise context", "raise: no exception found");
48537da2899SCharles.Forsyth		ename = (hd e);
48637da2899SCharles.Forsyth	} else
48737da2899SCharles.Forsyth		ename = hd tl args;
48837da2899SCharles.Forsyth	if (ename.word == nil && ename.cmd != nil)
48937da2899SCharles.Forsyth		ctxt.fail("bad raise context", "raise: bad exception name");
49037da2899SCharles.Forsyth	xraise("fail:" + ename.word);
49137da2899SCharles.Forsyth	return nil;
49237da2899SCharles.Forsyth}
49337da2899SCharles.Forsyth
49437da2899SCharles.Forsyth# usage: rescue pattern rescuecmd cmd
49537da2899SCharles.Forsythbuiltin_rescue(ctxt: ref Context, args: list of ref Listnode, last: int) : string
49637da2899SCharles.Forsyth{
49737da2899SCharles.Forsyth	args = tl args;
49837da2899SCharles.Forsyth	if (len args != 3 || !iscmd(hd tl args) || !iscmd(hd tl tl args))
49937da2899SCharles.Forsyth		builtinusage(ctxt, "rescue pattern {rescuecmd} {cmd}");
50037da2899SCharles.Forsyth	if ((hd args).word == nil && (hd args).cmd != nil)
50137da2899SCharles.Forsyth		ctxt.fail("usage", "rescue: bad pattern");
50237da2899SCharles.Forsyth	dolstar := ctxt.get("*");
50337da2899SCharles.Forsyth	handler := hd tl args :: dolstar;
50437da2899SCharles.Forsyth	code := hd tl tl args :: dolstar;
50537da2899SCharles.Forsyth	{
50637da2899SCharles.Forsyth		return ctxt.run(code, 0);
50737da2899SCharles.Forsyth	} exception e {
50837da2899SCharles.Forsyth	"fail:*" =>
50937da2899SCharles.Forsyth		ctxt.push();
51037da2899SCharles.Forsyth		ctxt.set("exception", ref Listnode(nil, e[5:]) :: nil);
51137da2899SCharles.Forsyth		{
51237da2899SCharles.Forsyth			status := ctxt.run(handler, last);
51337da2899SCharles.Forsyth			ctxt.pop();
51437da2899SCharles.Forsyth			return status;
515*66f5808bSforsyth		} exception {
51637da2899SCharles.Forsyth		"fail:*" =>
51737da2899SCharles.Forsyth			ctxt.pop();
51837da2899SCharles.Forsyth			raise e;
51937da2899SCharles.Forsyth		}
52037da2899SCharles.Forsyth	}
52137da2899SCharles.Forsyth}
52237da2899SCharles.Forsyth
52337da2899SCharles.Forsythbuiltin_not(ctxt: ref Context, args: list of ref Listnode, last: int): string
52437da2899SCharles.Forsyth{
52537da2899SCharles.Forsyth	# syntax: ! cmd [args...]
52637da2899SCharles.Forsyth	args = tl args;
52737da2899SCharles.Forsyth	if (args == nil || ctxt.run(args, last) == nil)
52837da2899SCharles.Forsyth		return "false";
52937da2899SCharles.Forsyth	return "";
53037da2899SCharles.Forsyth}
53137da2899SCharles.Forsyth
53237da2899SCharles.Forsythbuiltin_for(ctxt: ref Context, args: list of ref Listnode, nil: int): string
53337da2899SCharles.Forsyth{
53437da2899SCharles.Forsyth	Usage: con "for var in [item...] {cmd}";
53537da2899SCharles.Forsyth	args = tl args;
53637da2899SCharles.Forsyth	if (args == nil)
53737da2899SCharles.Forsyth		builtinusage(ctxt, Usage);
53837da2899SCharles.Forsyth	var := (hd args).word;
53937da2899SCharles.Forsyth	if (var == nil)
54037da2899SCharles.Forsyth		ctxt.fail("bad assign", "for: bad variable name");
54137da2899SCharles.Forsyth	args = tl args;
54237da2899SCharles.Forsyth	if (args == nil || (hd args).word != "in")
54337da2899SCharles.Forsyth		builtinusage(ctxt, Usage);
54437da2899SCharles.Forsyth	args = tl args;
54537da2899SCharles.Forsyth	if (args == nil)
54637da2899SCharles.Forsyth		builtinusage(ctxt, Usage);
54737da2899SCharles.Forsyth	for (eargs := args; tl eargs != nil; eargs = tl eargs)
54837da2899SCharles.Forsyth			;
54937da2899SCharles.Forsyth	cmd := hd eargs;
55037da2899SCharles.Forsyth	if (!iscmd(cmd))
55137da2899SCharles.Forsyth		builtinusage(ctxt, Usage);
55237da2899SCharles.Forsyth
55337da2899SCharles.Forsyth	status := "";
55437da2899SCharles.Forsyth	dolstar := ctxt.get("*");
55537da2899SCharles.Forsyth	for(;;){
55637da2899SCharles.Forsyth		{
55737da2899SCharles.Forsyth			for (; tl args != nil; args = tl args) {
55837da2899SCharles.Forsyth				ctxt.setlocal(var, hd args :: nil);
55937da2899SCharles.Forsyth				status = setstatus(ctxt, ctxt.run(cmd :: dolstar, 0));
56037da2899SCharles.Forsyth			}
56137da2899SCharles.Forsyth			return status;
56237da2899SCharles.Forsyth		} exception e {
56337da2899SCharles.Forsyth		"fail:*" =>
56437da2899SCharles.Forsyth			if (loopexcept(e) == BREAK)
56537da2899SCharles.Forsyth				return status;
56637da2899SCharles.Forsyth			args = tl args;
56737da2899SCharles.Forsyth		}
56837da2899SCharles.Forsyth	}
56937da2899SCharles.Forsyth}
57037da2899SCharles.Forsyth
57137da2899SCharles.ForsythCONTINUE, BREAK: con iota;
57237da2899SCharles.Forsythloopexcept(ename: string): int
57337da2899SCharles.Forsyth{
57437da2899SCharles.Forsyth	case ename[5:] {
57537da2899SCharles.Forsyth	"break" =>
57637da2899SCharles.Forsyth		return BREAK;
57737da2899SCharles.Forsyth	"continue" =>
57837da2899SCharles.Forsyth		return CONTINUE;
57937da2899SCharles.Forsyth	* =>
58037da2899SCharles.Forsyth		raise ename;
58137da2899SCharles.Forsyth	}
58237da2899SCharles.Forsyth	return 0;
58337da2899SCharles.Forsyth}
58437da2899SCharles.Forsyth
58537da2899SCharles.Forsythbuiltin_apply(ctxt: ref Context, args: list of ref Listnode, nil: int): string
58637da2899SCharles.Forsyth{
58737da2899SCharles.Forsyth	args = tl args;
58837da2899SCharles.Forsyth	if (args == nil || !iscmd(hd args))
58937da2899SCharles.Forsyth		builtinusage(ctxt, "apply {cmd} [val...]");
59037da2899SCharles.Forsyth
59137da2899SCharles.Forsyth	status := "";
59237da2899SCharles.Forsyth	cmd := hd args;
59337da2899SCharles.Forsyth	for(;;){
59437da2899SCharles.Forsyth		{
59537da2899SCharles.Forsyth			for (args = tl args; args != nil; args = tl args)
59637da2899SCharles.Forsyth				status = setstatus(ctxt, ctxt.run(cmd :: hd args :: nil, 0));
59737da2899SCharles.Forsyth
59837da2899SCharles.Forsyth			return status;
59937da2899SCharles.Forsyth		} exception e{
60037da2899SCharles.Forsyth		"fail:*" =>
60137da2899SCharles.Forsyth			if (loopexcept(e) == BREAK)
60237da2899SCharles.Forsyth				return status;
60337da2899SCharles.Forsyth		}
60437da2899SCharles.Forsyth	}
60537da2899SCharles.Forsyth}
60637da2899SCharles.Forsyth
60737da2899SCharles.Forsythbuiltin_status(nil: ref Context, args: list of ref Listnode, nil: int): string
60837da2899SCharles.Forsyth{
60937da2899SCharles.Forsyth	if (tl args != nil)
61037da2899SCharles.Forsyth		return (hd tl args).word;
61137da2899SCharles.Forsyth	return "";
61237da2899SCharles.Forsyth}
61337da2899SCharles.Forsyth
61437da2899SCharles.Forsythpctlnames := array[] of {
61537da2899SCharles.Forsyth	("newfd", Sys->NEWFD),
61637da2899SCharles.Forsyth	("forkfd", Sys->FORKFD),
61737da2899SCharles.Forsyth	("newns", Sys->NEWNS),
61837da2899SCharles.Forsyth	("forkns", Sys->FORKNS),
61937da2899SCharles.Forsyth	("newpgrp", Sys->NEWPGRP),
62037da2899SCharles.Forsyth	("nodevs", Sys->NODEVS)
62137da2899SCharles.Forsyth};
62237da2899SCharles.Forsyth
62337da2899SCharles.Forsythbuiltin_pctl(ctxt: ref Context, argv: list of ref Listnode, nil: int): string
62437da2899SCharles.Forsyth{
62537da2899SCharles.Forsyth	if (len argv < 2)
62637da2899SCharles.Forsyth		builtinusage(ctxt, "pctl option... [fdnum...]");
62737da2899SCharles.Forsyth
62837da2899SCharles.Forsyth	finalmask := 0;
62937da2899SCharles.Forsyth	fdlist: list of int;
63037da2899SCharles.Forsyth	for (argv = tl argv; argv != nil; argv = tl argv) {
63137da2899SCharles.Forsyth		w := (hd argv).word;
63237da2899SCharles.Forsyth		if (isnum(w))
63337da2899SCharles.Forsyth			fdlist = int w :: fdlist;
63437da2899SCharles.Forsyth		else {
63537da2899SCharles.Forsyth			for (i := 0; i < len pctlnames; i++) {
63637da2899SCharles.Forsyth				(name, mask) := pctlnames[i];
63737da2899SCharles.Forsyth				if (name == w) {
63837da2899SCharles.Forsyth					finalmask |= mask;
63937da2899SCharles.Forsyth					break;
64037da2899SCharles.Forsyth				}
64137da2899SCharles.Forsyth			}
64237da2899SCharles.Forsyth			if (i == len pctlnames)
64337da2899SCharles.Forsyth				ctxt.fail("usage", "pctl: unknown flag " + w);
64437da2899SCharles.Forsyth		}
64537da2899SCharles.Forsyth	}
64637da2899SCharles.Forsyth	sys->pctl(finalmask, fdlist);
64737da2899SCharles.Forsyth	return nil;
64837da2899SCharles.Forsyth}
64937da2899SCharles.Forsyth
65037da2899SCharles.Forsyth# usage: ~ value pattern...
65137da2899SCharles.Forsythbuiltin_twiddle(ctxt: ref Context, argv: list of ref Listnode, nil: int): string
65237da2899SCharles.Forsyth{
65337da2899SCharles.Forsyth	argv = tl argv;
65437da2899SCharles.Forsyth	if (argv == nil)
65537da2899SCharles.Forsyth		builtinusage(ctxt, "~ word [pattern...]");
65637da2899SCharles.Forsyth	if (tl argv == nil)
65737da2899SCharles.Forsyth		return "no match";
65837da2899SCharles.Forsyth	w := word(hd argv);
65937da2899SCharles.Forsyth
66037da2899SCharles.Forsyth	for (argv = tl argv; argv != nil; argv = tl argv)
66137da2899SCharles.Forsyth		if (filepat->match(word(hd argv), w))
66237da2899SCharles.Forsyth			return "";
66337da2899SCharles.Forsyth
66437da2899SCharles.Forsyth	return "no match";
66537da2899SCharles.Forsyth}
66637da2899SCharles.Forsyth
66737da2899SCharles.Forsyth#builtin_echo(ctxt: ref Context, argv: list of ref Listnode, nil: int): string
66837da2899SCharles.Forsyth#{
66937da2899SCharles.Forsyth#	argv = tl argv;
67037da2899SCharles.Forsyth#	nflag := 0;
67137da2899SCharles.Forsyth#	if (argv != nil && word(hd argv) == "-n") {
67237da2899SCharles.Forsyth#		nflag = 1;
67337da2899SCharles.Forsyth#		argv = tl argv;
67437da2899SCharles.Forsyth#	}
67537da2899SCharles.Forsyth#	s: string;
67637da2899SCharles.Forsyth#	if (argv != nil) {
67737da2899SCharles.Forsyth#		s = word(hd argv);
67837da2899SCharles.Forsyth#		for (argv = tl argv; argv != nil; argv = tl argv)
67937da2899SCharles.Forsyth#			s += " " + word(hd argv);
68037da2899SCharles.Forsyth#	}
68137da2899SCharles.Forsyth#	e: int;
68237da2899SCharles.Forsyth#	if (nflag)
68337da2899SCharles.Forsyth#		e = sys->print("%s", s);
68437da2899SCharles.Forsyth#	else
68537da2899SCharles.Forsyth#		e = sys->print("%s\n", s);
68637da2899SCharles.Forsyth#	if (e == -1) {
68737da2899SCharles.Forsyth#		err := sys->sprint("%r");
68837da2899SCharles.Forsyth#		if (ctxt.options() & ctxt.VERBOSE)
68937da2899SCharles.Forsyth#			sys->fprint(sys->fildes(2), "echo: write error: %s\n", err);
69037da2899SCharles.Forsyth#		return err;
69137da2899SCharles.Forsyth#	}
69237da2899SCharles.Forsyth#	return nil;
69337da2899SCharles.Forsyth#}
69437da2899SCharles.Forsyth
69537da2899SCharles.ForsythENOEXIST: con "file does not exist";
69637da2899SCharles.ForsythTMPDIR: con "/tmp/pipes";
69737da2899SCharles.Forsythsbuiltin_pipe(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode
69837da2899SCharles.Forsyth{
69937da2899SCharles.Forsyth	n: int;
70037da2899SCharles.Forsyth	if (len argv != 3 || !iscmd(hd tl tl argv))
70137da2899SCharles.Forsyth		builtinusage(ctxt, "pipe (from|to|fdnum) {cmd}");
70237da2899SCharles.Forsyth	s := (hd tl argv).word;
70337da2899SCharles.Forsyth	case s {
70437da2899SCharles.Forsyth	"from" =>
70537da2899SCharles.Forsyth		n = 1;
70637da2899SCharles.Forsyth	"to" =>
70737da2899SCharles.Forsyth		n = 0;
70837da2899SCharles.Forsyth	* =>
70937da2899SCharles.Forsyth		if (!isnum(s))
71037da2899SCharles.Forsyth			builtinusage(ctxt, "pipe (from|to|fdnum) {cmd}");
71137da2899SCharles.Forsyth		n = int s;
71237da2899SCharles.Forsyth	}
71337da2899SCharles.Forsyth	pipeid := ctxt.get("pipeid");
71437da2899SCharles.Forsyth	seq: int;
71537da2899SCharles.Forsyth	if (pipeid == nil)
71637da2899SCharles.Forsyth		seq = 0;
71737da2899SCharles.Forsyth	else
71837da2899SCharles.Forsyth		seq = int (hd pipeid).word;
71937da2899SCharles.Forsyth	id := "pipe." + string sys->pctl(0, nil) + "." + string seq;
72037da2899SCharles.Forsyth	ctxt.set("pipeid", ref Listnode(nil, string ++seq) :: nil);
72137da2899SCharles.Forsyth	mkdir(TMPDIR);
72237da2899SCharles.Forsyth	d := "/tmp/" + id + "d";
72337da2899SCharles.Forsyth	if (mkdir(d) == -1)
72437da2899SCharles.Forsyth		ctxt.fail("bad pipe", sys->sprint("pipe: cannot make %s: %r", d));
72537da2899SCharles.Forsyth	if (sys->bind("#|", d, Sys->MREPL) == -1) {
72637da2899SCharles.Forsyth		sys->remove(d);
72737da2899SCharles.Forsyth		ctxt.fail("bad pipe", sys->sprint("pipe: cannot bind pipe onto %s: %r", d));
72837da2899SCharles.Forsyth	}
72937da2899SCharles.Forsyth	if (rename(d + "/data", id + "x") == -1 || rename(d + "/data1", id + "y")) {
73037da2899SCharles.Forsyth		sys->unmount(nil, d);
73137da2899SCharles.Forsyth		sys->remove(d);
73237da2899SCharles.Forsyth		ctxt.fail("bad pipe", sys->sprint("pipe: cannot rename pipe: %r"));
73337da2899SCharles.Forsyth	}
73437da2899SCharles.Forsyth	if (sys->bind(d, TMPDIR, Sys->MBEFORE) == -1) {
73537da2899SCharles.Forsyth		sys->unmount(nil, d);
73637da2899SCharles.Forsyth		sys->remove(d);
73737da2899SCharles.Forsyth		ctxt.fail("bad pipe", sys->sprint("pipe: cannot bind pipe dir: %r"));
73837da2899SCharles.Forsyth	}
73937da2899SCharles.Forsyth	sys->unmount(nil, d);
74037da2899SCharles.Forsyth	sys->remove(d);
74137da2899SCharles.Forsyth	sync := chan of string;
74237da2899SCharles.Forsyth	spawn runpipe(sync, ctxt, n, TMPDIR + "/" + id + "x", hd tl tl argv);
74337da2899SCharles.Forsyth	if ((e := <-sync) != nil)
74437da2899SCharles.Forsyth		ctxt.fail("bad pipe", e);
74537da2899SCharles.Forsyth	return ref Listnode(nil, TMPDIR + "/" + id + "y") :: nil;
74637da2899SCharles.Forsyth}
74737da2899SCharles.Forsyth
74837da2899SCharles.Forsythmkdir(f: string): int
74937da2899SCharles.Forsyth{
75037da2899SCharles.Forsyth	if (sys->create(f, Sys->OREAD, Sys->DMDIR | 8r777) == nil)
75137da2899SCharles.Forsyth		return -1;
75237da2899SCharles.Forsyth	return 0;
75337da2899SCharles.Forsyth}
75437da2899SCharles.Forsyth
75537da2899SCharles.Forsythrunpipe(sync: chan of string, ctxt: ref Context, fdno: int, p: string, cmd: ref Listnode)
75637da2899SCharles.Forsyth{
75737da2899SCharles.Forsyth	sys->pctl(Sys->FORKFD, nil);
75837da2899SCharles.Forsyth	ctxt = ctxt.copy(1);
75937da2899SCharles.Forsyth	if ((fd := sys->open(p, Sys->ORDWR)) == nil) {
76037da2899SCharles.Forsyth		sync <-= sys->sprint("cannot open %s: %r", p);
76137da2899SCharles.Forsyth		exit;
76237da2899SCharles.Forsyth	}
76337da2899SCharles.Forsyth	sys->dup(fd.fd, fdno);
76437da2899SCharles.Forsyth	fd = nil;
76537da2899SCharles.Forsyth	sync <-= nil;
76637da2899SCharles.Forsyth	ctxt.run(cmd :: ctxt.get("*"), 1);
76737da2899SCharles.Forsyth}
76837da2899SCharles.Forsyth
76937da2899SCharles.Forsythrename(x, y: string): int
77037da2899SCharles.Forsyth{
77137da2899SCharles.Forsyth	(ok, nil) := sys->stat(x);
77237da2899SCharles.Forsyth	if (ok == -1)
77337da2899SCharles.Forsyth		return -1;
77437da2899SCharles.Forsyth	inf := sys->nulldir;
77537da2899SCharles.Forsyth	inf.name = y;
77637da2899SCharles.Forsyth	if (sys->wstat(x, inf) == -1)
77737da2899SCharles.Forsyth		return -1;
77837da2899SCharles.Forsyth	return 0;
77937da2899SCharles.Forsyth}
78037da2899SCharles.Forsyth
78137da2899SCharles.Forsythbuiltinusage(ctxt: ref Context, s: string)
78237da2899SCharles.Forsyth{
78337da2899SCharles.Forsyth	ctxt.fail("usage", "usage: " + s);
78437da2899SCharles.Forsyth}
78537da2899SCharles.Forsyth
78637da2899SCharles.Forsythsetstatus(ctxt: ref Context, val: string): string
78737da2899SCharles.Forsyth{
78837da2899SCharles.Forsyth	ctxt.setlocal("status", ref Listnode(nil, val) :: nil);
78937da2899SCharles.Forsyth	return val;
79037da2899SCharles.Forsyth}
79137da2899SCharles.Forsyth
79237da2899SCharles.Forsyth# same as sys->raise(), but check that length of error string is
79337da2899SCharles.Forsyth# acceptable, and truncate as appropriate.
79437da2899SCharles.Forsythxraise(s: string)
79537da2899SCharles.Forsyth{
79637da2899SCharles.Forsyth	d := array of byte s;
79737da2899SCharles.Forsyth	if (len d > Sys->WAITLEN)
79837da2899SCharles.Forsyth		raise string d[0:Sys->WAITLEN];
79937da2899SCharles.Forsyth	else {
80037da2899SCharles.Forsyth		d = nil;
80137da2899SCharles.Forsyth		raise s;
80237da2899SCharles.Forsyth	}
80337da2899SCharles.Forsyth}
80437da2899SCharles.Forsyth
80537da2899SCharles.Forsythisnum(s: string): int
80637da2899SCharles.Forsyth{
80737da2899SCharles.Forsyth	for (i := 0; i < len s; i++)
80837da2899SCharles.Forsyth		if (s[i] > '9' || s[i] < '0')
80937da2899SCharles.Forsyth			return 0;
81037da2899SCharles.Forsyth	return 1;
81137da2899SCharles.Forsyth}
81237da2899SCharles.Forsyth
813