Plan 9's rc.

not a clear win over byron's,
but at least it has the right syntax.
diff --git a/src/cmd/rc/plan9ish.c b/src/cmd/rc/plan9ish.c
new file mode 100644
index 0000000..0c3076e
--- /dev/null
+++ b/src/cmd/rc/plan9ish.c
@@ -0,0 +1,480 @@
+/*
+ * Plan 9 versions of system-specific functions
+ *	By convention, exported routines herein have names beginning with an
+ *	upper case letter.
+ */
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+#include "getflags.h"
+char *Signame[]={
+	"sigexit",	"sighup",	"sigint",	"sigquit",
+	"sigalrm",	"sigkill",	"sigfpe",	"sigterm",
+	0
+};
+char *syssigname[]={
+	"exit",		/* can't happen */
+	"hangup",
+	"interrupt",
+	"quit",		/* can't happen */
+	"alarm",
+	"kill",
+	"sys: fp: ",
+	"term",
+	0
+};
+char*
+Rcmain(void)
+{
+	static char buf[256];
+	char *root;
+
+	root = getenv("PLAN9");
+	if(root == nil)
+		root = "/usr/local/plan9";
+	snprint(buf, sizeof buf, "%s/rcmain", root);
+	return buf;
+}
+
+char Fdprefix[]="/dev/fd/";
+void execfinit(void);
+void execbind(void);
+void execmount(void);
+void execnewpgrp(void);
+builtin Builtin[]={
+	"cd",		execcd,
+	"whatis",	execwhatis,
+	"eval",		execeval,
+	"exec",		execexec,	/* but with popword first */
+	"exit",		execexit,
+	"shift",	execshift,
+	"wait",		execwait,
+	".",		execdot,
+	"finit",	execfinit,
+	"flag",		execflag,
+	0
+};
+#define	SEP	'\1'
+char **environp;
+struct word *enval(s)
+register char *s;
+{
+	register char *t, c;
+	register struct word *v;
+	for(t=s;*t && *t!=SEP;t++);
+	c=*t;
+	*t='\0';
+	v=newword(s, c=='\0'?(struct word *)0:enval(t+1));
+	*t=c;
+	return v;
+}
+void Vinit(void){
+	extern char **environ;
+	register char *s;
+	register char **env=environ;
+	environp=env;
+	for(;*env;env++){
+		for(s=*env;*s && *s!='(' && *s!='=';s++);
+		switch(*s){
+		case '\0':
+			pfmt(err, "environment %q?\n", *env);
+			break;
+		case '=':
+			*s='\0';
+			setvar(*env, enval(s+1));
+			*s='=';
+			break;
+		case '(':	/* ignore functions for now */
+			break;
+		}
+	}
+}
+char **envp;
+void Xrdfn(void){
+	char *p;
+	register char *s;
+	register int len;
+	for(;*envp;envp++){
+		s = *envp;
+		if(strncmp(s, "fn#", 3) == 0){
+			p = strchr(s, '=');
+			if(p == nil)
+				continue;
+			*p = ' ';
+			s[2] = ' ';
+			len = strlen(s);
+			execcmds(opencore(s, len));
+			s[len] = '\0';
+			return;
+		}
+#if 0
+		for(s=*envp;*s && *s!='(' && *s!='=';s++);
+		switch(*s){
+		case '\0':
+			pfmt(err, "environment %q?\n", *envp);
+			break;
+		case '=':	/* ignore variables */
+			break;
+		case '(':		/* Bourne again */
+			s=*envp+3;
+			envp++;
+			len=strlen(s);
+			s[len]='\n';
+			execcmds(opencore(s, len+1));
+			s[len]='\0';
+			return;
+		}
+#endif
+	}
+	Xreturn();
+}
+union code rdfns[4];
+void execfinit(void){
+	static int first=1;
+	if(first){
+		rdfns[0].i=1;
+		rdfns[1].f=Xrdfn;
+		rdfns[2].f=Xjump;
+		rdfns[3].i=1;
+		first=0;
+	}
+	Xpopm();
+	envp=environp;
+	start(rdfns, 1, runq->local);
+}
+int Waitfor(int pid, int unused0){
+	thread *p;
+	Waitmsg *w;
+	char errbuf[ERRMAX];
+
+	while((w = wait()) != nil){
+		if(w->pid==pid){
+			setstatus(w->msg);
+			free(w);
+			return 0;
+		}
+		for(p=runq->ret;p;p=p->ret)
+			if(p->pid==w->pid){
+				p->pid=-1;
+				strcpy(p->status, w->msg);
+			}
+		free(w);
+	}
+
+	errstr(errbuf, sizeof errbuf);
+	if(strcmp(errbuf, "interrupted")==0) return -1;
+	return 0;
+}
+char **mkargv(word *a)
+{
+	char **argv=(char **)emalloc((count(a)+2)*sizeof(char *));
+	char **argp=argv+1;	/* leave one at front for runcoms */
+	for(;a;a=a->next) *argp++=a->word;
+	*argp=0;
+	return argv;
+}
+/*
+void addenv(var *v)
+{
+	char envname[256];
+	word *w;
+	int f;
+	io *fd;
+	if(v->changed){
+		v->changed=0;
+		snprint(envname, sizeof envname, "/env/%s", v->name);
+		if((f=Creat(envname))<0)
+			pfmt(err, "rc: can't open %s: %r\n", envname);
+		else{
+			for(w=v->val;w;w=w->next)
+				write(f, w->word, strlen(w->word)+1L);
+			close(f);
+		}
+	}
+	if(v->fnchanged){
+		v->fnchanged=0;
+		snprint(envname, sizeof envname, "/env/fn#%s", v->name);
+		if((f=Creat(envname))<0)
+			pfmt(err, "rc: can't open %s: %r\n", envname);
+		else{
+			if(v->fn){
+				fd=openfd(f);
+				pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
+				closeio(fd);
+			}
+			close(f);
+		}
+	}
+}
+void updenvlocal(var *v)
+{
+	if(v){
+		updenvlocal(v->next);
+		addenv(v);
+	}
+}
+void Updenv(void){
+	var *v, **h;
+	for(h=gvar;h!=&gvar[NVAR];h++)
+		for(v=*h;v;v=v->next)
+			addenv(v);
+	if(runq) updenvlocal(runq->local);
+}
+*/
+int
+cmpenv(a, b)
+char **a, **b;
+{
+	return strcmp(*a, *b);
+}
+char **mkenv(){
+	register char **env, **ep, *p, *q;
+	register struct var **h, *v;
+	register struct word *a;
+	register int nvar=0, nchr=0, sep;
+	/*
+	 * Slightly kludgy loops look at locals then globals
+	 */
+	for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){
+		if((v==vlook(v->name)) && v->val){
+			nvar++;
+			nchr+=strlen(v->name)+1;
+			for(a=v->val;a;a=a->next)
+				nchr+=strlen(a->word)+1;
+		}
+		if(v->fn){
+			nvar++;
+			nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8;
+		}
+	}
+	env=(char **)emalloc((nvar+1)*sizeof(char *)+nchr);
+	ep=env;
+	p=(char *)&env[nvar+1];
+	for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){
+		if((v==vlook(v->name)) && v->val){
+			*ep++=p;
+			q=v->name;
+			while(*q) *p++=*q++;
+			sep='=';
+			for(a=v->val;a;a=a->next){
+				*p++=sep;
+				sep=SEP;
+				q=a->word;
+				while(*q) *p++=*q++;
+			}
+			*p++='\0';
+		}
+		if(v->fn){
+			*ep++=p;
+#if 0
+			*p++='#'; *p++='('; *p++=')';	/* to fool Bourne */
+			*p++='f'; *p++='n'; *p++=' ';
+			q=v->name;
+			while(*q) *p++=*q++;
+			*p++=' ';
+#endif
+			*p++='f'; *p++='n'; *p++='#';
+			q=v->name;
+			while(*q) *p++=*q++;
+			*p++='=';
+			q=v->fn[v->pc-1].s;
+			while(*q) *p++=*q++;
+			*p++='\n';
+			*p++='\0';
+		}
+	}
+	*ep=0;
+	qsort((char *)env, nvar, sizeof ep[0], cmpenv);
+	return env;	
+}
+void Updenv(void){}
+void Execute(word *args, word *path)
+{
+	char **argv=mkargv(args);
+	char **env=mkenv();
+	char file[1024];
+	int nc;
+	Updenv();
+	for(;path;path=path->next){
+		nc=strlen(path->word);
+		if(nc<1024){
+			strcpy(file, path->word);
+			if(file[0]){
+				strcat(file, "/");
+				nc++;
+			}
+			if(nc+strlen(argv[1])<1024){
+				strcat(file, argv[1]);
+				execve(file, argv+1, env);
+			}
+			else werrstr("command name too long");
+		}
+	}
+	rerrstr(file, sizeof file);
+	pfmt(err, "%s: %s\n", argv[1], file);
+	efree((char *)argv);
+}
+#define	NDIR	256		/* shoud be a better way */
+int Globsize(char *p)
+{
+	ulong isglob=0, globlen=NDIR+1;
+	for(;*p;p++){
+		if(*p==GLOB){
+			p++;
+			if(*p!=GLOB) isglob++;
+			globlen+=*p=='*'?NDIR:1;
+		}
+		else
+			globlen++;
+	}
+	return isglob?globlen:0;
+}
+#define	NFD	50
+#define	NDBUF	32
+struct{
+	Dir	*dbuf;
+	int	i;
+	int	n;
+}dir[NFD];
+int Opendir(char *name)
+{
+	Dir *db;
+	int f;
+	f=open(name, 0);
+	if(f==-1)
+		return f;
+	db = dirfstat(f);
+	if(db!=nil && (db->mode&DMDIR)){
+		if(f<NFD){
+			dir[f].i=0;
+			dir[f].n=0;
+		}
+		free(db);
+		return f;
+	}
+	free(db);
+	close(f);
+	return -1;
+}
+int Readdir(int f, char *p)
+{
+	int n;
+	if(f<0 || f>=NFD)
+		return 0;
+	if(dir[f].i==dir[f].n){	/* read */
+		free(dir[f].dbuf);
+		dir[f].dbuf=0;
+		n=dirread(f, &dir[f].dbuf);
+		if(n>=0)
+			dir[f].n=n;
+		else
+			dir[f].n=0;
+		dir[f].i=0;
+	}
+	if(dir[f].i==dir[f].n)
+		return 0;
+	strcpy(p, dir[f].dbuf[dir[f].i].name);
+	dir[f].i++;
+	return 1;
+}
+void Closedir(int f){
+	if(f>=0 && f<NFD){
+		free(dir[f].dbuf);
+		dir[f].i=0;
+		dir[f].n=0;
+		dir[f].dbuf=0;
+	}
+	close(f);
+}
+int interrupted = 0;
+void
+notifyf(void *unused0, char *s)
+{
+	int i;
+	for(i=0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
+		if(strncmp(s, "sys: ", 5)!=0) interrupted=1;
+		goto Out;
+	}
+	pfmt(err, "rc: note: %s\n", s);
+	noted(NDFLT);
+	return;
+Out:
+	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
+		trap[i]++;
+		ntrap++;
+	}
+	if(ntrap>=32){	/* rc is probably in a trap loop */
+		pfmt(err, "rc: Too many traps (trap %s), aborting\n", s);
+		abort();
+	}
+	noted(NCONT);
+}
+void Trapinit(void){
+	notify(notifyf);
+}
+void Unlink(char *name)
+{
+	remove(name);
+}
+long Write(int fd, char *buf, long cnt)
+{
+	return write(fd, buf, (long)cnt);
+}
+long Read(int fd, char *buf, long cnt)
+{
+	return read(fd, buf, cnt);
+}
+long Seek(int fd, long cnt, long whence)
+{
+	return seek(fd, cnt, whence);
+}
+int Executable(char *file)
+{
+	Dir *statbuf;
+	int ret;
+
+	statbuf = dirstat(file);
+	if(statbuf == nil) return 0;
+	ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0);
+	free(statbuf);
+	return ret;
+}
+int Creat(char *file)
+{
+	return create(file, 1, 0666L);
+}
+int Dup(int a, int b){
+	return dup(a, b);
+}
+int Dup1(int unused0){
+	return -1;
+}
+void Exit(char *stat)
+{
+	Updenv();
+	setstatus(stat);
+	exits(truestatus()?"":getstatus());
+}
+int Eintr(void){
+	return interrupted;
+}
+void Noerror(void){
+	interrupted=0;
+}
+int
+Isatty(fd){
+	return isatty(fd);
+}
+void Abort(void){
+	pfmt(err, "aborting\n");
+	flush(err);
+	Exit("aborting");
+}
+void Memcpy(char *a, char *b, long n)
+{
+	memmove(a, b, (long)n);
+}
+void *Malloc(ulong n){
+	return malloc(n);
+}