Charles.Forsyth20060303a828770d19 years agoHistory
628 lines · 10.6 KB · Brainfuck
RawDownload
implement Sh; include "sys.m";	sys: Sys;	FD: import Sys; include "draw.m";	Context: import Draw; include "filepat.m";	filepat: Filepat;	nofilepat := 0;			# true if load Filepat has failed. include "bufio.m";	bufio: Bufio;	Iobuf: import bufio; include "env.m";	env: Env; stdin: ref FD;stderr: ref FD;waitfd: ref FD; Quoted: con '\uFFF0';stringQuoted: con "\uFFF0"; Sh: module{	init: fn(ctxt: ref Context, argv: list of string);}; Command: adt{	args: list of string;	inf, outf: string;	append: int;}; Async, Seq: con iota; Pipeline: adt{	cmds: list of ref Command;	term: int;}; usage(){	sys->fprint(stderr, "Usage: sh [-n] [-c cmd] [file]\n");} init(ctxt: ref Context, argv: list of string){	n: int;	arg: list of string;	buf := array[1024] of byte; 	sys = load Sys Sys->PATH;	env = load Env Env->PATH; 	stderr = sys->fildes(2); 	waitfd = sys->open("#p/"+string sys->pctl(0, nil)+"/wait", sys->OREAD);	if(waitfd == nil){		sys->fprint(stderr, "sh: open wait: %r\n");		return;	} 	eflag := nflag := lflag := 0;	cmd: string;	if(argv != nil)		argv = tl argv;	for(; argv != nil && len hd argv && (hd argv)[0]=='-'; argv = tl argv)		case hd argv {		"-e" =>			eflag = 1;		"-n" =>			nflag = 1;		"-l" =>			lflag = 1;		"-c" =>			argv = tl argv;			if(len argv != 1){				usage();				return;			}			cmd = hd argv;		* =>			usage();			return;		}		if (lflag)		startup(ctxt); 	if(eflag == 0)		sys->pctl(sys->FORKENV, nil);	if(nflag == 0)		sys->pctl(sys->FORKNS, nil);	if(cmd != nil){		arg = tokenize(cmd+"\n");		if(arg != nil)			runit(ctxt, parseit(arg));		return;	}	if(argv != nil){		script(ctxt, hd argv);		return;	} 	stdin = sys->fildes(0); 	prompt := sysname() + "$ ";	for(;;){		sys->print("%s", prompt);		n = sys->read(stdin, buf, len buf);		if(n <= 0)			break;		arg = tokenize(string buf[0:n]);		if(arg != nil)			runit(ctxt, parseit(arg));	}} rev(arg: list of string): list of string{	ret: list of string; 	while(arg != nil){		ret = hd arg :: ret;		arg = tl arg;	}	return ret;} waitfor(pid: int){	if(pid <= 0)		return;	buf := array[sys->WAITLEN] of byte;	status := "";	for(;;){		n := sys->read(waitfd, buf, len buf);		if(n < 0){			sys->fprint(stderr, "sh: read wait: %r\n");			return;		}		status = string buf[0:n];		if(status[len status-1] != ':')			sys->fprint(stderr, "%s\n", status);		who := int status;		if(who != 0){			if(who == pid)				return;		}	}} mkprog(ctxt: ref Context, arg: list of string, infd, outfd: ref Sys->FD, waitpid: chan of int){	fds := list of {0, 1, 2};	if(infd != nil)		fds = infd.fd :: fds;	if(outfd != nil)		fds = outfd.fd :: fds;	pid := sys->pctl(sys->NEWFD, fds);	console := sys->fildes(2); 	if(infd != nil){		sys->dup(infd.fd, 0);		infd = nil;	}	if(outfd != nil){		sys->dup(outfd.fd, 1);		outfd = nil;	} 	waitpid <-= pid; 	if(pid < 0 || arg == nil)		return; 	{		exec(ctxt, arg, console);	}exception{	"fail:*" =>		#sys->fprint(console, "%s:%s\n", hd arg, e.name[5:]);		exit;	"write on closed pipe" =>		#sys->fprint(console, "%s: %s\n", hd arg, e.name);		exit;	}} exec(ctxt: ref Context, args: list of string, console: ref Sys->FD){	if (args == nil)		return;	cmd := hd args;	file := cmd;			if(len file<4 || file[len file-4:]!=".dis")		file += ".dis"; 	c := load Sh file;	if(c == nil) {		err := sys->sprint("%r");		if(err != "permission denied" && err != "access permission denied" && file[0]!='/' && file[0:2]!="./"){			c = load Sh "/dis/"+file;			if(c == nil)				err = sys->sprint("%r");		}		if(c == nil){			sys->fprint(console, "%s: %s\n", cmd, err);			return;		}	} 	c->init(ctxt, args);} script(ctxt: ref Context, src: string){	bufio = load Bufio Bufio->PATH;	if(bufio == nil){		sys->fprint(stderr, "sh: load bufio: %r\n");		return;	} 	f := bufio->open(src, Bufio->OREAD);	if(f == nil){		sys->fprint(stderr, "sh: open %s: %r\n", src);		return;	}	for(;;){		s := f.gets('\n');		if(s == nil)			break;		arg := tokenize(s);		if(arg != nil)			runit(ctxt, parseit(arg));	}} sysname(): string{	fd := sys->open("#c/sysname", sys->OREAD);	if(fd == nil)		return "anon";	buf := array[128] of byte;	n := sys->read(fd, buf, len buf);	if(n < 0) 		return "anon";	return string buf[0:n];} # Lexer. tokenize(s: string): list of string{	tok: list of string;	token := "";	instring := 0; loop:	for(i:=0; i<len s; i++) {		if(instring) {			if(s[i] != '\'')				token = addchar(token, s[i]);			else if(i == len s-1 || s[i+1] != '\'') {				if(i == len s-1 || s[i+1] == ' ' || s[i+1] == '\t' || s[i+1] == '\n'){					tok = token :: tok;					token = "";				}				instring = 0;			} else {				token[len token] = '\'';				i++;			}			continue;		}		case s[i] {		' ' or '\t' or '\n' or '#' or		'\'' or '|' or '&' or ';' or		'>' or '<' or '\r' =>			if(token != "" && s[i]!='\''){				tok = token :: tok;				token = "";			}			case s[i] {			'#' =>				break loop;			'\'' =>				instring = 1;			'>' =>				ss := "";				ss[0] = s[i];				if(i<len s-1 && s[i+1]==s[i])					ss[1] = s[i++];				tok = ss :: tok;			'|' or '&' or ';' or '<' =>				ss := "";				ss[0] = s[i];				tok = ss :: tok;			}		* =>			token[len token] = s[i];		}	}	if(instring){		sys->fprint(stderr, "sh: unmatched quote\n");		return nil;	}	return rev(tok);} ismeta(char: int): int{	case char {	'*'  or '[' or '?' or	'#'  or '\'' or '|' or	'&' or ';' or '>' or	'<'  =>		return 1;	}	return 0;} addchar(token: string, char: int): string{	if(ismeta(char) && (len token==0 || token[0]!=Quoted))		token = stringQuoted + token;	token[len token] = char;	return token;} # Parser. getcommand(words: list of string): (ref Command, list of string){	args: list of string;	word: string;	si, so: string;	append := 0; gather:	do {		word = hd words; 		case word {		">" or ">>" =>			if(so != nil)				return (nil, nil); 			words = tl words; 			if(words == nil)				return (nil, nil); 			so = hd words;			if(len so>0 && so[0]==Quoted)				so = so[1:];			if(word == ">>")				append = 1;		"<" =>			if(si != nil)				return (nil, nil); 			words = tl words; 			if(words == nil)				return (nil, nil); 			si = hd words;			if(len si>0 && si[0]==Quoted)				si = si[1:];		"|" or ";" or "&" =>			break gather;		* =>			files := doexpand(word);			while(files != nil){				args = hd files :: args;				files = tl files;			}		} 		words = tl words;	} while (words != nil); 	return (ref Command(rev(args), si, so, append), words);} doexpand(file: string): list of string{	if(file == nil)		return file :: nil;	if(len file>0 && file[0]==Quoted)		return file[1:] :: nil;	if (nofilepat)		return file :: nil;	for(i:=0; i<len file; i++)		if(file[i]=='*' || file[i]=='[' || file[i]=='?'){			if(filepat == nil) {				if ((filepat = load Filepat Filepat->PATH) == nil) {					sys->fprint(stderr, "sh: warning: cannot load %s: %r\n",							Filepat->PATH);					nofilepat = 1;					return file :: nil;				}			}			files := filepat->expand(file);			if(files != nil)				return files;			break;		}	return file :: nil;} revc(arg: list of ref Command): list of ref Command{	ret: list of ref Command;	while(arg != nil) {		ret = hd arg :: ret;		arg = tl arg;	}	return ret;} getpipe(words: list of string): (ref Pipeline, list of string){	cmds: list of ref Command;	cur: ref Command;	word: string; 	term := Seq;gather:	while(words != nil) {		word = hd words; 		if(word == "|")			return (nil, nil); 		(cur, words) = getcommand(words); 		if(cur == nil)			return (nil, nil); 		cmds = cur :: cmds; 		if(words == nil)			break gather; 		word = hd words;		words = tl words; 		case word {		";" =>			break gather;		"&" =>			term = Async;			break gather;		"|" =>			continue gather;		}		return (nil, nil);	} 	if(word == "|")		return (nil, nil); 	return (ref Pipeline(revc(cmds), term), words);} revp(arg: list of ref Pipeline): list of ref Pipeline{	ret: list of ref Pipeline; 	while(arg != nil) {		ret = hd arg :: ret;		arg = tl arg;	}	return ret;} parseit(words: list of string): list of ref Pipeline{	ret: list of ref Pipeline;	cur: ref Pipeline; 	while(words != nil) {		(cur, words) = getpipe(words);		if(cur == nil){			sys->fprint(stderr, "sh: syntax error\n");			return nil;		}		ret = cur :: ret;	}	return revp(ret);}

Lines 1–500 of 628.