forsyth20121205-174063a80cc13 years agoHistory
812 lines · 18.8 KB · Brainfuck
RawDownload
implement Shellbuiltin; include "sys.m";	sys: Sys;include "draw.m";include "sh.m";	sh: Sh;	Listnode, Context: import sh;	myself: Shellbuiltin;include "filepat.m";	filepat: Filepat;include "bufio.m";	bufio: Bufio;	Iobuf: import bufio; builtinnames := array[] of {	"if", "while", "~", "!", "apply", "for",	"status", "pctl", "fn", "subfn", "and", "or",	"raise", "rescue", "flag", "getlines", "no",}; sbuiltinnames := array[] of {	"hd", "tl", "index", "split", "join", "pid", "parse", "env", "pipe",}; initbuiltin(ctxt: ref Context, shmod: Sh): string{	sys = load Sys Sys->PATH;	sh = shmod;	myself = load Shellbuiltin "$self";	if (myself == nil)		ctxt.fail("bad module", sys->sprint("std: cannot load self: %r"));	filepat = load Filepat Filepat->PATH;	if (filepat == nil)		ctxt.fail("bad module",			sys->sprint("std: cannot load: %s: %r", Filepat->PATH));	bufio = load Bufio Bufio->PATH;	if (bufio == nil)		ctxt.fail("bad module",			sys->sprint("std: cannot load: %s: %r", Bufio->PATH));	names := builtinnames; 	for (i := 0; i < len names; i++)		ctxt.addbuiltin(names[i], myself);	names = sbuiltinnames;	for (i = 0; i < len names; i++)		ctxt.addsbuiltin(names[i], myself);	env := ctxt.envlist();	for (; env != nil; env = tl env) {		(name, val) := hd env;		if (len name > 3 && name[0:3] == "fn-")			fndef(ctxt, name[3:], val, 0);		if (len name > 4 && name[0:4] == "sfn-")			fndef(ctxt, name[4:], val, 1);	}	return nil;} whatis(c: ref Sh->Context, sh: Sh, name: string, wtype: int): string{	ename, fname: string;	case wtype {	BUILTIN =>		(ename, fname) = ("fn-", "fn ");	SBUILTIN =>		(ename, fname) = ("sfn-", "subfn ");	OTHER =>		return nil;	} 	val := c.get(ename + name);	if (val != nil)		return fname + name + " " + sh->quoted(hd val :: nil, 0);	return nil;} getself(): Shellbuiltin{	return myself;} runbuiltin(c: ref Sh->Context, nil: Sh,			cmd: list of ref Sh->Listnode, last: int): string{	status: string;	name := (hd cmd).word;	val := c.get("fn-" + name);	if (val != nil)		return c.run(hd val :: tl cmd, last);	case name {	"if" =>		status = builtin_if(c, cmd, last);	"while" =>		status = builtin_while(c, cmd, last);	"and" =>		status = builtin_and(c, cmd, last);	"apply" =>	status = builtin_apply(c, cmd, last);	"for" =>		status = builtin_for(c, cmd, last);	"or" =>		status = builtin_or(c, cmd, last);	"!" =>		status = builtin_not(c, cmd, last);	"fn" =>		status = builtin_fn(c, cmd, last, 0);	"subfn" =>	status = builtin_fn(c, cmd, last, 1);	"~" =>		status = builtin_twiddle(c, cmd, last);	"status" =>	status = builtin_status(c, cmd, last);	"pctl" =>		status = builtin_pctl(c, cmd, last);	"raise" =>		status = builtin_raise(c, cmd, last);	"rescue" =>	status = builtin_rescue(c, cmd, last);	"flag" =>		status = builtin_flag(c, cmd, last);	"getlines" =>	status = builtin_getlines(c, cmd, last);	"no" =>		status = builtin_no(c, cmd, last);	}	return status;} runsbuiltin(c: ref Sh->Context, nil: Sh,			cmd: list of ref Sh->Listnode): list of ref Listnode{	name := (hd cmd).word;	val := c.get("sfn-" + name);	if (val != nil)		return runsubfn(c, val, tl cmd);	case name {	"pid" =>		return ref Listnode(nil, string sys->pctl(0, nil)) :: nil;	"hd" =>		if (tl cmd == nil)			return nil;		return hd tl cmd :: nil;	"tl" =>		if (tl cmd == nil)			return nil;		return tl tl cmd;	"index" =>		return sbuiltin_index(c, cmd);	"split" =>		return sbuiltin_split(c, cmd);	"join" =>		return sbuiltin_join(c, cmd);	"parse" =>		return sbuiltin_parse(c, cmd);	"env" =>		return sbuiltin_env(c, cmd);	"pipe" =>		return sbuiltin_pipe(c, cmd);	}	return nil;} runsubfn(ctxt: ref Context, body, args: list of ref Listnode): list of ref Listnode{	if (body == nil)		return nil;	ctxt.push();	{		ctxt.setlocal("result", nil);		ctxt.run(hd body :: args, 0);		result := ctxt.get("result");		ctxt.pop();		return result;	} exception e {	"fail:*" =>		ctxt.pop();		raise e;	}} sbuiltin_index(ctxt: ref Context, val: list of ref Listnode): list of ref Listnode{	if (len val < 2 || (hd tl val).word == nil)		builtinusage(ctxt, "index num list");	k := int (hd tl val).word - 1;	val = tl tl val;	for (; k > 0 && val != nil; k--)		val = tl val;	if (val != nil)		val = hd val :: nil;	return val;} # return a parsed version of a string, raising a "parse error" exception if# it fails. the string must be a braced command block.sbuiltin_parse(ctxt: ref Context, args: list of ref Listnode): list of ref Listnode{	if (len args != 2)		builtinusage(ctxt, "parse arg");	args = tl args;	if ((hd args).cmd != nil)		return ref Listnode((hd args).cmd, nil) :: nil;	w := (hd args).word;	if (w == nil || w[0] != '{')	#}		ctxt.fail("parse error", "parse: argument must be a braced block");	(n, err) := sh->parse(w);	if (err != nil)		ctxt.fail("parse error", "parse: " + err);	return ref Listnode(n, nil) :: nil;} sbuiltin_env(ctxt: ref Context, nil: list of ref Listnode): list of ref Listnode{	vl: list of string;	for (e := ctxt.envlist(); e != nil; e = tl e) {		(n, v) := hd e;		if (v != nil)		# XXX this is debatable... someone might want to see null local vars.			vl = n :: vl;	}	return sh->stringlist2list(vl);} word(n: ref Listnode): string{	if (n.word != nil)		return n.word;	if (n.cmd != nil)		n.word = sh->cmd2string(n.cmd);	return n.word;} # usage: split [separators] valuesbuiltin_split(ctxt: ref Context, args: list of ref Listnode): list of ref Listnode{	n := len args;	if (n < 2  || n > 3)		builtinusage(ctxt, "split [separators] value");	seps: string;	if (n == 2) {		ifs := ctxt.get("ifs");		if (ifs == nil)			ctxt.fail("usage", "split: $ifs not set");		seps = word(hd ifs);	} else {		args = tl args;		seps = word(hd args);	}	(nil, toks) := sys->tokenize(word(hd tl args), seps);	return sh->stringlist2list(toks);} sbuiltin_join(ctxt: ref Context, args: list of ref Listnode): list of ref Listnode{	args = tl args;	if (args == nil)		builtinusage(ctxt, "join separator [arg...]");	seps := word(hd args);	if (tl args == nil)		return ref Listnode(nil, nil) :: nil;	s := word(hd tl args);	for (args = tl tl args; args != nil; args = tl args)		s += seps + word(hd args);	return ref Listnode(nil, s) :: nil;} builtin_fn(ctxt: ref Context, args: list of ref Listnode, nil: int, issub: int): string{	n := len args;	title := (hd args).word;	if (n < 2)		builtinusage(ctxt, title + " [name...] [{body}]");	for (al := tl args; tl al != nil; al = tl al)		if ((hd al).cmd != nil)			builtinusage(ctxt, title + " [name...] [{body}]");	if ((hd al).cmd != nil) {		cmd := hd al :: nil;		for (al = tl args; tl al != nil; al = tl al)			fndef(ctxt, (hd al).word, cmd, issub);	} else {		for (al = tl args; al != nil; al = tl al)			fnundef(ctxt, (hd al).word, issub);	}	return nil;} fndef(ctxt: ref Context, name: string, cmd: list of ref Listnode, issub: int){	if (cmd == nil)		return;	if (issub) {		ctxt.set("sfn-" + name, cmd);		ctxt.addsbuiltin(name, myself);	} else {		ctxt.set("fn-" + name, cmd);		ctxt.addbuiltin(name, myself);	}} fnundef(ctxt: ref Context, name: string, issub: int){	if (issub) {		ctxt.set("sfn-" + name, nil);		ctxt.removesbuiltin(name, myself);	} else {		ctxt.set("fn-" + name, nil);		ctxt.removebuiltin(name, myself);	}} builtin_flag(ctxt: ref Context, args: list of ref Listnode, nil: int): string{	n := len args;	if (n < 2 || n > 3 || len (hd tl args).word != 1)		builtinusage(ctxt, "flag [vxei] [+-]");	flag := (hd tl args).word[0];	p := "";	if (n == 3)		p = (hd tl tl args).word;	mask := 0;	case flag {	'v' =>	mask = Context.VERBOSE;	'x' =>	mask = Context.EXECPRINT;	'e' =>	mask = Context.ERROREXIT;	'i' =>		mask = Context.INTERACTIVE;	* =>		builtinusage(ctxt, "flag [vxei] [+-]");	}	case p {	"" =>		if (ctxt.options() & mask)				return nil;			return "not set";	"-" =>	ctxt.setoptions(mask, 0);	"+" =>	ctxt.setoptions(mask, 1);	* =>		builtinusage(ctxt, "flag [vxei] [+-]");	}	return nil;} builtin_no(nil: ref Context, args: list of ref Listnode, nil: int): string{	if (tl args != nil)		return "yes";	return nil;} iscmd(n: ref Listnode): int{	return n.cmd != nil || (n.word != nil && n.word[0] == '{');} builtin_if(ctxt: ref Context, args: list of ref Listnode, nil: int): string{	args = tl args;	nargs := len args;	if (nargs < 2)		builtinusage(ctxt, "if {cond} {action} [{cond} {action}]... [{elseaction}]"); 	status: string;	dolstar := ctxt.get("*");	while (args != nil) {		cmd: ref Listnode = nil;		if (tl args == nil) {			cmd = hd args;			args = tl args;		} else {			if (!iscmd(hd args))				builtinusage(ctxt, "if [{cond} {action}]... [{elseaction}]"); 			status = ctxt.run(hd args :: dolstar, 0);			if (status == nil) {				cmd = hd tl args;				args = nil;			} else				args = tl tl args;			setstatus(ctxt, status);		}		if (cmd != nil) {			if (!iscmd(cmd))				builtinusage(ctxt, "if [{cond} {action}]... [{elseaction}]"); 			status = ctxt.run(cmd :: dolstar, 0);		}	}	return status;	} builtin_or(ctxt: ref Context, args: list of ref Listnode, nil: int): string{	s: string;	dolstar := ctxt.get("*");	for (args = tl args; args != nil; args = tl args) {		if (!iscmd(hd args))			builtinusage(ctxt, "or [{cmd} ...]");		if ((s = ctxt.run(hd args :: dolstar, 0)) == nil)			return nil;		else			setstatus(ctxt, s);	}	return s;} builtin_and(ctxt: ref Context, args: list of ref Listnode, nil: int): string{	dolstar := ctxt.get("*");	for (args = tl args; args != nil; args = tl args) {		if (!iscmd(hd args))			builtinusage(ctxt, "and [{cmd} ...]");		if ((s := ctxt.run(hd args :: dolstar, 0)) != nil)			return s;		else			setstatus(ctxt, nil);	}	return nil;} builtin_while(ctxt: ref Context, args: list of ref Listnode, nil: int) : string{	args = tl args;	if (len args != 2 || !iscmd(hd args) || !iscmd(hd tl args))		builtinusage(ctxt, "while {condition} {cmd}"); 	dolstar := ctxt.get("*");	cond := hd args :: dolstar;	action := hd tl args :: dolstar;	status := "";		for(;;){		{			while (ctxt.run(cond, 0) == nil)				status = setstatus(ctxt, ctxt.run(action, 0));			return status;		} exception e{		"fail:*" =>			if (loopexcept(e) == BREAK)				return status;		}	}} builtin_getlines(ctxt: ref Context, argv: list of ref Listnode, nil: int) : string{	n := len argv;	if (n < 2  || n > 3)		builtinusage(ctxt, "getlines [separators] {cmd}");	argv = tl argv;	seps := "\n";	if (n == 3) {		seps = word(hd argv);		argv = tl argv;	}	if (len seps == 0)		builtinusage(ctxt, "getlines [separators] {cmd}");	if (!iscmd(hd argv))		builtinusage(ctxt, "getlines [separators] {cmd}");	cmd := hd argv :: ctxt.get("*");	stdin := bufio->fopen(sys->fildes(0), Sys->OREAD);	if (stdin == nil)		ctxt.fail("bad input", sys->sprint("getlines: cannot open stdin: %r"));	status := "";	ctxt.push();	for(;;){		{			for (;;) {				s: string;				if (len seps == 1)					s = stdin.gets(seps[0]);				else					s = stdin.gett(seps);				if (s == nil)					break;				# make sure we don't lose the last unterminated line				lastc := s[len s - 1];				if (lastc == seps[0])					s = s[0:len s - 1];				else for (i := 1; i < len seps; i++) {					if (lastc == seps[i]) {						s = s[0:len s - 1];						break;					}				}				ctxt.setlocal("line", ref Listnode(nil, s) :: nil);				status = setstatus(ctxt, ctxt.run(cmd, 0));			}			ctxt.pop();			return status;		} exception e {		"fail:*" =>			ctxt.pop();			if (loopexcept(e) == BREAK)				return status;			ctxt.push();		}	}} # usage: raise [name]builtin_raise(ctxt: ref Context, args: list of ref Listnode, nil: int) : string{	ename: ref Listnode;	if (tl args == nil) {		e := ctxt.get("exception");		if (e == nil)			ctxt.fail("bad raise context", "raise: no exception found");		ename = (hd e);	} else		ename = hd tl args;	if (ename.word == nil && ename.cmd != nil)		ctxt.fail("bad raise context", "raise: bad exception name");	xraise("fail:" + ename.word);	return nil;} # usage: rescue pattern rescuecmd cmdbuiltin_rescue(ctxt: ref Context, args: list of ref Listnode, last: int) : string{	args = tl args;	if (len args != 3 || !iscmd(hd tl args) || !iscmd(hd tl tl args))		builtinusage(ctxt, "rescue pattern {rescuecmd} {cmd}");	if ((hd args).word == nil && (hd args).cmd != nil)

Lines 1–500 of 812.