Charles.Forsyth20071003-14060204e4119 years agoHistory
2866 lines · 65.9 KB · Brainfuck
RawDownload
implement Sh; include "sys.m";	sys: Sys;	sprint: import sys;include "draw.m";include "bufio.m";	bufio: Bufio;include "string.m";	str: String;include "filepat.m";	filepat: Filepat;include "env.m";	env: Env;include "sh.m";	myself: Sh;	myselfbuiltin: Shellbuiltin; YYSTYPE: adt {	node:	ref Node;	word:	string; 	redir:	ref Redir;	optype:	int;}; YYLEX: adt {	lval:			YYSTYPE;	err:			string;	# if error has occurred	errline:		int;		# line it occurred on.	path:			string;	# name of file that's being read. 	# free caret state	wasdollar:		int;	atendword:	int;	eof:			int;	cbuf:			array of int;	# last chars read	ncbuf:		int;			# number of chars in cbuf 	f:			ref Bufio->Iobuf;	s:			string;	strpos: 		int;			# string pos/cbuf index 	linenum:		int;	prompt:		string;	lastnl:		int; 	initstring:		fn(s: string): ref YYLEX;	initfile:		fn(fd: ref Sys->FD, path: string): ref YYLEX;	lex:			fn(l: self ref YYLEX): int;	error:		fn(l: self ref YYLEX, err: string);	getc:			fn(l: self ref YYLEX): int;	ungetc:		fn(l: self ref YYLEX); 	EOF:			con -1;}; Options: adt {	lflag,	nflag:		int;	ctxtflags:		int;	carg:			string;};  	# module definition is in shell.mDUP: con	57346;REDIR: con	57347;WORD: con	57348;OP: con	57349;END: con	57350;ERROR: con	57351;ANDAND: con	57352;OROR: con	57353;YYEOFCODE: con 1;YYERRCODE: con 2;YYMAXDEPTH: con 200;   EPERM: con "permission denied";EPIPE: con "write on closed pipe"; LIBSHELLRC: con "/lib/sh/profile";BUILTINPATH: con "/dis/sh"; DEBUG: con 0; ENVSEP: con 0;				# word seperator in external environmentENVHASHSIZE: con 7;		# XXX profile usage of this...OAPPEND: con 16r80000;		# make sure this doesn't clash with O* constants in sys.mOMASK: con 7; usage(){	sys->fprint(stderr(), "usage: sh [-ilexn] [-c command] [file [arg...]]\n");	raise "fail:usage";} badmodule(path: string){	sys->fprint(sys->fildes(2), "sh: cannot load %s: %r\n", path);	raise "fail:bad module" ;} initialise(){	if (sys == nil) {		sys = load Sys Sys->PATH; 		filepat = load Filepat Filepat->PATH;		if (filepat == nil) badmodule(Filepat->PATH); 		str = load String String->PATH;		if (str == nil) badmodule(String->PATH); 		bufio = load Bufio Bufio->PATH;		if (bufio == nil) badmodule(Bufio->PATH); 		myself = load Sh "$self";		if (myself == nil) badmodule("$self(Sh)"); 		myselfbuiltin = load Shellbuiltin "$self";		if (myselfbuiltin == nil) badmodule("$self(Shellbuiltin)"); 		env = load Env Env->PATH;	}}blankopts: Options;init(drawcontext: ref Draw->Context, argv: list of string){	initialise();	opts := blankopts;	if (argv != nil) {		if ((hd argv)[0] == '-')			opts.lflag++;		argv = tl argv;	} 	interactive := 0;loop: while (argv != nil && hd argv != nil && (hd argv)[0] == '-') {		for (i := 1; i < len hd argv; i++) {			c := (hd argv)[i];			case c {			'i' =>				interactive = Context.INTERACTIVE;			'l' =>				opts.lflag++;	# login (read $home/lib/profile)			'n' =>				opts.nflag++;	# don't fork namespace			'e' =>				opts.ctxtflags |= Context.ERROREXIT;			'x' =>				opts.ctxtflags |= Context.EXECPRINT;			'c' =>				arg: string;				if (i < len hd argv - 1) {					arg = (hd argv)[i + 1:];				} else if (tl argv == nil || hd tl argv == "") {					usage();				} else {					arg = hd tl argv;					argv = tl argv;				}				argv = tl argv;				opts.carg = arg;				continue loop;			}		}		argv = tl argv;	} 	sys->pctl(Sys->FORKFD, nil);	if (!opts.nflag)		sys->pctl(Sys->FORKNS, nil);	ctxt := Context.new(drawcontext);	ctxt.setoptions(opts.ctxtflags, 1);	if (opts.carg != nil) {		status := ctxt.run(stringlist2list("{" + opts.carg + "}" :: argv), !interactive);		if (!interactive) {			if (status != nil)				raise "fail:" + status;			exit;		}		setstatus(ctxt, status);	} 	# if login shell, run standard init script	if (opts.lflag)		runscript(ctxt, LIBSHELLRC, nil, 0); 	if (argv == nil) {		if (isconsole(sys->fildes(0)))			interactive |= ctxt.INTERACTIVE;		ctxt.setoptions(interactive, 1);		runfile(ctxt, sys->fildes(0), "stdin", nil);	} else {		ctxt.setoptions(interactive, 1);		runscript(ctxt, hd argv, stringlist2list(tl argv), 1);	}} parse(s: string): (ref Node, string){	initialise();		lex := YYLEX.initstring(s); 	return doparse(lex, "", 0);} system(drawctxt: ref Draw->Context, cmd: string): string{	initialise();	{		(n, err) := parse(cmd);		if (err != nil)			return err;		if (n == nil)			return nil;		return Context.new(drawctxt).run(ref Listnode(n, nil) :: nil, 0);	} exception e {	"fail:*" =>		return e[5:];	}} run(drawctxt: ref Draw->Context, argv: list of string): string{	initialise();	{		return Context.new(drawctxt).run(stringlist2list(argv), 0);	} exception e {	"fail:*" =>		return e[5:];	}} isconsole(fd: ref Sys->FD): int{	(ok1, d1) := sys->fstat(fd);	(ok2, d2) := sys->stat("/dev/cons");	if (ok1 < 0 || ok2 < 0)		return 0;	return d1.dtype == d2.dtype && d1.qid.path == d2.qid.path;} runscript(ctxt: ref Context, path: string, args: list of ref Listnode, reporterr: int){	{		fd := sys->open(path, Sys->OREAD);		if (fd != nil)			runfile(ctxt, fd, path, args);		else if (reporterr)			ctxt.fail("bad script path", sys->sprint("sh: cannot open %s: %r", path));	} exception {	"fail:*" =>		if(!reporterr)			return;		raise;	}} runfile(ctxt: ref Context, fd: ref Sys->FD, path: string, args: list of ref Listnode){	ctxt.push();	{		ctxt.setlocal("0", stringlist2list(path :: nil));		ctxt.setlocal("*", args);		lex := YYLEX.initfile(fd, path);		if (DEBUG) debug(sprint("parse(interactive == %d)", (ctxt.options() & ctxt.INTERACTIVE) != 0));		prompt := "" :: "" :: nil;		laststatus: string;		while (!lex.eof) {			interactive := ctxt.options() & ctxt.INTERACTIVE;			if (interactive) {				prompt = list2stringlist(ctxt.get("prompt"));				if (prompt == nil)					prompt = "; " :: "" :: nil;					sys->fprint(stderr(), "%s", hd prompt);				if (tl prompt == nil) {					prompt = hd prompt :: "" :: nil;				}			}			(n, err) := doparse(lex, hd tl prompt, !interactive);			if (err != nil) {				sys->fprint(stderr(), "sh: %s\n", err);				if (!interactive)					raise "fail:parse error";			} else if (n != nil) {				if (interactive) {					{						laststatus = walk(ctxt, n, 0);					} exception e2 {					"fail:*" =>						laststatus = e2[5:];					}				} else					laststatus = walk(ctxt, n, 0);				setstatus(ctxt, laststatus);				if ((ctxt.options() & ctxt.ERROREXIT) && laststatus != nil)					break;			}		}		if (laststatus != nil)			raise "fail:" + laststatus;		ctxt.pop();	}	exception {	"fail:*" =>		ctxt.pop();		raise;	}} nonexistent(e: string): int{	errs := array[] of {"does not exist", "directory entry not found"};	for (i := 0; i < len errs; i++){		j := len errs[i];		if (j <= len e && e[len e-j:] == errs[i])			return 1;	}	return 0;} Redirword: adt {	fd: ref Sys->FD;	w: string;	r: Redir;}; Redirlist: adt {	r: list of Redirword;}; pipe2cmd(n: ref Node): ref Node{	if (n == nil || n.ntype != n_PIPE)		return n;	return mk(n_ADJ, mk(n_BLOCK,n,nil), mk(n_VAR,ref Node(n_WORD,nil,nil,"*",nil),nil));} walk(ctxt: ref Context, n: ref Node, last: int): string{	if (DEBUG) debug(sprint("walking: %s", cmd2string(n)));	# avoid tail recursion stack explosion	while (n != nil && n.ntype == n_SEQ) {		status := walk(ctxt, n.left, 0);		if (ctxt.options() & ctxt.ERROREXIT && status != nil)			raise "fail:" + status;		setstatus(ctxt, status);		n = n.right;	}	if (n == nil)		return nil;	case (n.ntype) {	n_PIPE =>		return waitfor(ctxt, walkpipeline(ctxt, n, nil, -1));	n_ASSIGN or n_LOCAL =>		assign(ctxt, n);		return nil;	* =>		bg := 0;		if (n.ntype == n_NOWAIT) {			bg = 1;			n = pipe2cmd(n.left);		} 		redirs := ref Redirlist(nil);		line := glob(glom(ctxt, n, redirs, nil)); 		if (bg) {			startchan := chan of (int, ref Expropagate);			spawn runasync(ctxt, 1, line, redirs, startchan);			(pid, nil) := <-startchan;			redirs = nil;			if (DEBUG) debug("started background process "+ string pid);			ctxt.set("apid", ref Listnode(nil, string pid) :: nil);			return nil;		} else {			return runsync(ctxt, line, redirs, last);		}	}} assign(ctxt: ref Context, n: ref Node): list of ref Listnode{	redirs := ref Redirlist;	val: list of ref Listnode;	if (n.right != nil && (n.right.ntype == n_ASSIGN || n.right.ntype == n_LOCAL))		val = assign(ctxt, n.right);	else		val = glob(glom(ctxt, n.right, redirs, nil));	vars := glom(ctxt, n.left, redirs, nil);	if (vars == nil)		ctxt.fail("bad assign", "sh: nil variable name");	if (redirs.r != nil)		ctxt.fail("bad assign", "sh: redirections not allowed in assignment");	tval := val;	for (; vars != nil; vars = tl vars) {		vname := deglob((hd vars).word);		if (vname == nil) 			ctxt.fail("bad assign", "sh: bad variable name");		v: list of ref Listnode = nil;		if (tl vars == nil)			v = tval;		else if (tval != nil)			v = hd tval :: nil;		if (n.ntype == n_ASSIGN)			ctxt.set(vname, v);		else			ctxt.setlocal(vname, v);		if (tval != nil)			tval = tl tval;	}	return val;} walkpipeline(ctxt: ref Context, n: ref Node, wrpipe: ref Sys->FD, wfdno: int): list of int{	if (n == nil)		return nil; 	fds := array[2] of ref Sys->FD;	pids: list of int;	rfdno := -1;	if (n.ntype == n_PIPE) {		if (sys->pipe(fds) == -1)			ctxt.fail("no pipe", sys->sprint("sh: cannot make pipe: %r"));		nwfdno := -1;		if (n.redir != nil) {			(fd1, fd2) := (n.redir.fd2, n.redir.fd1);			if (fd2 == -1)				(fd1, fd2) = (fd2, fd1);			(nwfdno, rfdno) = (fd2, fd1);		}		pids = walkpipeline(ctxt, n.left, fds[1], nwfdno);		fds[1] = nil;		n = n.right;	}	r := ref Redirlist(nil);	rlist := glob(glom(ctxt, n, r, nil));	if (fds[0] != nil) {		if (rfdno == -1)			rfdno = 0;		r.r = Redirword(fds[0], nil, Redir(Sys->OREAD, rfdno, -1)) :: r.r;	}	if (wrpipe != nil) {		if (wfdno == -1)			wfdno = 1;		r.r = Redirword(wrpipe, nil, Redir(Sys->OWRITE, wfdno, -1)) :: r.r;	}	startchan := chan of (int, ref Expropagate);	spawn runasync(ctxt, 1, rlist, r, startchan);	(pid, nil) := <-startchan;	if (DEBUG) debug("started pipe process "+string pid);	return pid :: pids;} makeredir(f: string, mode: int, fd: int): Redirword{	return Redirword(nil, f, Redir(mode, fd, -1));} glom(ctxt: ref Context, n: ref Node, redirs: ref Redirlist, onto: list of ref Listnode)		: list of ref Listnode{	if (n == nil) return nil; 	if (n.ntype != n_ADJ)		return listjoin(glomoperation(ctxt, n, redirs), onto); 	nlist := glom(ctxt, n.right, redirs, onto); 	if (n.left.ntype != n_ADJ) {		# if it's a terminal node		nlist = listjoin(glomoperation(ctxt, n.left, redirs), nlist);	} else		nlist = glom(ctxt, n.left, redirs, nlist);	return nlist;} listjoin(left, right: list of ref Listnode): list of ref Listnode{	l: list of ref Listnode;	for (; left != nil; left = tl left)		l = hd left :: l;	for (; l != nil; l = tl l)		right = hd l :: right;	return right;} pipecmd(ctxt: ref Context, cmd: list of ref Listnode, redir: ref Redir): ref Sys->FD{	if(redir.fd2 != -1 || (redir.rtype & OAPPEND))		ctxt.fail("bad redir", "sh: bad redirection");	r := *redir;	case redir.rtype {

Lines 1–500 of 2866.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t