Commit Diff


commit - 5993a8f2756bc455101a8c9ce95347d5050e7883
commit + f08fdedcee12c06e3ce9ac9bec363915978e8289
blob - /dev/null
blob + 748b9f8246ac5df0ebda29915ad3bfdabb00c667 (mode 644)
--- /dev/null
+++ src/cmd/rc/code.c
@@ -0,0 +1,430 @@
+#include "rc.h"
+#include "io.h"
+#include "exec.h"
+#include "fns.h"
+#include "getflags.h"
+#define	c0	t->child[0]
+#define	c1	t->child[1]
+#define	c2	t->child[2]
+int codep, ncode;
+#define	emitf(x) ((void)(codep!=ncode || morecode()), codebuf[codep].f=(x), codep++)
+#define	emiti(x) ((void)(codep!=ncode || morecode()), codebuf[codep].i=(x), codep++)
+#define	emits(x) ((void)(codep!=ncode || morecode()), codebuf[codep].s=(x), codep++)
+void stuffdot(int);
+char *fnstr(tree*);
+void outcode(tree*, int);
+void codeswitch(tree*, int);
+int iscase(tree*);
+code *codecopy(code*);
+void codefree(code*);
+int morecode(void){
+	ncode+=100;
+	codebuf=(code *)realloc((char *)codebuf, ncode*sizeof codebuf[0]);
+	if(codebuf==0) panic("Can't realloc %d bytes in morecode!",
+				ncode*sizeof codebuf[0]);
+	return 0;
+}
+void stuffdot(int a){
+	if(a<0 || codep<=a) panic("Bad address %d in stuffdot", a);
+	codebuf[a].i=codep;
+}
+int compile(tree *t)
+{
+	ncode=100;
+	codebuf=(code *)emalloc(ncode*sizeof codebuf[0]);
+	codep=0;
+	emiti(0);			/* reference count */
+	outcode(t, flag['e']?1:0);
+	if(nerror){
+		efree((char *)codebuf);
+		return 0;
+	}
+	readhere();
+	emitf(Xreturn);
+	emitf(0);
+	return 1;
+}
+void cleanhere(char *f)
+{
+	emitf(Xdelhere);
+	emits(strdup(f));
+}
+char *fnstr(tree *t)
+{
+	io *f=openstr();
+	char *v;
+	extern char nl;
+	char svnl=nl;
+	nl=';';
+	pfmt(f, "%t", t);
+	nl=svnl;
+	v=f->strp;
+	f->strp=0;
+	closeio(f);
+	return v;
+}
+void outcode(tree *t, int eflag)
+{
+	int p, q;
+	tree *tt;
+	if(t==0) return;
+	if(t->type!=NOT && t->type!=';') runq->iflast=0;
+	switch(t->type){
+	default:
+		pfmt(err, "bad type %d in outcode\n", t->type);
+		break;
+	case '$':
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xdol);
+		break;
+	case '"':
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xqdol);
+		break;
+	case SUB:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xmark);
+		outcode(c1, eflag);
+		emitf(Xsub);
+		break;
+	case '&':
+		emitf(Xasync);
+		p=emiti(0);
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+		break;
+	case ';':
+		outcode(c0, eflag);
+		outcode(c1, eflag);
+		break;
+	case '^':
+		emitf(Xmark);
+		outcode(c1, eflag);
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xconc);
+		break;
+	case '`':
+		emitf(Xbackq);
+		p=emiti(0);
+		outcode(c0, 0);
+		emitf(Xexit);
+		stuffdot(p);
+		break;
+	case ANDAND:
+		outcode(c0, 0);
+		emitf(Xtrue);
+		p=emiti(0);
+		outcode(c1, eflag);
+		stuffdot(p);
+		break;
+	case ARGLIST:
+		outcode(c1, eflag);
+		outcode(c0, eflag);
+		break;
+	case BANG:
+		outcode(c0, eflag);
+		emitf(Xbang);
+		break;
+	case PCMD:
+	case BRACE:
+		outcode(c0, eflag);
+		break;
+	case COUNT:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xcount);
+		break;
+	case FN:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		if(c1){
+			emitf(Xfn);
+			p=emiti(0);
+			emits(fnstr(c1));
+			outcode(c1, eflag);
+			emitf(Xunlocal);	/* get rid of $* */
+			emitf(Xreturn);
+			stuffdot(p);
+		}
+		else
+			emitf(Xdelfn);
+		break;
+	case IF:
+		outcode(c0, 0);
+		emitf(Xif);
+		p=emiti(0);
+		outcode(c1, eflag);
+		emitf(Xwastrue);
+		stuffdot(p);
+		break;
+	case NOT:
+		if(!runq->iflast) yyerror("`if not' does not follow `if(...)'");
+		emitf(Xifnot);
+		p=emiti(0);
+		outcode(c0, eflag);
+		stuffdot(p);
+		break;
+	case OROR:
+		outcode(c0, 0);
+		emitf(Xfalse);
+		p=emiti(0);
+		outcode(c1, eflag);
+		stuffdot(p);
+		break;
+	case PAREN:
+		outcode(c0, eflag);
+		break;
+	case SIMPLE:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xsimple);
+		if(eflag) emitf(Xeflag);
+		break;
+	case SUBSHELL:
+		emitf(Xsubshell);
+		p=emiti(0);
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+		if(eflag) emitf(Xeflag);
+		break;
+	case SWITCH:
+		codeswitch(t, eflag);
+		break;
+	case TWIDDLE:
+		emitf(Xmark);
+		outcode(c1, eflag);
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xmatch);
+		if(eflag) emitf(Xeflag);
+		break;
+	case WHILE:
+		q=codep;
+		outcode(c0, 0);
+		if(q==codep) emitf(Xsettrue);	/* empty condition == while(true) */
+		emitf(Xtrue);
+		p=emiti(0);
+		outcode(c1, eflag);
+		emitf(Xjump);
+		emiti(q);
+		stuffdot(p);
+		break;
+	case WORDS:
+		outcode(c1, eflag);
+		outcode(c0, eflag);
+		break;
+	case FOR:
+		emitf(Xmark);
+		if(c1){
+			outcode(c1, eflag);
+			emitf(Xglob);
+		}
+		else{
+			emitf(Xmark);
+			emitf(Xword);
+			emits(strdup("*"));
+			emitf(Xdol);
+		}
+		emitf(Xmark);		/* dummy value for Xlocal */
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xlocal);
+		p=emitf(Xfor);
+		q=emiti(0);
+		outcode(c2, eflag);
+		emitf(Xjump);
+		emiti(p);
+		stuffdot(q);
+		emitf(Xunlocal);
+		break;
+	case WORD:
+		emitf(Xword);
+		emits(strdup(t->str));
+		break;
+	case DUP:
+		if(t->rtype==DUPFD){
+			emitf(Xdup);
+			emiti(t->fd0);
+			emiti(t->fd1);
+		}
+		else{
+			emitf(Xclose);
+			emiti(t->fd0);
+		}
+		outcode(c1, eflag);
+		emitf(Xpopredir);
+		break;
+	case PIPEFD:
+		emitf(Xpipefd);
+		emiti(t->rtype);
+		p=emiti(0);
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+		break;
+	case REDIR:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xglob);
+		switch(t->rtype){
+		case APPEND:
+			emitf(Xappend);
+			break;
+		case WRITE:
+			emitf(Xwrite);
+			break;
+		case READ:
+		case HERE:
+			emitf(Xread);
+			break;
+		}
+		emiti(t->fd0);
+		outcode(c1, eflag);
+		emitf(Xpopredir);
+		break;
+	case '=':
+		tt=t;
+		for(;t && t->type=='=';t=c2);
+		if(t){
+			for(t=tt;t->type=='=';t=c2){
+				emitf(Xmark);
+				outcode(c1, eflag);
+				emitf(Xmark);
+				outcode(c0, eflag);
+				emitf(Xlocal);
+			}
+			t=tt;
+			outcode(c2, eflag);
+			for(;t->type=='=';t=c2) emitf(Xunlocal);
+		}
+		else{
+			for(t=tt;t;t=c2){
+				emitf(Xmark);
+				outcode(c1, eflag);
+				emitf(Xmark);
+				outcode(c0, eflag);
+				emitf(Xassign);
+			}
+		}
+		t=tt;	/* so tests below will work */
+		break;
+	case PIPE:
+		emitf(Xpipe);
+		emiti(t->fd0);
+		emiti(t->fd1);
+		p=emiti(0);
+		q=emiti(0);
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+		outcode(c1, eflag);
+		emitf(Xreturn);
+		stuffdot(q);
+		emitf(Xpipewait);
+		break;
+	}
+	if(t->type!=NOT && t->type!=';')
+		runq->iflast=t->type==IF;
+	else if(c0) runq->iflast=c0->type==IF;
+}
+/*
+ * switch code looks like this:
+ *	Xmark
+ *	(get switch value)
+ *	Xjump	1f
+ * out:	Xjump	leave
+ * 1:	Xmark
+ *	(get case values)
+ *	Xcase	1f
+ *	(commands)
+ *	Xjump	out
+ * 1:	Xmark
+ *	(get case values)
+ *	Xcase	1f
+ *	(commands)
+ *	Xjump	out
+ * 1:
+ * leave:
+ *	Xpopm
+ */
+void codeswitch(tree *t, int eflag)
+{
+	int leave;		/* patch jump address to leave switch */
+	int out;		/* jump here to leave switch */
+	int nextcase;	/* patch jump address to next case */
+	tree *tt;
+	if(c1->child[0]==nil
+	|| c1->child[0]->type!=';'
+	|| !iscase(c1->child[0]->child[0])){
+		yyerror("case missing in switch");
+		return;
+	}
+	emitf(Xmark);
+	outcode(c0, eflag);
+	emitf(Xjump);
+	nextcase=emiti(0);
+	out=emitf(Xjump);
+	leave=emiti(0);
+	stuffdot(nextcase);
+	t=c1->child[0];
+	while(t->type==';'){
+		tt=c1;
+		emitf(Xmark);
+		for(t=c0->child[0];t->type==ARGLIST;t=c0) outcode(c1, eflag);
+		emitf(Xcase);
+		nextcase=emiti(0);
+		t=tt;
+		for(;;){
+			if(t->type==';'){
+				if(iscase(c0)) break;
+				outcode(c0, eflag);
+				t=c1;
+			}
+			else{
+				if(!iscase(t)) outcode(t, eflag);
+				break;
+			}
+		}
+		emitf(Xjump);
+		emiti(out);
+		stuffdot(nextcase);
+	}
+	stuffdot(leave);
+	emitf(Xpopm);
+}
+int iscase(tree *t)
+{
+	if(t->type!=SIMPLE) return 0;
+	do t=c0; while(t->type==ARGLIST);
+	return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0;
+}
+code *codecopy(code *cp)
+{
+	cp[0].i++;
+	return cp;
+}
+void codefree(code *cp)
+{
+	code *p;
+	if(--cp[0].i!=0) return;
+	for(p=cp+1;p->f;p++){
+		if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite
+		|| p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse
+		|| p->f==Xfor || p->f==Xjump
+		|| p->f==Xsubshell || p->f==Xtrue) p++;
+		else if(p->f==Xdup || p->f==Xpipefd) p+=2;
+		else if(p->f==Xpipe) p+=4;
+		else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s);
+		else if(p->f==Xfn){
+			efree(p[2].s);
+			p+=2;
+		}
+	}
+	efree((char *)cp);
+}
blob - /dev/null
blob + ebed11d82e4b2728dd7e1dc26e43bcc75385b069 (mode 644)
--- /dev/null
+++ src/cmd/rc/exec.c
@@ -0,0 +1,902 @@
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+/*
+ * Start executing the given code at the given pc with the given redirection
+ */
+char *argv0="rc";
+void start(code *c, int pc, var *local)
+{
+	struct thread *p=new(struct thread);
+	p->code=codecopy(c);
+	p->pc=pc;
+	p->argv=0;
+	p->redir=p->startredir=runq?runq->redir:0;
+	p->local=local;
+	p->cmdfile=0;
+	p->cmdfd=0;
+	p->eof=0;
+	p->iflag=0;
+	p->lineno=1;
+	p->ret=runq;
+	runq=p;
+}
+word *newword(char *wd, word *next)
+{
+	word *p=new(word);
+	p->word=strdup(wd);
+	p->next=next;
+	return p;
+}
+void pushword(char *wd)
+{
+	if(runq->argv==0) panic("pushword but no argv!", 0);
+	runq->argv->words=newword(wd, runq->argv->words);
+}
+void popword(void){
+	word *p;
+	if(runq->argv==0) panic("popword but no argv!", 0);
+	p=runq->argv->words;
+	if(p==0) panic("popword but no word!", 0);
+	runq->argv->words=p->next;
+	efree(p->word);
+	efree((char *)p);
+}
+void freelist(word *w)
+{
+	word *nw;
+	while(w){
+		nw=w->next;
+		efree(w->word);
+		efree((char *)w);
+		w=nw;
+	}
+}
+void pushlist(void){
+	list *p=new(list);
+	p->next=runq->argv;
+	p->words=0;
+	runq->argv=p;
+}
+void poplist(void){
+	list *p=runq->argv;
+	if(p==0) panic("poplist but no argv", 0);
+	freelist(p->words);
+	runq->argv=p->next;
+	efree((char *)p);
+}
+int count(word *w)
+{
+	int n;
+	for(n=0;w;n++) w=w->next;
+	return n;
+}
+void pushredir(int type, int from, int to){
+	redir * rp=new(redir);
+	rp->type=type;
+	rp->from=from;
+	rp->to=to;
+	rp->next=runq->redir;
+	runq->redir=rp;
+}
+var *newvar(char *name, var *next)
+{
+	var *v=new(var);
+	v->name=name;
+	v->val=0;
+	v->fn=0;
+	v->changed=0;
+	v->fnchanged=0;
+	v->next=next;
+	return v;
+}
+/*
+ * get command line flags, initialize keywords & traps.
+ * get values from environment.
+ * set $pid, $cflag, $*
+ * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*)
+ * start interpreting code
+ */
+int
+main(int argc, char *argv[])
+{
+	code bootstrap[32];
+	char num[12], *rcmain;
+	int i;
+
+	argc=getflags(argc, argv, "srdiIlxepvVc:1m:1[command]", 1);
+	if(argc==-1) usage("[file [arg ...]]");
+	if(argv[0][0]=='-') flag['l']=flagset;
+	if(flag['I']) flag['i'] = 0;
+	else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
+	rcmain=flag['m']?flag['m'][0]:Rcmain(); 
+	err=openfd(2);
+	kinit();
+	Trapinit();
+	Vinit();
+	itoa(num, mypid=getpid());
+	setvar("pid", newword(num, (word *)0));
+	setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0)
+				:(word *)0);
+	setvar("rcname", newword(argv[0], (word *)0));
+	i=0;
+	bootstrap[i++].i=1;
+	bootstrap[i++].f=Xmark;
+	bootstrap[i++].f=Xword;
+	bootstrap[i++].s="*";
+	bootstrap[i++].f=Xassign;
+	bootstrap[i++].f=Xmark;
+	bootstrap[i++].f=Xmark;
+	bootstrap[i++].f=Xword;
+	bootstrap[i++].s="*";
+	bootstrap[i++].f=Xdol;
+	bootstrap[i++].f=Xword;
+	bootstrap[i++].s=rcmain;
+	bootstrap[i++].f=Xword;
+	bootstrap[i++].s=".";
+	bootstrap[i++].f=Xsimple;
+	bootstrap[i++].f=Xexit;
+	bootstrap[i].i=0;
+	start(bootstrap, 1, (var *)0);
+	/* prime bootstrap argv */
+	pushlist();
+	argv0 = strdup(argv[0]);
+	for(i=argc-1;i!=0;--i) pushword(argv[i]);
+	for(;;){
+		if(flag['r']) pfnc(err, runq);
+		runq->pc++;
+		(*runq->code[runq->pc-1].f)();
+		if(ntrap) dotrap();
+	}
+}
+/*
+ * Opcode routines
+ * Arguments on stack (...)
+ * Arguments in line [...]
+ * Code in line with jump around {...}
+ *
+ * Xappend(file)[fd]			open file to append
+ * Xassign(name, val)			assign val to name
+ * Xasync{... Xexit}			make thread for {}, no wait
+ * Xbackq{... Xreturn}			make thread for {}, push stdout
+ * Xbang				complement condition
+ * Xcase(pat, value){...}		exec code on match, leave (value) on
+ * 					stack
+ * Xclose[i]				close file descriptor
+ * Xconc(left, right)			concatenate, push results
+ * Xcount(name)				push var count
+ * Xdelfn(name)				delete function definition
+ * Xdeltraps(names)			delete named traps
+ * Xdol(name)				get variable value
+ * Xqdol(name)				concatenate variable components
+ * Xdup[i j]				dup file descriptor
+ * Xexit				rc exits with status
+ * Xfalse{...}				execute {} if false
+ * Xfn(name){... Xreturn}			define function
+ * Xfor(var, list){... Xreturn}		for loop
+ * Xjump[addr]				goto
+ * Xlocal(name, val)			create local variable, assign value
+ * Xmark				mark stack
+ * Xmatch(pat, str)			match pattern, set status
+ * Xpipe[i j]{... Xreturn}{... Xreturn}	construct a pipe between 2 new threads,
+ * 					wait for both
+ * Xpipefd[type]{... Xreturn}		connect {} to pipe (input or output,
+ * 					depending on type), push /dev/fd/??
+ * Xpopm(value)				pop value from stack
+ * Xread(file)[fd]			open file to read
+ * Xsettraps(names){... Xreturn}		define trap functions
+ * Xshowtraps				print trap list
+ * Xsimple(args)			run command and wait
+ * Xreturn				kill thread
+ * Xsubshell{... Xexit}			execute {} in a subshell and wait
+ * Xtrue{...}				execute {} if true
+ * Xunlocal				delete local variable
+ * Xword[string]			push string
+ * Xwrite(file)[fd]			open file to write
+ */
+void Xappend(void){
+	char *file;
+	int f;
+	switch(count(runq->argv->words)){
+	default: Xerror1(">> requires singleton"); return;
+	case 0: Xerror1(">> requires file"); return;
+	case 1: break;
+	}
+	file=runq->argv->words->word;
+	if((f=open(file, 1))<0 && (f=Creat(file))<0){
+		pfmt(err, "%s: ", file);
+		Xerror("can't open");
+		return;
+	}
+	Seek(f, 0L, 2);
+	pushredir(ROPEN, f, runq->code[runq->pc].i);
+	runq->pc++;
+	poplist();
+}
+void Xasync(void){
+	int null=open("/dev/null", 0);
+	int pid;
+	char npid[10];
+	if(null<0){
+		Xerror("Can't open /dev/null\n");
+		return;
+	}
+	switch(pid=rfork(RFFDG|RFPROC|RFNOTEG)){
+	case -1:
+		close(null);
+		Xerror("try again");
+		break;
+	case 0:
+		pushredir(ROPEN, null, 0);
+		start(runq->code, runq->pc+1, runq->local);
+		runq->ret=0;
+		break;
+	default:
+		close(null);
+		runq->pc=runq->code[runq->pc].i;
+		itoa(npid, pid);
+		setvar("apid", newword(npid, (word *)0));
+		break;
+	}
+}
+void Xsettrue(void){
+	setstatus("");
+}
+void Xbang(void){
+	setstatus(truestatus()?"false":"");
+}
+void Xclose(void){
+	pushredir(RCLOSE, runq->code[runq->pc].i, 0);
+	runq->pc++;
+}
+void Xdup(void){
+	pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
+	runq->pc+=2;
+}
+void Xeflag(void){
+	if(eflagok && !truestatus()) Xexit();
+}
+void Xexit(void){
+	struct var *trapreq;
+	struct word *starval;
+	static int beenhere=0;
+	if(getpid()==mypid && !beenhere){
+		trapreq=vlook("sigexit");
+		if(trapreq->fn){
+			beenhere=1;
+			--runq->pc;
+			starval=vlook("*")->val;
+			start(trapreq->fn, trapreq->pc, (struct var *)0);
+			runq->local=newvar(strdup("*"), runq->local);
+			runq->local->val=copywords(starval, (struct word *)0);
+			runq->local->changed=1;
+			runq->redir=runq->startredir=0;
+			return;
+		}
+	}
+	Exit(getstatus());
+}
+void Xfalse(void){
+	if(truestatus()) runq->pc=runq->code[runq->pc].i;
+	else runq->pc++;
+}
+int ifnot;		/* dynamic if not flag */
+void Xifnot(void){
+	if(ifnot)
+		runq->pc++;
+	else
+		runq->pc=runq->code[runq->pc].i;
+}
+void Xjump(void){
+	runq->pc=runq->code[runq->pc].i;
+}
+void Xmark(void){
+	pushlist();
+}
+void Xpopm(void){
+	poplist();
+}
+void Xread(void){
+	char *file;
+	int f;
+	switch(count(runq->argv->words)){
+	default: Xerror1("< requires singleton\n"); return;
+	case 0: Xerror1("< requires file\n"); return;
+	case 1: break;
+	}
+	file=runq->argv->words->word;
+	if((f=open(file, 0))<0){
+		pfmt(err, "%s: ", file);
+		Xerror("can't open");
+		return;
+	}
+	pushredir(ROPEN, f, runq->code[runq->pc].i);
+	runq->pc++;
+	poplist();
+}
+void turfredir(void){
+	while(runq->redir!=runq->startredir)
+		Xpopredir();
+}
+void Xpopredir(void){
+	struct redir *rp=runq->redir;
+	if(rp==0) panic("turfredir null!", 0);
+	runq->redir=rp->next;
+	if(rp->type==ROPEN) close(rp->from);
+	efree((char *)rp);
+}
+void Xreturn(void){
+	struct thread *p=runq;
+	turfredir();
+	while(p->argv) poplist();
+	codefree(p->code);
+	runq=p->ret;
+	efree((char *)p);
+	if(runq==0) Exit(getstatus());
+}
+void Xtrue(void){
+	if(truestatus()) runq->pc++;
+	else runq->pc=runq->code[runq->pc].i;
+}
+void Xif(void){
+	ifnot=1;
+	if(truestatus()) runq->pc++;
+	else runq->pc=runq->code[runq->pc].i;
+}
+void Xwastrue(void){
+	ifnot=0;
+}
+void Xword(void){
+	pushword(runq->code[runq->pc++].s);
+}
+void Xwrite(void){
+	char *file;
+	int f;
+	switch(count(runq->argv->words)){
+	default: Xerror1("> requires singleton\n"); return;
+	case 0: Xerror1("> requires file\n"); return;
+	case 1: break;
+	}
+	file=runq->argv->words->word;
+	if((f=Creat(file))<0){
+		pfmt(err, "%s: ", file);
+		Xerror("can't open");
+		return;
+	}
+	pushredir(ROPEN, f, runq->code[runq->pc].i);
+	runq->pc++;
+	poplist();
+}
+char *list2str(word *words){
+	char *value, *s, *t;
+	int len=0;
+	word *ap;
+	for(ap=words;ap;ap=ap->next)
+		len+=1+strlen(ap->word);
+	value=emalloc(len+1);
+	s=value;
+	for(ap=words;ap;ap=ap->next){
+		for(t=ap->word;*t;) *s++=*t++;
+		*s++=' ';
+	}
+	if(s==value) *s='\0';
+	else s[-1]='\0';
+	return value;
+}
+void Xmatch(void){
+	word *p;
+	char *subject;
+	subject=list2str(runq->argv->words);
+	setstatus("no match");
+	for(p=runq->argv->next->words;p;p=p->next)
+		if(match(subject, p->word, '\0')){
+			setstatus("");
+			break;
+		}
+	efree(subject);
+	poplist();
+	poplist();
+}
+void Xcase(void){
+	word *p;
+	char *s;
+	int ok=0;
+	s=list2str(runq->argv->next->words);
+	for(p=runq->argv->words;p;p=p->next){
+		if(match(s, p->word, '\0')){
+			ok=1;
+			break;
+		}
+	}
+	efree(s);
+	if(ok)
+		runq->pc++;
+	else
+		runq->pc=runq->code[runq->pc].i;
+	poplist();
+}
+word *conclist(word *lp, word *rp, word *tail)
+{
+	char *buf;
+	word *v;
+	if(lp->next || rp->next)
+		tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next,
+			tail);
+	buf=emalloc(strlen(lp->word)+strlen(rp->word)+1);
+	strcpy(buf, lp->word);
+	strcat(buf, rp->word);
+	v=newword(buf, tail);
+	efree(buf);
+	return v;
+}
+void Xconc(void){
+	word *lp=runq->argv->words;
+	word *rp=runq->argv->next->words;
+	word *vp=runq->argv->next->next->words;
+	int lc=count(lp), rc=count(rp);
+	if(lc!=0 || rc!=0){
+		if(lc==0 || rc==0){
+			Xerror1("null list in concatenation");
+			return;
+		}
+		if(lc!=1 && rc!=1 && lc!=rc){
+			Xerror1("mismatched list lengths in concatenation");
+			return;
+		}
+		vp=conclist(lp, rp, vp);
+	}
+	poplist();
+	poplist();
+	runq->argv->words=vp;
+}
+void Xassign(void){
+	var *v;
+	if(count(runq->argv->words)!=1){
+		Xerror1("variable name not singleton!");
+		return;
+	}
+	deglob(runq->argv->words->word);
+	v=vlook(runq->argv->words->word);
+	poplist();
+	globlist();
+	freewords(v->val);
+	v->val=runq->argv->words;
+	v->changed=1;
+	runq->argv->words=0;
+	poplist();
+}
+/*
+ * copy arglist a, adding the copy to the front of tail
+ */
+word *copywords(word *a, word *tail)
+{
+	word *v=0, **end;
+	for(end=&v;a;a=a->next,end=&(*end)->next)
+		*end=newword(a->word, 0);
+	*end=tail;
+	return v;
+}
+void Xdol(void){
+	word *a, *star;
+	char *s, *t;
+	int n;
+	if(count(runq->argv->words)!=1){
+		Xerror1("variable name not singleton!");
+		return;
+	}
+	s=runq->argv->words->word;
+	deglob(s);
+	n=0;
+	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
+	a=runq->argv->next->words;
+	if(n==0 || *t)
+		a=copywords(vlook(s)->val, a);
+	else{
+		star=vlook("*")->val;
+		if(star && 1<=n && n<=count(star)){
+			while(--n) star=star->next;
+			a=newword(star->word, a);
+		}
+	}
+	poplist();
+	runq->argv->words=a;
+}
+void Xqdol(void){
+	word *a, *p;
+	char *s;
+	int n;
+	if(count(runq->argv->words)!=1){
+		Xerror1("variable name not singleton!");
+		return;
+	}
+	s=runq->argv->words->word;
+	deglob(s);
+	a=vlook(s)->val;
+	poplist();
+	n=count(a);
+	if(n==0){
+		pushword("");
+		return;
+	}
+	for(p=a;p;p=p->next) n+=strlen(p->word);
+	s=emalloc(n);
+	if(a){
+		strcpy(s, a->word);
+		for(p=a->next;p;p=p->next){
+			strcat(s, " ");
+			strcat(s, p->word);
+		}
+	}
+	else
+		s[0]='\0';
+	pushword(s);
+	efree(s);
+}
+word *subwords(word *val, int len, word *sub, word *a)
+{
+	int n;
+	char *s;
+	if(!sub) return a;
+	a=subwords(val, len, sub->next, a);
+	s=sub->word;
+	deglob(s);
+	n=0;
+	while('0'<=*s && *s<='9') n=n*10+ *s++ -'0';
+	if(n<1 || len<n) return a;
+	for(;n!=1;--n) val=val->next;
+	return newword(val->word, a);
+}
+void Xsub(void){
+	word *a, *v;
+	char *s;
+	if(count(runq->argv->next->words)!=1){
+		Xerror1("variable name not singleton!");
+		return;
+	}
+	s=runq->argv->next->words->word;
+	deglob(s);
+	a=runq->argv->next->next->words;
+	v=vlook(s)->val;
+	a=subwords(v, count(v), runq->argv->words, a);
+	poplist();
+	poplist();
+	runq->argv->words=a;
+}
+void Xcount(void){
+	word *a;
+	char *s, *t;
+	int n;
+	char num[12];
+	if(count(runq->argv->words)!=1){
+		Xerror1("variable name not singleton!");
+		return;
+	}
+	s=runq->argv->words->word;
+	deglob(s);
+	n=0;
+	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
+	if(n==0 || *t){
+		a=vlook(s)->val;
+		itoa(num, count(a));
+	}
+	else{
+		a=vlook("*")->val;
+		itoa(num, a && 1<=n && n<=count(a)?1:0);
+	}
+	poplist();
+	pushword(num);
+}
+void Xlocal(void){
+	if(count(runq->argv->words)!=1){
+		Xerror1("variable name must be singleton\n");
+		return;
+	}
+	deglob(runq->argv->words->word);
+	runq->local=newvar(strdup(runq->argv->words->word), runq->local);
+	runq->local->val=copywords(runq->argv->next->words, (word *)0);
+	runq->local->changed=1;
+	poplist();
+	poplist();
+}
+void Xunlocal(void){
+	var *v=runq->local, *hid;
+	if(v==0) panic("Xunlocal: no locals!", 0);
+	runq->local=v->next;
+	hid=vlook(v->name);
+	hid->changed=1;
+	efree(v->name);
+	freewords(v->val);
+	efree((char *)v);
+}
+void freewords(word *w)
+{
+	word *nw;
+	while(w){
+		efree(w->word);
+		nw=w->next;
+		efree((char *)w);
+		w=nw;
+	}
+}
+void Xfn(void){
+	var *v;
+	word *a;
+	int end;
+	end=runq->code[runq->pc].i;
+	for(a=runq->argv->words;a;a=a->next){
+		v=gvlook(a->word);
+		if(v->fn) codefree(v->fn);
+		v->fn=codecopy(runq->code);
+		v->pc=runq->pc+2;
+		v->fnchanged=1;
+	}
+	runq->pc=end;
+	poplist();
+}
+void Xdelfn(void){
+	var *v;
+	word *a;
+	for(a=runq->argv->words;a;a=a->next){
+		v=gvlook(a->word);
+		if(v->fn) codefree(v->fn);
+		v->fn=0;
+		v->fnchanged=1;
+	}
+	poplist();
+}
+void Xpipe(void){
+	struct thread *p=runq;
+	int pc=p->pc, forkid;
+	int lfd=p->code[pc++].i;
+	int rfd=p->code[pc++].i;
+	int pfd[2];
+	if(pipe(pfd)<0){
+		Xerror("can't get pipe");
+		return;
+	}
+	switch(forkid=fork()){
+	case -1:
+		Xerror("try again");
+		break;
+	case 0:
+		start(p->code, pc+2, runq->local);
+		runq->ret=0;
+		close(pfd[PRD]);
+		pushredir(ROPEN, pfd[PWR], lfd);
+		break;
+	default:
+		start(p->code, p->code[pc].i, runq->local);
+		close(pfd[PWR]);
+		pushredir(ROPEN, pfd[PRD], rfd);
+		p->pc=p->code[pc+1].i;
+		p->pid=forkid;
+		break;
+	}
+}
+char *concstatus(char *s, char *t)
+{
+	static char v[NSTATUS+1];
+	int n=strlen(s);
+	strncpy(v, s, NSTATUS);
+	if(n<NSTATUS){
+		v[n]='|';
+		strncpy(v+n+1, t, NSTATUS-n-1);
+	}
+	v[NSTATUS]='\0';
+	return v;
+}
+void Xpipewait(void){
+	char status[NSTATUS+1];
+	if(runq->pid==-1)
+		setstatus(concstatus(runq->status, getstatus()));
+	else{
+		strncpy(status, getstatus(), NSTATUS);
+		status[NSTATUS]='\0';
+		Waitfor(runq->pid, 1);
+		runq->pid=-1;
+		setstatus(concstatus(getstatus(), status));
+	}
+}
+void Xrdcmds(void){
+	struct thread *p=runq;
+	word *prompt;
+	flush(err);
+	nerror=0;
+	if(flag['s'] && !truestatus())
+		pfmt(err, "status=%v\n", vlook("status")->val);
+	if(runq->iflag){
+		prompt=vlook("prompt")->val;
+		if(prompt)
+			promptstr=prompt->word;
+		else
+			promptstr="% ";
+	}
+	Noerror();
+	if(yyparse()){
+		if(!p->iflag || p->eof && !Eintr()){
+			if(p->cmdfile) efree(p->cmdfile);
+			closeio(p->cmdfd);
+			Xreturn();	/* should this be omitted? */
+		}
+		else{
+			if(Eintr()){
+				pchr(err, '\n');
+				p->eof=0;
+			}
+			--p->pc;	/* go back for next command */
+		}
+	}
+	else{
+		ntrap = 0;	/* avoid double-interrupts during blocked writes */
+		--p->pc;	/* re-execute Xrdcmds after codebuf runs */
+		start(codebuf, 1, runq->local);
+	}
+	freenodes();
+}
+void Xerror(char *s)
+{
+	if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
+		pfmt(err, "rc: %s: %r\n", s);
+	else
+		pfmt(err, "rc (%s): %s: %r\n", argv0, s);
+	flush(err);
+	while(!runq->iflag) Xreturn();
+}
+void Xerror1(char *s)
+{
+	if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
+		pfmt(err, "rc: %s\n", s);
+	else
+		pfmt(err, "rc (%s): %s\n", argv0, s);
+	flush(err);
+	while(!runq->iflag) Xreturn();
+}
+void Xbackq(void){
+	char wd[8193];
+	int c;
+	char *s, *ewd=&wd[8192], *stop;
+	struct io *f;
+	var *ifs=vlook("ifs");
+	word *v, *nextv;
+	int pfd[2];
+	int pid;
+	stop=ifs->val?ifs->val->word:"";
+	if(pipe(pfd)<0){
+		Xerror("can't make pipe");
+		return;
+	}
+	switch(pid=fork()){
+	case -1: Xerror("try again");
+		close(pfd[PRD]);
+		close(pfd[PWR]);
+		return;
+	case 0:
+		close(pfd[PRD]);
+		start(runq->code, runq->pc+1, runq->local);
+		pushredir(ROPEN, pfd[PWR], 1);
+		return;
+	default:
+		close(pfd[PWR]);
+		f=openfd(pfd[PRD]);
+		s=wd;
+		v=0;
+		while((c=rchr(f))!=EOF){
+			if(strchr(stop, c) || s==ewd){
+				if(s!=wd){
+					*s='\0';
+					v=newword(wd, v);
+					s=wd;
+				}
+			}
+			else *s++=c;
+		}
+		if(s!=wd){
+			*s='\0';
+			v=newword(wd, v);
+		}
+		closeio(f);
+		Waitfor(pid, 0);
+		/* v points to reversed arglist -- reverse it onto argv */
+		while(v){
+			nextv=v->next;
+			v->next=runq->argv->words;
+			runq->argv->words=v;
+			v=nextv;
+		}
+		runq->pc=runq->code[runq->pc].i;
+		return;
+	}
+}
+/*
+ * Who should wait for the exit from the fork?
+ */
+void Xpipefd(void){
+	struct thread *p=runq;
+	int pc=p->pc;
+	char name[40];
+	int pfd[2];
+	int sidefd, mainfd;
+	if(pipe(pfd)<0){
+		Xerror("can't get pipe");
+		return;
+	}
+	if(p->code[pc].i==READ){
+		sidefd=pfd[PWR];
+		mainfd=pfd[PRD];
+	}
+	else{
+		sidefd=pfd[PRD];
+		mainfd=pfd[PWR];
+	}
+	switch(fork()){
+	case -1:
+		Xerror("try again");
+		break;
+	case 0:
+		start(p->code, pc+2, runq->local);
+		close(mainfd);
+		pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0);
+		runq->ret=0;
+		break;
+	default:
+		close(sidefd);
+		pushredir(ROPEN, mainfd, mainfd);	/* isn't this a noop? */
+		strcpy(name, Fdprefix);
+		itoa(name+strlen(name), mainfd);
+		pushword(name);
+		p->pc=p->code[pc+1].i;
+		break;
+	}
+}
+void Xsubshell(void){
+	int pid;
+	switch(pid=fork()){
+	case -1:
+		Xerror("try again");
+		break;
+	case 0:
+		start(runq->code, runq->pc+1, runq->local);
+		runq->ret=0;
+		break;
+	default:
+		Waitfor(pid, 1);
+		runq->pc=runq->code[runq->pc].i;
+		break;
+	}
+}
+void setstatus(char *s)
+{
+	setvar("status", newword(s, (word *)0));
+}
+char *getstatus(void){
+	var *status=vlook("status");
+	return status->val?status->val->word:"";
+}
+int truestatus(void){
+	char *s;
+	for(s=getstatus();*s;s++)
+		if(*s!='|' && *s!='0') return 0;
+	return 1;
+}
+void Xdelhere(void){
+	Unlink(runq->code[runq->pc++].s);
+}
+void Xfor(void){
+	if(runq->argv->words==0){
+		poplist();
+		runq->pc=runq->code[runq->pc].i;
+	}
+	else{
+		freelist(runq->local->val);
+		runq->local->val=runq->argv->words;
+		runq->local->changed=1;
+		runq->argv->words=runq->argv->words->next;
+		runq->local->val->next=0;
+		runq->pc++;
+	}
+}
+void Xglob(void){
+	globlist();
+}
blob - /dev/null
blob + fbfe2dbb137ada9c6e63ed6f94e444ba0793f303 (mode 644)
--- /dev/null
+++ src/cmd/rc/exec.h
@@ -0,0 +1,71 @@
+/*
+ * Definitions used in the interpreter
+ */
+extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void);
+extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void);
+extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void);
+extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void);
+extern void Xrdfn(void), Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void);
+extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void);
+extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void);
+extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void);
+extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void);
+extern void Xerror(char*);
+extern void Xerror1(char*);
+/*
+ * word lists are in correct order,
+ * i.e. word0->word1->word2->word3->0
+ */
+struct word{
+	char *word;
+	word *next;
+};
+struct list{
+	word *words;
+	list *next;
+};
+word *newword(char *, word *), *copywords(word *, word *);
+struct redir{
+	char type;			/* what to do */
+	short from, to;			/* what to do it to */
+	struct redir *next;		/* what else to do (reverse order) */
+};
+#define	NSTATUS	ERRMAX			/* length of status (from plan 9) */
+/*
+ * redir types
+ */
+#define	ROPEN	1			/* dup2(from, to); close(from); */
+#define	RDUP	2			/* dup2(from, to); */
+#define	RCLOSE	3			/* close(from); */
+struct thread{
+	union code *code;		/* code for this thread */
+	int pc;				/* code[pc] is the next instruction */
+	struct list *argv;		/* argument stack */
+	struct redir *redir;		/* redirection stack */
+	struct redir *startredir;	/* redir inheritance point */
+	struct var *local;		/* list of local variables */
+	char *cmdfile;			/* file name in Xrdcmd */
+	struct io *cmdfd;		/* file descriptor for Xrdcmd */
+	int iflast;			/* static `if not' checking */
+	int eof;			/* is cmdfd at eof? */
+	int iflag;			/* interactive? */
+	int lineno;			/* linenumber */
+	int pid;			/* process for Xpipewait to wait for */
+	char status[NSTATUS];		/* status for Xpipewait */
+	tree *treenodes;		/* tree nodes created by this process */
+	thread *ret;		/* who continues when this finishes */
+};
+thread *runq;
+code *codecopy(code*);
+code *codebuf;				/* compiler output */
+int ntrap;				/* number of outstanding traps */
+int trap[NSIG];				/* number of outstanding traps per type */
+extern struct builtin{
+	char *name;
+	void (*fnc)(void);
+}Builtin[];
+int eflagok;			/* kludge flag so that -e doesn't exit in startup */
+void execcd(void), execwhatis(void), execeval(void), execexec(void);
+void execexit(void), execshift(void);
+void execwait(void), execumask(void), execdot(void), execflag(void);
+void execfunc(var*), execcmds(io *);
blob - /dev/null
blob + e6b91e34b35b81c4b9642dcc51c869d52918eeed (mode 644)
--- /dev/null
+++ src/cmd/rc/fmtquote.c
@@ -0,0 +1,162 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <u.h>
+#include <libc.h>
+#include "fmt.h"
+#include "fmtdef.h"
+
+extern int (*doquote)(int);
+
+/*
+ * How many bytes of output UTF will be produced by quoting (if necessary) this string?
+ * How many runes? How much of the input will be consumed?
+ * The parameter q is filled in by _quotesetup.
+ * The string may be UTF or Runes (s or r).
+ * Return count does not include NUL.
+ * Terminate the scan at the first of:
+ *	NUL in input
+ *	count exceeded in input
+ *	count exceeded on output
+ * *ninp is set to number of input bytes accepted.
+ * nin may be <0 initially, to avoid checking input by count.
+ */
+void
+__quotesetup(char *s, int nin, int nout, Quoteinfo *q, int sharp)
+{
+	int c;
+
+	q->quoted = 0;
+	q->nbytesout = 0;
+	q->nrunesout = 0;
+	q->nbytesin = 0;
+	q->nrunesin = 0;
+	if(sharp || nin==0 || *s=='\0'){
+		if(nout < 2)
+			return;
+		q->quoted = 1;
+		q->nbytesout = 2;
+		q->nrunesout = 2;
+	}
+	for(; nin!=0; nin-=1){
+		c = *s;
+
+		if(c == '\0')
+			break;
+		if(q->nrunesout+1 > nout)
+			break;
+
+		if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){
+			if(!q->quoted){
+				if(1+q->nrunesout+1+1 > nout)	/* no room for quotes */
+					break;
+				q->nrunesout += 2;	/* include quotes */
+				q->nbytesout += 2;	/* include quotes */
+				q->quoted = 1;
+			}
+			if(c == '\'')	{
+				q->nbytesout++;
+				q->nrunesout++;	/* quotes reproduce as two characters */
+			}
+		}
+
+		/* advance input */
+		s++;
+		q->nbytesin++;
+		q->nrunesin++;
+
+		/* advance output */
+		q->nbytesout++;
+		q->nrunesout++;
+	}
+}
+
+static int
+qstrfmt(char *sin, Quoteinfo *q, Fmt *f)
+{
+	int r;
+	char *t, *s, *m, *me;
+	ulong fl;
+	int nc, w;
+
+	m = sin;
+	me = m + q->nbytesin;
+
+	w = f->width;
+	fl = f->flags;
+	if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
+		return -1;
+	t = f->to;
+	s = f->stop;
+	FMTCHAR(f, t, s, '\'');
+	for(nc = q->nrunesin; nc > 0; nc--){
+		r = *(uchar*)m++;
+		FMTCHAR(f, t, s, r);
+		if(r == '\'')
+			FMTCHAR(f, t, s, r);
+	}
+
+	FMTCHAR(f, t, s, '\'');
+	f->nfmt += t - (char *)f->to;
+	f->to = t;
+	if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
+		return -1;
+	return 0;
+}
+
+int
+__quotestrfmt(int runesin, Fmt *f)
+{
+	int outlen;
+	char *s;
+	Quoteinfo q;
+
+	f->flags &= ~FmtPrec;	/* ignored for %q %Q, so disable for %s %S in easy case */
+	s = va_arg(f->args, char *);
+	if(!s)
+		return __fmtcpy(f, "<nil>", 5, 5);
+
+	if(f->flush)
+		outlen = 0x7FFFFFFF;	/* if we can flush, no output limit */
+	else
+		outlen = (char*)f->stop - (char*)f->to;
+
+	__quotesetup(s, -1, outlen, &q, f->flags&FmtSharp);
+
+	if(!q.quoted)
+		return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
+	return qstrfmt(s, &q, f);
+}
+
+int
+quotestrfmt(Fmt *f)
+{
+	return __quotestrfmt(0, f);
+}
+
+void
+quotefmtinstall(void)
+{
+	fmtinstall('q', quotestrfmt);
+}
+
+int
+__needsquotes(char *s, int *quotelenp)
+{
+	Quoteinfo q;
+
+	__quotesetup(s, -1, 0x7FFFFFFF, &q, 0);
+	*quotelenp = q.nbytesout;
+
+	return q.quoted;
+}
blob - /dev/null
blob + 4c8be104c4041c9eab5037faf01680e04a4a4454 (mode 644)
--- /dev/null
+++ src/cmd/rc/fns.h
@@ -0,0 +1,58 @@
+void	Abort(void);
+void	Closedir(int);
+int	Creat(char*);
+int	Dup(int, int);
+int	Dup1(int);
+int	Eintr(void);
+int	Executable(char*);
+void	Execute(word*,  word*);
+void	Exit(char*);
+int	Globsize(char*);
+int	Isatty(int);
+void	Memcpy(char*, char*, long);
+void	Noerror(void);
+int	Opendir(char*);
+long	Read(int, char*, long);
+int	Readdir(int, char*);
+long	Seek(int, long, long);
+void	Trapinit(void);
+void	Unlink(char*);
+void	Updenv(void);
+void	Vinit(void);
+int	Waitfor(int, int);
+long	Write(int, char*, long);
+int	advance(void);
+int	back(int);
+void	cleanhere(char*);
+void	codefree(code*);
+int	compile(tree*);
+char *	list2str(word*);
+int	count(word*);
+void	deglob(char*);
+void	dotrap(void);
+void	freenodes(void);
+void	freewords(word*);
+void	globlist(void);
+int	idchr(int);
+void	itoa(char*, long);
+void	kinit(void);
+int	match(char*, char*, int);
+int	matchfn(char*, char*);
+void	panic(char*, int);
+void	poplist(void);
+void	popword(void);
+void	pprompt(void);
+void	pushlist(void);
+void	pushredir(int, int, int);
+void	pushword(char*);
+void	readhere(void);
+void	setstatus(char*);
+void	setvar(char*, word*);
+void	skipnl(void);
+void	start(code*, int, var*);
+int	truestatus(void);
+void	usage(char*);
+int	wordchr(int);
+void	yyerror(char*);
+int	yylex(void);
+int	yyparse(void);
blob - /dev/null
blob + c452067fd511aead421cdf50ea254dff05bad8ae (mode 644)
--- /dev/null
+++ src/cmd/rc/getflags.c
@@ -0,0 +1,217 @@
+/*% cyntax -DTEST % && cc -DTEST -go # %
+ */
+#include "rc.h"
+#include "getflags.h"
+#include "fns.h"
+char *flagset[]={"<flag>"};
+char **flag[NFLAG];
+char cmdline[NCMDLINE+1];
+char *cmdname;
+static char *flagarg="";
+static void reverse(char**, char**);
+static int scanflag(int, char*);
+static void errn(char*, int);
+static void errs(char*);
+static void errc(int);
+static int reason;
+#define	RESET	1
+#define	FEWARGS	2
+#define	FLAGSYN	3
+#define	BADFLAG	4
+static int badflag;
+int getflags(int argc, char *argv[], char *flags, int stop)
+{
+	char *s, *t;
+	int i, j, c, count;
+	flagarg=flags;
+	if(cmdname==0) cmdname=argv[0];
+	s=cmdline;
+	for(i=0;i!=argc;i++){
+		for(t=argv[i];*t;t++)
+			if(s!=&cmdline[NCMDLINE])
+				*s++=*t;
+		if(i!=argc-1 && s!=&cmdline[NCMDLINE])
+			*s++=' ';
+	}
+	*s='\0';
+	i=1;
+	while(i!=argc){
+		if(argv[i][0]!='-' || argv[i][1]=='\0'){
+			if(stop) return argc;
+			i++;
+			continue;
+		}
+		s=argv[i]+1;
+		while(*s){
+			c=*s++;
+			count=scanflag(c, flags);
+			if(count==-1) return -1;
+			if(flag[c]){ reason=RESET; badflag=c; return -1; }
+			if(count==0){
+				flag[c]=flagset;
+				if(*s=='\0'){
+					for(j=i+1;j<=argc;j++)
+						argv[j-1]=argv[j];
+					--argc;
+				}
+			}
+			else{
+				if(*s=='\0'){
+					for(j=i+1;j<=argc;j++)
+						argv[j-1]=argv[j];
+					--argc;
+					s=argv[i];
+				}
+				if(argc-i<count){
+					reason=FEWARGS;
+					badflag=c;
+					return -1;
+				}
+				reverse(argv+i, argv+argc);
+				reverse(argv+i, argv+argc-count);
+				reverse(argv+argc-count+1, argv+argc);
+				argc-=count;
+				flag[c]=argv+argc+1;
+				flag[c][0]=s;
+				s="";
+			}
+		}
+	}
+	return argc;
+}
+static void reverse(char **p, char **q)
+{
+	char *t;
+	for(;p<q;p++,--q){ t=*p; *p=*q; *q=t; }
+}
+static int scanflag(int c, char *f)
+{
+	int fc, count;
+	if(0<=c && c<NFLAG) while(*f){
+		if(*f==' '){
+			f++;
+			continue;
+		}
+		fc=*f++;
+		if(*f==':'){
+			f++;
+			if(*f<'0' || '9'<*f){ reason=FLAGSYN; return -1; }
+			count=0;
+			while('0'<=*f && *f<='9') count=count*10+*f++-'0';
+		}
+		else
+			count=0;
+		if(*f=='['){
+			do{
+				f++;
+				if(*f=='\0'){ reason=FLAGSYN; return -1; }
+			}while(*f!=']');
+			f++;
+		}
+		if(c==fc) return count;
+	}
+	reason=BADFLAG;
+	badflag=c;
+	return -1;
+}
+void usage(char *tail)
+{
+	char *s, *t, c;
+	int count, nflag=0;
+	switch(reason){
+	case RESET:
+		errs("Flag -");
+		errc(badflag);
+		errs(": set twice\n");
+		break;
+	case FEWARGS:
+		errs("Flag -");
+		errc(badflag);
+		errs(": too few arguments\n");
+		break;
+	case FLAGSYN:
+		errs("Bad argument to getflags!\n");
+		break;
+	case BADFLAG:
+		errs("Illegal flag -");
+		errc(badflag);
+		errc('\n');
+		break;
+	}
+	errs("Usage: ");
+	errs(cmdname);
+	for(s=flagarg;*s;){
+		c=*s;
+		if(*s++==' ') continue;
+		if(*s==':'){
+			s++;
+			count=0;
+			while('0'<=*s && *s<='9') count=count*10+*s++-'0';
+		}
+		else count=0;
+		if(count==0){
+			if(nflag==0) errs(" [-");
+			nflag++;
+			errc(c);
+		}
+		if(*s=='['){
+			s++;
+			while(*s!=']' && *s!='\0') s++;
+			if(*s==']') s++;
+		}
+	}
+	if(nflag) errs("]");
+	for(s=flagarg;*s;){
+		c=*s;
+		if(*s++==' ') continue;
+		if(*s==':'){
+			s++;
+			count=0;
+			while('0'<=*s && *s<='9') count=count*10+*s++-'0';
+		}
+		else count=0;
+		if(count!=0){
+			errs(" [-");
+			errc(c);
+			if(*s=='['){
+				s++;
+				t=s;
+				while(*s!=']' && *s!='\0') s++;
+				errs(" ");
+				errn(t, s-t);
+				if(*s==']') s++;
+			}
+			else
+				while(count--) errs(" arg");
+			errs("]");
+		}
+		else if(*s=='['){
+			s++;
+			while(*s!=']' && *s!='\0') s++;
+			if(*s==']') s++;
+		}
+	}
+	if(tail){
+		errs(" ");
+		errs(tail);
+	}
+	errs("\n");
+	Exit("bad flags");
+}
+static void errn(char *s, int count)
+{
+	while(count){ errc(*s++); --count; }
+}
+static void errs(char *s)
+{
+	while(*s) errc(*s++);
+}
+#define	NBUF	80
+static char buf[NBUF], *bufp=buf;
+static void errc(int c){
+	*bufp++=c;
+	if(bufp==&buf[NBUF] || c=='\n'){
+		Write(2, buf, bufp-buf);
+		bufp=buf;
+	}
+}
blob - /dev/null
blob + 9afb32ec52557da30e971d70d1bf4c6778f7a449 (mode 644)
--- /dev/null
+++ src/cmd/rc/getflags.h
@@ -0,0 +1,7 @@
+#define	NFLAG	128
+#define	NCMDLINE	512
+extern char **flag[NFLAG];
+extern char cmdline[NCMDLINE+1];
+extern char *cmdname;
+extern char *flagset[];
+int getflags(int, char*[], char*, int);
blob - /dev/null
blob + fcd09c0cbf66652453169c085dd0ff4952d80404 (mode 644)
--- /dev/null
+++ src/cmd/rc/glob.c
@@ -0,0 +1,211 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+char *globname;
+struct word *globv;
+/*
+ * delete all the GLOB marks from s, in place
+ */
+void deglob(char *s)
+{
+	char *t=s;
+	do{
+		if(*t==GLOB) t++;
+		*s++=*t;
+	}while(*t++);
+}
+int globcmp(const void *s, const void *t)
+{
+	return strcmp(*(char**)s, *(char**)t);
+}
+void globsort(word *left, word *right)
+{
+	char **list;
+	word *a;
+	int n=0;
+	for(a=left;a!=right;a=a->next) n++;
+	list=(char **)emalloc(n*sizeof(char *));
+	for(a=left,n=0;a!=right;a=a->next,n++) list[n]=a->word;
+	qsort((char *)list, n, sizeof(char *), globcmp);
+	for(a=left,n=0;a!=right;a=a->next,n++) a->word=list[n];
+	efree((char *)list);
+}
+/*
+ * Push names prefixed by globname and suffixed by a match of p onto the astack.
+ * namep points to the end of the prefix in globname.
+ */
+void globdir(char *p, char *namep)
+{
+	char *t, *newp;
+	int f;
+	/* scan the pattern looking for a component with a metacharacter in it */
+	if(*p=='\0'){
+		globv=newword(globname, globv);
+		return;
+	}
+	t=namep;
+	newp=p;
+	while(*newp){
+		if(*newp==GLOB)
+			break;
+		*t=*newp++;
+		if(*t++=='/'){
+			namep=t;
+			p=newp;
+		}
+	}
+	/* If we ran out of pattern, append the name if accessible */
+	if(*newp=='\0'){
+		*t='\0';
+		if(access(globname, 0)==0)
+			globv=newword(globname, globv);
+		return;
+	}
+	/* read the directory and recur for any entry that matches */
+	*namep='\0';
+	if((f=Opendir(globname[0]?globname:"."))<0) return;
+	while(*newp!='/' && *newp!='\0') newp++;
+	while(Readdir(f, namep)){
+		if(matchfn(namep, p)){
+			for(t=namep;*t;t++);
+			globdir(newp, t);
+		}
+	}
+	Closedir(f);
+}
+/*
+ * Push all file names matched by p on the current thread's stack.
+ * If there are no matches, the list consists of p.
+ */
+void glob(char *p)
+{
+	word *svglobv=globv;
+	int globlen=Globsize(p);
+	if(!globlen){
+		deglob(p);
+		globv=newword(p, globv);
+		return;
+	}
+	globname=emalloc(globlen);
+	globname[0]='\0';
+	globdir(p, globname);
+	efree(globname);
+	if(svglobv==globv){
+		deglob(p);
+		globv=newword(p, globv);
+	}
+	else
+		globsort(globv, svglobv);
+}
+/*
+ * Do p and q point at equal utf codes
+ */
+int equtf(char *p, char *q){
+	if(*p!=*q) return 0;
+	if(twobyte(*p)) return p[1]==q[1];
+	if(threebyte(*p)){
+		if(p[1]!=q[1]) return 0;
+		if(p[1]=='\0') return 1;	/* broken code at end of string! */
+		return p[2]==q[2];
+	}
+	return 1;
+}
+/*
+ * Return a pointer to the next utf code in the string,
+ * not jumping past nuls in broken utf codes!
+ */
+char *nextutf(char *p){
+	if(twobyte(*p)) return p[1]=='\0'?p+1:p+2;
+	if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3;
+	return p+1;
+}
+/*
+ * Convert the utf code at *p to a unicode value
+ */
+int unicode(char *p){
+	int u=*p&0xff;
+	if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f);
+	if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f);
+	return u;
+}
+/*
+ * Does the string s match the pattern p
+ * . and .. are only matched by patterns starting with .
+ * * matches any sequence of characters
+ * ? matches any single character
+ * [...] matches the enclosed list of characters
+ */
+int matchfn(char *s, char *p)
+{
+	if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.')
+		return 0;
+	return match(s, p, '/');
+}
+int match(char *s, char *p, int stop)
+{
+	int compl, hit, lo, hi, t, c;
+	for(;*p!=stop && *p!='\0';s=nextutf(s),p=nextutf(p)){
+		if(*p!=GLOB){
+			if(!equtf(p, s)) return 0;
+		}
+		else switch(*++p){
+		case GLOB:
+			if(*s!=GLOB) return 0;
+			break;
+		case '*':
+			for(;;){
+				if(match(s, nextutf(p), stop)) return 1;
+				if(!*s) break;
+				s=nextutf(s);
+			}
+			return 0;
+		case '?':
+			if(*s=='\0') return 0;
+			break;
+		case '[':
+			if(*s=='\0') return 0;
+			c=unicode(s);
+			p++;
+			compl=*p=='~';
+			if(compl) p++;
+			hit=0;
+			while(*p!=']'){
+				if(*p=='\0') return 0;		/* syntax error */
+				lo=unicode(p);
+				p=nextutf(p);
+				if(*p!='-') hi=lo;
+				else{
+					p++;
+					if(*p=='\0') return 0;	/* syntax error */
+					hi=unicode(p);
+					p=nextutf(p);
+					if(hi<lo){ t=lo; lo=hi; hi=t; }
+				}
+				if(lo<=c && c<=hi) hit=1;
+			}
+			if(compl) hit=!hit;
+			if(!hit) return 0;
+			break;
+		}
+	}
+	return *s=='\0';
+}
+void globlist1(word *gl)
+{
+	if(gl){
+		globlist1(gl->next);
+		glob(gl->word);
+	}
+}
+void globlist(void){
+	word *a;
+	globv=0;
+	globlist1(runq->argv->words);
+	poplist();
+	pushlist();
+	if(globv){
+		for(a=globv;a->next;a=a->next);
+		a->next=runq->argv->words;
+		runq->argv->words=globv;
+	}
+}
blob - /dev/null
blob + e81046d619af2a1a121e2d43c44a659860b46174 (mode 644)
--- /dev/null
+++ src/cmd/rc/havefork.c
@@ -0,0 +1,212 @@
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+int havefork = 1;
+
+void
+Xasync(void)
+{
+	int null = open("/dev/null", 0);
+	int pid;
+	char npid[10];
+	if(null<0){
+		Xerror("Can't open /dev/null\n");
+		return;
+	}
+	switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){
+	case -1:
+		close(null);
+		Xerror("try again");
+		break;
+	case 0:
+		pushredir(ROPEN, null, 0);
+		start(runq->code, runq->pc+1, runq->local);
+		runq->ret = 0;
+		break;
+	default:
+		close(null);
+		runq->pc = runq->code[runq->pc].i;
+		inttoascii(npid, pid);
+		setvar("apid", newword(npid, (word *)0));
+		break;
+	}
+}
+
+void
+Xpipe(void)
+{
+	struct thread *p = runq;
+	int pc = p->pc, forkid;
+	int lfd = p->code[pc++].i;
+	int rfd = p->code[pc++].i;
+	int pfd[2];
+	if(pipe(pfd)<0){
+		Xerror("can't get pipe");
+		return;
+	}
+	switch(forkid = fork()){
+	case -1:
+		Xerror("try again");
+		break;
+	case 0:
+		start(p->code, pc+2, runq->local);
+		runq->ret = 0;
+		close(pfd[PRD]);
+		pushredir(ROPEN, pfd[PWR], lfd);
+		break;
+	default:
+		start(p->code, p->code[pc].i, runq->local);
+		close(pfd[PWR]);
+		pushredir(ROPEN, pfd[PRD], rfd);
+		p->pc = p->code[pc+1].i;
+		p->pid = forkid;
+		break;
+	}
+}
+
+/*
+ * Who should wait for the exit from the fork?
+ */
+void
+Xbackq(void)
+{
+	char wd[8193];
+	int c;
+	char *s, *ewd=&wd[8192], *stop;
+	struct io *f;
+	var *ifs = vlook("ifs");
+	word *v, *nextv;
+	int pfd[2];
+	int pid;
+	stop = ifs->val?ifs->val->word:"";
+	if(pipe(pfd)<0){
+		Xerror("can't make pipe");
+		return;
+	}
+	switch(pid = fork()){
+	case -1:
+		Xerror("try again");
+		close(pfd[PRD]);
+		close(pfd[PWR]);
+		return;
+	case 0:
+		close(pfd[PRD]);
+		start(runq->code, runq->pc+1, runq->local);
+		pushredir(ROPEN, pfd[PWR], 1);
+		return;
+	default:
+		close(pfd[PWR]);
+		f = openfd(pfd[PRD]);
+		s = wd;
+		v = 0;
+		while((c = rchr(f))!=EOF){
+			if(strchr(stop, c) || s==ewd){
+				if(s!=wd){
+					*s='\0';
+					v = newword(wd, v);
+					s = wd;
+				}
+			}
+			else *s++=c;
+		}
+		if(s!=wd){
+			*s='\0';
+			v = newword(wd, v);
+		}
+		closeio(f);
+		Waitfor(pid, 0);
+		/* v points to reversed arglist -- reverse it onto argv */
+		while(v){
+			nextv = v->next;
+			v->next = runq->argv->words;
+			runq->argv->words = v;
+			v = nextv;
+		}
+		runq->pc = runq->code[runq->pc].i;
+		return;
+	}
+}
+
+void
+Xpipefd(void)
+{
+	struct thread *p = runq;
+	int pc = p->pc;
+	char name[40];
+	int pfd[2];
+	int sidefd, mainfd;
+	if(pipe(pfd)<0){
+		Xerror("can't get pipe");
+		return;
+	}
+	if(p->code[pc].i==READ){
+		sidefd = pfd[PWR];
+		mainfd = pfd[PRD];
+	}
+	else{
+		sidefd = pfd[PRD];
+		mainfd = pfd[PWR];
+	}
+	switch(fork()){
+	case -1:
+		Xerror("try again");
+		break;
+	case 0:
+		start(p->code, pc+2, runq->local);
+		close(mainfd);
+		pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0);
+		runq->ret = 0;
+		break;
+	default:
+		close(sidefd);
+		pushredir(ROPEN, mainfd, mainfd);	/* isn't this a noop? */
+		strcpy(name, Fdprefix);
+		inttoascii(name+strlen(name), mainfd);
+		pushword(name);
+		p->pc = p->code[pc+1].i;
+		break;
+	}
+}
+
+void
+Xsubshell(void)
+{
+	int pid;
+	switch(pid = fork()){
+	case -1:
+		Xerror("try again");
+		break;
+	case 0:
+		start(runq->code, runq->pc+1, runq->local);
+		runq->ret = 0;
+		break;
+	default:
+		Waitfor(pid, 1);
+		runq->pc = runq->code[runq->pc].i;
+		break;
+	}
+}
+
+int
+execforkexec(void)
+{
+	int pid;
+	int n;
+	char buf[ERRMAX];
+
+	switch(pid = fork()){
+	case -1:
+		return -1;
+	case 0:
+		pushword("exec");
+		execexec();
+		strcpy(buf, "can't exec: ");
+		n = strlen(buf);
+		errstr(buf+n, ERRMAX-n);
+		Exit(buf);
+	}
+	return pid;
+}
blob - /dev/null
blob + fc8f7608ec32dd2798b99a69bc02b3cecaf626c2 (mode 644)
--- /dev/null
+++ src/cmd/rc/haventfork.c
@@ -0,0 +1,211 @@
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+int havefork = 0;
+
+static char **
+rcargv(char *s)
+{
+	int argc;
+	char **argv;
+	word *p;
+
+	p = vlook("*")->val;
+	argv = malloc((count(p)+6)*sizeof(char*));
+	argc = 0;
+	argv[argc++] = argv0;
+	if(flag['e'])
+		argv[argc++] = "-Se";
+	else
+		argv[argc++] = "-S";
+	argv[argc++] = "-c";
+	argv[argc++] = s;
+	for(p = vlook("*")->val; p; p = p->next)
+		argv[argc++] = p->word;
+	argv[argc] = 0;
+	return argv;
+}
+
+void
+Xasync(void)
+{
+	uint pid;
+	char buf[20], **argv;
+
+	Updenv();
+
+	argv = rcargv(runq->code[runq->pc].s);
+	pid = ForkExecute(argv0, argv, -1, 1, 2);
+	free(argv);
+
+	if(pid == 0) {
+		Xerror("proc failed");
+		return;
+	}
+
+	runq->pc++;
+	sprint(buf, "%d", pid);
+	setvar("apid", newword(buf, (word *)0));
+}
+
+void
+Xbackq(void)
+{
+	char wd[8193], **argv;
+	int c;
+	char *s, *ewd=&wd[8192], *stop;
+	struct io *f;
+	var *ifs = vlook("ifs");
+	word *v, *nextv;
+	int pfd[2];
+	int pid;
+
+	stop = ifs->val?ifs->val->word:"";
+	if(pipe(pfd)<0){
+		Xerror("can't make pipe");
+		return;
+	}
+
+	Updenv();
+
+	argv = rcargv(runq->code[runq->pc].s);
+	pid = ForkExecute(argv0, argv, -1, pfd[1], 2);
+	free(argv);
+
+	close(pfd[1]);
+
+	if(pid == 0) {
+		Xerror("proc failed");
+		close(pfd[0]);
+		return;
+	}
+
+	f = openfd(pfd[0]);
+	s = wd;
+	v = 0;
+	while((c=rchr(f))!=EOF){
+		if(strchr(stop, c) || s==ewd){
+			if(s!=wd){
+				*s='\0';
+				v=newword(wd, v);
+				s=wd;
+			}
+		}
+		else *s++=c;
+	}
+	if(s!=wd){
+		*s='\0';
+		v=newword(wd, v);
+	}
+	closeio(f);
+	Waitfor(pid, 1);
+	/* v points to reversed arglist -- reverse it onto argv */
+	while(v){
+		nextv=v->next;
+		v->next=runq->argv->words;
+		runq->argv->words=v;
+		v=nextv;
+	}
+	runq->pc++;
+}
+
+void
+Xpipe(void)
+{
+	thread *p=runq;
+	int pc=p->pc, pid;
+	int rfd=p->code[pc+1].i;
+	int pfd[2];
+	char **argv;
+
+	if(pipe(pfd)<0){
+		Xerror1("can't get pipe");
+		return;
+	}
+
+	Updenv();
+
+	argv = rcargv(runq->code[pc+2].s);
+	pid = ForkExecute(argv0, argv, 0, pfd[1], 2);
+	free(argv);
+	close(pfd[1]);
+
+	if(pid == 0) {
+		Xerror("proc failed");
+		close(pfd[0]);
+		return;
+	}
+
+	start(p->code, pc+4, runq->local);
+	pushredir(ROPEN, pfd[0], rfd);
+	p->pc=p->code[pc+3].i;
+	p->pid=pid;
+}
+
+void
+Xpipefd(void)
+{
+	Abort();
+}
+
+void
+Xsubshell(void)
+{
+	char **argv;
+	int pid;
+
+	Updenv();
+
+	argv = rcargv(runq->code[runq->pc].s);
+	pid = ForkExecute(argv0, argv, -1, 1, 2);
+	free(argv);
+
+	if(pid < 0) {
+		Xerror("proc failed");
+		return;
+	}
+
+	Waitfor(pid, 1);
+	runq->pc++;
+}
+
+/*
+ *  start a process running the cmd on the stack and return its pid.
+ */
+int
+execforkexec(void)
+{
+	char **argv;
+	char file[1024];
+	int nc;
+	word *path;
+	int pid;
+
+	if(runq->argv->words==0)
+		return -1;
+	argv = mkargv(runq->argv->words);
+
+	for(path = searchpath(runq->argv->words->word);path;path = path->next){
+		nc = strlen(path->word);
+		if(nc<sizeof(file)){
+			strcpy(file, path->word);
+			if(file[0]){
+				strcat(file, "/");
+				nc++;
+			}
+			if(nc+strlen(argv[1])<sizeof(file)){
+				strcat(file, argv[1]);
+				pid = ForkExecute(file, argv+1, mapfd(0), mapfd(1), mapfd(2));
+				if(pid >= 0){
+					free(argv);
+					return pid;
+				}
+			}
+		}
+	}
+	free(argv);
+	return -1;
+}
blob - /dev/null
blob + 2a0fe2f2024d130019981aeeed1a850145ad914a (mode 644)
--- /dev/null
+++ src/cmd/rc/here.c
@@ -0,0 +1,131 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+struct here *here, **ehere;
+int ser=0;
+char tmp[]="/tmp/here0000.0000";
+char hex[]="0123456789abcdef";
+void psubst(io*, char*);
+void pstrs(io*, word*);
+void hexnum(char *p, int n)
+{
+	*p++=hex[(n>>12)&0xF];
+	*p++=hex[(n>>8)&0xF];
+	*p++=hex[(n>>4)&0xF];
+	*p=hex[n&0xF];
+}
+tree *heredoc(tree *tag)
+{
+	struct here *h=new(struct here);
+	if(tag->type!=WORD) yyerror("Bad here tag");
+	h->next=0;
+	if(here)
+		*ehere=h;
+	else
+		here=h;
+	ehere=&h->next;
+	h->tag=tag;
+	hexnum(&tmp[9], getpid());
+	hexnum(&tmp[14], ser++);
+	h->name=strdup(tmp);
+	return token(tmp, WORD);
+}
+/*
+ * bug: lines longer than NLINE get split -- this can cause spurious
+ * missubstitution, or a misrecognized EOF marker.
+ */
+#define	NLINE	4096
+void readhere(void){
+	struct here *h, *nexth;
+	io *f;
+	char *s, *tag;
+	int c, subst;
+	char line[NLINE+1];
+	for(h=here;h;h=nexth){
+		subst=!h->tag->quoted;
+		tag=h->tag->str;
+		c=Creat(h->name);
+		if(c<0) yyerror("can't create here document");
+		f=openfd(c);
+		s=line;
+		pprompt();
+		while((c=rchr(runq->cmdfd))!=EOF){
+			if(c=='\n' || s==&line[NLINE]){
+				*s='\0';
+				if(strcmp(line, tag)==0) break;
+				if(subst) psubst(f, line);
+				else pstr(f, line);
+				s=line;
+				if(c=='\n'){
+					pprompt();
+					pchr(f, c);
+				}
+				else *s++=c;
+			}
+			else *s++=c;
+		}
+		flush(f);
+		closeio(f);
+		cleanhere(h->name);
+		nexth=h->next;
+		efree((char *)h);
+	}
+	here=0;
+	doprompt=1;
+}
+void psubst(io *f, char *s)
+{
+	char *t, *u;
+	int savec, n;
+	word *star;
+	while(*s){
+		if(*s!='$'){
+			if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){
+				pchr(f, *s++);
+				if(*s=='\0') break;
+			}
+			else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){
+				pchr(f, *s++);
+				if(*s=='\0') break;
+				pchr(f, *s++);
+				if(*s=='\0') break;
+			}
+			pchr(f, *s++);
+		}
+		else{
+			t=++s;
+			if(*t=='$') pchr(f, *t++);
+			else{
+				while(*t && idchr(*t)) t++;
+				savec=*t;
+				*t='\0';
+				n=0;
+				for(u=s;*u && '0'<=*u && *u<='9';u++) n=n*10+*u-'0';
+				if(n && *u=='\0'){
+					star=vlook("*")->val;
+					if(star && 1<=n && n<=count(star)){
+						while(--n) star=star->next;
+						pstr(f, star->word);
+					}
+				}
+				else
+					pstrs(f, vlook(s)->val);
+				*t=savec;
+				if(savec=='^') t++;
+			}
+			s=t;
+		}
+	}
+}
+void pstrs(io *f, word *a)
+{
+	if(a){
+		while(a->next && a->next->word){
+			pstr(f, a->word);
+			pchr(f, ' ');
+			a=a->next;
+		}
+		pstr(f, a->word);
+	}
+}
blob - /dev/null
blob + cf143b0fab6be61c6293b6d378df56a958cf50b4 (mode 644)
--- /dev/null
+++ src/cmd/rc/io.c
@@ -0,0 +1,179 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+int pfmtnest=0;
+void pfmt(io *f, char *fmt, ...){
+	va_list ap;
+	char err[ERRMAX];
+	va_start(ap, fmt);
+	pfmtnest++;
+	for(;*fmt;fmt++)
+		if(*fmt!='%') pchr(f, *fmt);
+		else switch(*++fmt){
+		case '\0': va_end(ap); return;
+		case 'c': pchr(f, va_arg(ap, int)); break;
+		case 'd': pdec(f, va_arg(ap, int)); break;
+		case 'o': poct(f, va_arg(ap, unsigned)); break;
+		case 'p': phex(f, (long)va_arg(ap, char *)); break; /*unportable*/
+		case 'Q': pquo(f, va_arg(ap, char *)); break;
+		case 'q': pwrd(f, va_arg(ap, char *)); break;
+		case 'r': errstr(err, sizeof err); pstr(f, err); break;
+		case 's': pstr(f, va_arg(ap, char *)); break;
+		case 't': pcmd(f, va_arg(ap, struct tree *)); break;
+		case 'v': pval(f, va_arg(ap, struct word *)); break;
+		default: pchr(f, *fmt); break;
+		}
+	va_end(ap);
+	if(--pfmtnest==0) flush(f);
+}
+void pchr(io *b, int c)
+{
+	if(b->bufp==b->ebuf) fullbuf(b, c);
+	else *b->bufp++=c;
+}
+int rchr(io *b)
+{
+	if(b->bufp==b->ebuf) return emptybuf(b);
+	return *b->bufp++ & 0xFF;
+}
+
+void pquo(io *f, char *s)
+{
+	pchr(f, '\'');
+	for(;*s;s++)
+		if(*s=='\'') pfmt(f, "''");
+		else pchr(f, *s);
+	pchr(f, '\'');
+}
+void pwrd(io *f, char *s)
+{
+	char *t;
+	for(t=s;*t;t++) if(!wordchr(*t)) break;
+	if(t==s || *t) pquo(f, s);
+	else pstr(f, s);
+}
+void phex(io *f, long p)
+{
+	int n;
+	for(n=28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]);
+}
+void pstr(io *f, char *s)
+{
+	if(s==0) s="(null)";
+	while(*s) pchr(f, *s++);
+}
+void pdec(io *f, long n)
+{
+	if(n<0){
+		n=-n;
+		if(n>=0){
+			pchr(f, '-');
+			pdec(f, n);
+			return;
+		}
+		/* n is two's complement minimum integer */
+		n=1-n;
+		pchr(f, '-');
+		pdec(f, n/10);
+		pchr(f, n%10+'1');
+		return;
+	}
+	if(n>9) pdec(f, n/10);
+	pchr(f, n%10+'0');
+}
+void poct(io *f, ulong n)
+{
+	if(n>7) poct(f, n>>3);
+	pchr(f, (n&7)+'0');
+}
+void pval(io *f, word *a)
+{
+	if(a){
+		while(a->next && a->next->word){
+			pwrd(f, a->word);
+			pchr(f, ' ');
+			a=a->next;
+		}
+		pwrd(f, a->word);
+	}
+}
+int fullbuf(io *f, int c)
+{
+	flush(f);
+	return *f->bufp++=c;
+}
+void flush(io *f)
+{
+	int n;
+	char *s;
+	if(f->strp){
+		n=f->ebuf-f->strp;
+		f->strp=realloc(f->strp, n+101);
+		if(f->strp==0) panic("Can't realloc %d bytes in flush!", n+101);
+		f->bufp=f->strp+n;
+		f->ebuf=f->bufp+100;
+		for(s=f->bufp;s<=f->ebuf;s++) *s='\0';
+	}
+	else{
+		n=f->bufp-f->buf;
+		if(n && Write(f->fd, f->buf, n) < 0){
+			Write(3, "Write error\n", 12);
+			if(ntrap) dotrap();
+		}
+		f->bufp=f->buf;
+		f->ebuf=f->buf+NBUF;
+	}
+}
+io *openfd(int fd){
+	io *f=new(struct io);
+	f->fd=fd;
+	f->bufp=f->ebuf=f->buf;
+	f->strp=0;
+	return f;
+}
+io *openstr(void){
+	io *f=new(struct io);
+	char *s;
+	f->fd=-1;
+	f->bufp=f->strp=emalloc(101);
+	f->ebuf=f->bufp+100;
+	for(s=f->bufp;s<=f->ebuf;s++) *s='\0';
+	return f;
+}
+/*
+ * Open a corebuffer to read.  EOF occurs after reading len
+ * characters from buf.
+ */
+io *opencore(char *s, int len)
+{
+	io *f=new(struct io);
+	char *buf=emalloc(len);
+	f->fd= -1 /*open("/dev/null", 0)*/;
+	f->bufp=f->strp=buf;
+	f->ebuf=buf+len;
+	Memcpy(buf, s, len);
+	return f;
+}
+void rewind(io *io)
+{
+	if(io->fd==-1) io->bufp=io->strp;
+	else{
+		io->bufp=io->ebuf=io->buf;
+		Seek(io->fd, 0L, 0);
+	}
+}
+void closeio(io *io)
+{
+	if(io->fd>=0) close(io->fd);
+	if(io->strp) efree(io->strp);
+	efree((char *)io);
+}
+int emptybuf(io *f)
+{
+	int n;
+	if(f->fd==-1 || (n=Read(f->fd, f->buf, NBUF))<=0) return EOF;
+	f->bufp=f->buf;
+	f->ebuf=f->buf+n;
+	return *f->bufp++&0xff;
+}
blob - /dev/null
blob + 92a439e5d921907f32cb3932b1fe897db08592c6 (mode 644)
--- /dev/null
+++ src/cmd/rc/io.h
@@ -0,0 +1,24 @@
+#define	EOF	(-1)
+#define	NBUF	512
+struct io{
+	int fd;
+	char *bufp, *ebuf, *strp, buf[NBUF];
+};
+io *err;
+io *openfd(int), *openstr(void), *opencore(char *, int);
+int emptybuf(io*);
+void pchr(io*, int);
+int rchr(io*);
+void closeio(io*);
+void flush(io*);
+int fullbuf(io*, int);
+void pdec(io*, long);
+void poct(io*, ulong);
+void phex(io*, long);
+void pquo(io*, char*);
+void pwrd(io*, char*);
+void pstr(io*, char*);
+void pcmd(io*, tree*);
+void pval(io*, word*);
+void pfnc(io*, thread*);
+void pfmt(io*, char*, ...);
blob - /dev/null
blob + dd58151dbc3c0135962891755d13c85c19bf10da (mode 644)
--- /dev/null
+++ src/cmd/rc/lex.c
@@ -0,0 +1,322 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "getflags.h"
+#include "fns.h"
+int getnext(void);
+int wordchr(int c)
+{
+	return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF;
+}
+int idchr(int c)
+{
+	/*
+	 * Formerly:
+	 * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9'
+	 *	|| c=='_' || c=='*';
+	 */
+	return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c);
+}
+int future=EOF;
+int doprompt=1;
+int inquote;
+/*
+ * Look ahead in the input stream
+ */
+int nextc(void){
+	if(future==EOF) future=getnext();
+	return future;
+}
+/*
+ * Consume the lookahead character.
+ */
+int advance(void){
+	int c=nextc();
+	lastc=future;
+	future=EOF;
+	return c;
+}
+/*
+ * read a character from the input stream
+ */	
+int getnext(void){
+	register int c;
+	static int peekc=EOF;
+	if(peekc!=EOF){
+		c=peekc;
+		peekc=EOF;
+		return c;
+	}
+	if(runq->eof) return EOF;
+	if(doprompt) pprompt();
+	c=rchr(runq->cmdfd);
+	if(!inquote && c=='\\'){
+		c=rchr(runq->cmdfd);
+		if(c=='\n'){
+			doprompt=1;
+			c=' ';
+		}
+		else{
+			peekc=c;
+			c='\\';
+		}
+	}
+	doprompt=doprompt || c=='\n' || c==EOF;
+	if(c==EOF) runq->eof++;
+	else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c);
+	return c;
+}
+void pprompt(void){
+	var *prompt;
+	if(runq->iflag){
+		pstr(err, promptstr);
+		flush(err);
+		prompt=vlook("prompt");
+		if(prompt->val && prompt->val->next)
+			promptstr=prompt->val->next->word;
+		else
+			promptstr="\t";
+	}
+	runq->lineno++;
+	doprompt=0;
+}
+void skipwhite(void){
+	int c;
+	for(;;){
+		c=nextc();
+		if(c=='#'){	/* Why did this used to be  if(!inquote && c=='#') ?? */
+			for(;;){
+				c=nextc();
+				if(c=='\n' || c==EOF) break;
+				advance();
+			}
+		}
+		if(c==' ' || c=='\t') advance();
+		else return;
+	}
+}
+void skipnl(void){
+	register int c;
+	for(;;){
+		skipwhite();
+		c=nextc();
+		if(c!='\n') return;
+		advance();
+	}
+}
+int nextis(int c){
+	if(nextc()==c){
+		advance();
+		return 1;
+	}
+	return 0;
+}
+char *addtok(char *p, int val){
+	if(p==0) return 0;
+	if(p==&tok[NTOK]){
+		*p=0;
+		yyerror("token buffer too short");
+		return 0;
+	}
+	*p++=val;
+	return p;
+}
+char *addutf(char *p, int c){
+	p=addtok(p, c);
+	if(twobyte(c))	 /* 2-byte escape */
+		return addtok(p, advance());
+	if(threebyte(c)){	/* 3-byte escape */
+		p=addtok(p, advance());
+		return addtok(p, advance());
+	}
+	return p;
+}
+int lastdol;	/* was the last token read '$' or '$#' or '"'? */
+int lastword;	/* was the last token read a word or compound word terminator? */
+int yylex(void){
+	register int c, d=nextc();
+	register char *w=tok;
+	register struct tree *t;
+	yylval.tree=0;
+	/*
+	 * Embarassing sneakiness:  if the last token read was a quoted or unquoted
+	 * WORD then we alter the meaning of what follows.  If the next character
+	 * is `(', we return SUB (a subscript paren) and consume the `('.  Otherwise,
+	 * if the next character is the first character of a simple or compound word,
+	 * we insert a `^' before it.
+	 */
+	if(lastword){
+		lastword=0;
+		if(d=='('){
+			advance();
+			strcpy(tok, "( [SUB]");
+			return SUB;
+		}
+		if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
+			strcpy(tok, "^");
+			return '^';
+		}
+	}
+	inquote=0;
+	skipwhite();
+	switch(c=advance()){
+	case EOF:
+		lastdol=0;
+		strcpy(tok, "EOF");
+		return EOF;
+	case '$':
+		lastdol=1;
+		if(nextis('#')){
+			strcpy(tok, "$#");
+			return COUNT;
+		}
+		if(nextis('"')){
+			strcpy(tok, "$\"");
+			return '"';
+		}
+		strcpy(tok, "$");
+		return '$';
+	case '&':
+		lastdol=0;
+		if(nextis('&')){
+			skipnl();
+			strcpy(tok, "&&");
+			return ANDAND;
+		}
+		strcpy(tok, "&");
+		return '&';
+	case '|':
+		lastdol=0;
+		if(nextis(c)){
+			skipnl();
+			strcpy(tok, "||");
+			return OROR;
+		}
+	case '<':
+	case '>':
+		lastdol=0;
+		/*
+		 * funny redirection tokens:
+		 *	redir:	arrow | arrow '[' fd ']'
+		 *	arrow:	'<' | '<<' | '>' | '>>' | '|'
+		 *	fd:	digit | digit '=' | digit '=' digit
+		 *	digit:	'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
+		 * some possibilities are nonsensical and get a message.
+		 */
+		*w++=c;
+		t=newtree();
+		switch(c){
+		case '|':
+			t->type=PIPE;
+			t->fd0=1;
+			t->fd1=0;
+			break;
+		case '>':
+			t->type=REDIR;
+			if(nextis(c)){
+				t->rtype=APPEND;
+				*w++=c;
+			}
+			else t->rtype=WRITE;
+			t->fd0=1;
+			break;
+		case '<':
+			t->type=REDIR;
+			if(nextis(c)){
+				t->rtype=HERE;
+				*w++=c;
+			}
+			else t->rtype=READ;
+			t->fd0=0;
+			break;
+		}
+		if(nextis('[')){
+			*w++='[';
+			c=advance();
+			*w++=c;
+			if(c<'0' || '9'<c){
+			RedirErr:
+				*w=0;
+				yyerror(t->type==PIPE?"pipe syntax"
+						:"redirection syntax");
+				return EOF;
+			}
+			t->fd0=0;
+			do{
+				t->fd0=t->fd0*10+c-'0';
+				*w++=c;
+				c=advance();
+			}while('0'<=c && c<='9');
+			if(c=='='){
+				*w++='=';
+				if(t->type==REDIR) t->type=DUP;
+				c=advance();
+				if('0'<=c && c<='9'){
+					t->rtype=DUPFD;
+					t->fd1=t->fd0;
+					t->fd0=0;
+					do{
+						t->fd0=t->fd0*10+c-'0';
+						*w++=c;
+						c=advance();
+					}while('0'<=c && c<='9');
+				}
+				else{
+					if(t->type==PIPE) goto RedirErr;
+					t->rtype=CLOSE;
+				}
+			}
+			if(c!=']'
+			|| t->type==DUP && (t->rtype==HERE || t->rtype==APPEND))
+				goto RedirErr;
+			*w++=']';
+		}
+		*w='\0';
+		yylval.tree=t;
+		if(t->type==PIPE) skipnl();
+		return t->type;
+	case '\'':
+		lastdol=0;
+		lastword=1;
+		inquote=1;
+		for(;;){
+			c=advance();
+			if(c==EOF) break;
+			if(c=='\''){
+				if(nextc()!='\'')
+					break;
+				advance();
+			}
+			w=addutf(w, c);
+		}
+		if(w!=0) *w='\0';
+		t=token(tok, WORD);
+		t->quoted=1;
+		yylval.tree=t;
+		return t->type;
+	}
+	if(!wordchr(c)){
+		lastdol=0;
+		tok[0]=c;
+		tok[1]='\0';
+		return c;
+	}
+	for(;;){
+		/* next line should have (char)c==GLOB, but ken's compiler is broken */
+		if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB)
+			w=addtok(w, GLOB);
+		w=addutf(w, c);
+		c=nextc();
+		if(lastdol?!idchr(c):!wordchr(c)) break;
+		advance();
+	}
+
+	lastword=1;
+	lastdol=0;
+	if(w!=0) *w='\0';
+	t=klook(tok);
+	if(t->type!=WORD) lastword=0;
+	t->quoted=0;
+	yylval.tree=t;
+	return t->type;
+}
blob - /dev/null
blob + cc476973c176d5a27b53c7099c62c46f2404f43d (mode 644)
--- /dev/null
+++ src/cmd/rc/mkfile
@@ -0,0 +1,39 @@
+PLAN9=../../..
+<$PLAN9/src/mkhdr
+YACC=yacc -d
+
+TARG=rc
+
+OFILES=\
+	code.$O\
+	exec.$O\
+	getflags.$O\
+	glob.$O\
+	here.$O\
+	io.$O\
+	lex.$O\
+	pcmd.$O\
+	pfnc.$O\
+	simple.$O\
+	subr.$O\
+	trap.$O\
+	tree.$O\
+	var.$O\
+	y.tab.$O\
+	plan9ish.$O\
+
+HFILES=\
+	rc.h\
+	x.tab.h\
+	io.h\
+	exec.h\
+	fns.h\
+
+YFILES=syn.y
+
+LDFLAGS=$LDFLAGS -l9 -lfmt -lutf
+
+<$PLAN9/src/mkone
+
+x.tab.h: y.tab.h
+	cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
blob - /dev/null
blob + 0f84298ae3784d45484c059bd41f4244b00c881b (mode 644)
--- /dev/null
+++ src/cmd/rc/pcmd.c
@@ -0,0 +1,108 @@
+#include "rc.h"
+#include "io.h"
+#include "fns.h"
+char nl='\n';		/* change to semicolon for bourne-proofing */
+#define	c0	t->child[0]
+#define	c1	t->child[1]
+#define	c2	t->child[2]
+void pdeglob(io *f, char *s)
+{
+	while(*s){
+		if(*s==GLOB) s++;
+		pchr(f, *s++);
+	}
+}
+void pcmd(io *f, tree *t)
+{
+	if(t==0) return;
+	switch(t->type){
+	default:	pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); break;
+	case '$':	pfmt(f, "$%t", c0); break;
+	case '"':	pfmt(f, "$\"%t", c0); break;
+	case '&':	pfmt(f, "%t&", c0); break;
+	case '^':	pfmt(f, "%t^%t", c0, c1); break;
+	case '`':	pfmt(f, "`%t", c0); break;
+	case ANDAND:	pfmt(f, "%t && %t", c0, c1); break;
+	case BANG:	pfmt(f, "! %t", c0); break;
+	case BRACE:	pfmt(f, "{%t}", c0); break;
+	case COUNT:	pfmt(f, "$#%t", c0); break;
+	case FN:	pfmt(f, "fn %t %t", c0, c1); break;
+	case IF:	pfmt(f, "if%t%t", c0, c1); break;
+	case NOT:	pfmt(f, "if not %t", c0); break;
+	case OROR:	pfmt(f, "%t || %t", c0, c1); break;
+	case PCMD:
+	case PAREN:	pfmt(f, "(%t)", c0); break;
+	case SUB:	pfmt(f, "$%t(%t)", c0, c1); break;
+	case SIMPLE:	pfmt(f, "%t", c0); break;
+	case SUBSHELL:	pfmt(f, "@ %t", c0); break;
+	case SWITCH:	pfmt(f, "switch %t %t", c0, c1); break;
+	case TWIDDLE:	pfmt(f, "~ %t %t", c0, c1); break;
+	case WHILE:	pfmt(f, "while %t%t", c0, c1); break;
+	case ARGLIST:
+		if(c0==0)
+			pfmt(f, "%t", c1);
+		else if(c1==0)
+			pfmt(f, "%t", c0);
+		else
+			pfmt(f, "%t %t", c0, c1);
+		break;
+	case ';':
+		if(c0){
+			if(c1) pfmt(f, "%t%c%t", c0, nl, c1);
+			else pfmt(f, "%t", c0);
+		}
+		else pfmt(f, "%t", c1);
+		break;
+	case WORDS:
+		if(c0) pfmt(f, "%t ", c0);
+		pfmt(f, "%t", c1);
+		break;
+	case FOR:
+		pfmt(f, "for(%t", c0);
+		if(c1) pfmt(f, " in %t", c1);
+		pfmt(f, ")%t", c2);
+		break;
+	case WORD:
+		if(t->quoted) pfmt(f, "%Q", t->str);
+		else pdeglob(f, t->str);
+		break;
+	case DUP:
+		if(t->rtype==DUPFD)
+			pfmt(f, ">[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */
+		else
+			pfmt(f, ">[%d=]", t->fd0);
+		pfmt(f, "%t", c1);
+		break;
+	case PIPEFD:
+	case REDIR:
+		switch(t->rtype){
+		case HERE:
+			pchr(f, '<');
+		case READ:
+			pchr(f, '<');
+			if(t->fd0!=0) pfmt(f, "[%d]", t->fd0);
+			break;
+		case APPEND:
+			pchr(f, '>');
+		case WRITE:
+			pchr(f, '>');
+			if(t->fd0!=1) pfmt(f, "[%d]", t->fd0);
+			break;
+		}
+		pfmt(f, "%t", c0);
+		if(c1) pfmt(f, " %t", c1);
+		break;
+	case '=':
+		pfmt(f, "%t=%t", c0, c1);
+		if(c2) pfmt(f, " %t", c2);
+		break;
+	case PIPE:
+		pfmt(f, "%t|", c0);
+		if(t->fd1==0){
+			if(t->fd0!=1) pfmt(f, "[%d]", t->fd0);
+		}
+		else pfmt(f, "[%d=%d]", t->fd0, t->fd1);
+		pfmt(f, "%t", c1);
+		break;
+	}
+}
blob - /dev/null
blob + a4606e7c5830f6a36dbb5a05d808c8946db83bf2 (mode 644)
--- /dev/null
+++ src/cmd/rc/pfnc.c
@@ -0,0 +1,67 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+struct{
+	void (*f)(void);
+	char *name;
+}fname[]={
+	Xappend, "Xappend",
+	Xasync, "Xasync",
+	Xbang, "Xbang",
+	Xclose, "Xclose",
+	Xdup, "Xdup",
+	Xeflag, "Xeflag",
+	Xexit, "Xexit",
+	Xfalse, "Xfalse",
+	Xifnot, "Xifnot",
+	Xjump, "Xjump",
+	Xmark, "Xmark",
+	Xpopm, "Xpopm",
+	Xread, "Xread",
+	Xreturn, "Xreturn",
+	Xtrue, "Xtrue",
+	Xif, "Xif",
+	Xwastrue, "Xwastrue",
+	Xword, "Xword",
+	Xwrite, "Xwrite",
+	Xmatch, "Xmatch",
+	Xcase, "Xcase",
+	Xconc, "Xconc",
+	Xassign, "Xassign",
+	Xdol, "Xdol",
+	Xcount, "Xcount",
+	Xlocal, "Xlocal",
+	Xunlocal, "Xunlocal",
+	Xfn, "Xfn",
+	Xdelfn, "Xdelfn",
+	Xpipe, "Xpipe",
+	Xpipewait, "Xpipewait",
+	Xrdcmds, "Xrdcmds",
+	(void (*)(void))Xerror, "Xerror",
+	Xbackq, "Xbackq",
+	Xpipefd, "Xpipefd",
+	Xsubshell, "Xsubshell",
+	Xdelhere, "Xdelhere",
+	Xfor, "Xfor",
+	Xglob, "Xglob",
+	Xrdfn, "Xrdfn",
+	Xsimple, "Xsimple",
+	Xrdfn, "Xrdfn",
+	Xqdol, "Xqdol",
+0};
+void pfnc(io *fd, thread *t)
+{
+	int i;
+	void (*fn)(void)=t->code[t->pc].f;
+	list *a;
+	pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc);
+	for(i=0;fname[i].f;i++) if(fname[i].f==fn){
+		pstr(fd, fname[i].name);
+		break;
+	}
+	if(!fname[i].f) pfmt(fd, "%p", fn);
+	for(a=t->argv;a;a=a->next) pfmt(fd, " (%v)", a->words);
+	pchr(fd, '\n');
+	flush(fd);
+}
blob - /dev/null
blob + 0c3076ec4fe810c9f236842fc5c7569bc47b1c14 (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + c81b6280deb406f009469d8a787282dc2a578827 (mode 644)
--- /dev/null
+++ src/cmd/rc/rc.h
@@ -0,0 +1,136 @@
+/*
+ * Plan9 is defined for plan 9
+ * V9 is defined for 9th edition
+ * Sun is defined for sun-os
+ * Please don't litter the code with ifdefs.  The three below (and one in
+ * getflags) should be enough.
+ */
+#define	Plan9
+#ifdef	Plan9
+#include <u.h>
+#include <libc.h>
+#define	NSIG	32
+#define	SIGINT	2
+#define	SIGQUIT	3
+#endif
+#ifdef	V9
+#include <signal.h>
+#include <libc.h>
+#endif
+#ifdef Sun
+#include <signal.h>
+#endif
+#define	YYMAXDEPTH	500
+#ifndef PAREN
+#ifndef YYMAJOR
+#include "x.tab.h"
+#endif
+#endif
+typedef struct tree tree;
+typedef struct word word;
+typedef struct io io;
+typedef union code code;
+typedef struct var var;
+typedef struct list list;
+typedef struct redir redir;
+typedef struct thread thread;
+typedef struct builtin builtin;
+
+struct tree{
+	int type;
+	int rtype, fd0, fd1;		/* details of REDIR PIPE DUP tokens */
+	char *str;
+	int quoted;
+	int iskw;
+	tree *child[3];
+	tree *next;
+};
+tree *newtree(void);
+tree *token(char*, int), *klook(char*), *tree1(int, tree*);
+tree *tree2(int, tree*, tree*), *tree3(int, tree*, tree*, tree*);
+tree *mung1(tree*, tree*), *mung2(tree*, tree*, tree*);
+tree *mung3(tree*, tree*, tree*, tree*), *epimung(tree*, tree*);
+tree *simplemung(tree*), *heredoc(tree*);
+void freetree(tree*);
+tree *cmdtree;
+/*
+ * The first word of any code vector is a reference count.
+ * Always create a new reference to a code vector by calling codecopy(.).
+ * Always call codefree(.) when deleting a reference.
+ */
+union code{
+	void (*f)(void);
+	int i;
+	char *s;
+};
+char *promptstr;
+int doprompt;
+#define	NTOK	8192
+char tok[NTOK];
+#define	APPEND	1
+#define	WRITE	2
+#define	READ	3
+#define	HERE	4
+#define	DUPFD	5
+#define	CLOSE	6
+struct var{
+	char *name;		/* ascii name */
+	word *val;	/* value */
+	int changed;
+	code *fn;		/* pointer to function's code vector */
+	int fnchanged;
+	int pc;			/* pc of start of function */
+	var *next;	/* next on hash or local list */
+};
+var *vlook(char*), *gvlook(char*), *newvar(char*, var*);
+#define	NVAR	521
+var *gvar[NVAR];				/* hash for globals */
+#define	new(type)	((type *)emalloc(sizeof(type)))
+char *emalloc(long);
+void *Malloc(ulong);
+void efree(char*);
+#define	NOFILE	128		/* should come from <param.h> */
+struct here{
+	tree *tag;
+	char *name;
+	struct here *next;
+};
+int mypid;
+/*
+ * Glob character escape in strings:
+ *	In a string, GLOB must be followed by *?[ or GLOB.
+ *	GLOB* matches any string
+ *	GLOB? matches any single character
+ *	GLOB[...] matches anything in the brackets
+ *	GLOBGLOB matches GLOB
+ */
+#define	GLOB	((char)0x01)
+/*
+ * onebyte(c), twobyte(c), threebyte(c)
+ * Is c the first character of a one- two- or three-byte utf sequence?
+ */
+#define	onebyte(c)	((c&0x80)==0x00)
+#define	twobyte(c)	((c&0xe0)==0xc0)
+#define	threebyte(c)	((c&0xf0)==0xe0)
+char **argp;
+char **args;
+int nerror;		/* number of errors encountered during compilation */
+int doprompt;		/* is it time for a prompt? */
+/*
+ * Which fds are the reading/writing end of a pipe?
+ * Unfortunately, this can vary from system to system.
+ * 9th edition Unix doesn't care, the following defines
+ * work on plan 9.
+ */
+#define	PRD	0
+#define	PWR	1
+extern char *Rcmain(), Fdprefix[];
+#define	register
+/*
+ * How many dot commands have we executed?
+ * Used to ensure that -v flag doesn't print rcmain.
+ */
+int ndot;
+char *getstatus(void);
+int lastc;
+int lastword;
blob - /dev/null
blob + c85c909836a91a1ef4f810f3c73ed2cb41a98384 (mode 644)
--- /dev/null
+++ src/cmd/rc/simple.c
@@ -0,0 +1,443 @@
+/*
+ * Maybe `simple' is a misnomer.
+ */
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+/*
+ * Search through the following code to see if we're just going to exit.
+ */
+int
+exitnext(void){
+	union code *c=&runq->code[runq->pc];
+	while(c->f==Xpopredir) c++;
+	return c->f==Xexit;
+}
+void Xsimple(void){
+	word *a;
+	thread *p=runq;
+	var *v;
+	struct builtin *bp;
+	int pid, n;
+	char buf[ERRMAX];
+	globlist();
+	a=runq->argv->words;
+	if(a==0){
+		Xerror1("empty argument list");
+		return;
+	}
+	if(flag['x'])
+		pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */
+	v=gvlook(a->word);
+	if(v->fn)
+		execfunc(v);
+	else{
+		if(strcmp(a->word, "builtin")==0){
+			if(count(a)==1){
+				pfmt(err, "builtin: empty argument list\n");
+				setstatus("empty arg list");
+				poplist();
+				return;
+			}
+			a=a->next;
+			popword();
+		}
+		for(bp=Builtin;bp->name;bp++)
+			if(strcmp(a->word, bp->name)==0){
+				(*bp->fnc)();
+				return;
+			}
+		if(exitnext()){
+			/* fork and wait is redundant */
+			pushword("exec");
+			execexec();
+			Xexit();
+		}
+		else{
+			flush(err);
+			Updenv();	/* necessary so changes don't go out again */
+			switch(pid=fork()){
+			case -1:
+				Xerror("try again");
+				return;
+			case 0:
+				pushword("exec");
+				execexec();
+				strcpy(buf, "can't exec: ");
+				n = strlen(buf);
+				errstr(buf+n, ERRMAX-n);
+				Exit(buf);
+			default:
+				poplist();
+				/* interrupts don't get us out */
+				while(Waitfor(pid, 1) < 0)
+					;
+			}
+		}
+	}
+}
+struct word nullpath={ "", 0};
+void doredir(redir *rp)
+{
+	if(rp){
+		doredir(rp->next);
+		switch(rp->type){
+		case ROPEN:
+			if(rp->from!=rp->to){
+				Dup(rp->from, rp->to);
+				close(rp->from);
+			}
+			break;
+		case RDUP: Dup(rp->from, rp->to); break;
+		case RCLOSE: close(rp->from); break;
+		}
+	}
+}
+word *searchpath(char *w){
+	word *path;
+	if(strncmp(w, "/", 1)==0
+	|| strncmp(w, "#", 1)==0
+	|| strncmp(w, "./", 2)==0
+	|| strncmp(w, "../", 3)==0
+	|| (path=vlook("path")->val)==0)
+		path=&nullpath;
+	return path;
+}
+void execexec(void){
+	popword();	/* "exec" */
+	if(runq->argv->words==0){
+		Xerror1("empty argument list");
+		return;
+	}
+	doredir(runq->redir);
+	Execute(runq->argv->words, searchpath(runq->argv->words->word));
+	poplist();
+}
+void execfunc(var *func)
+{
+	word *starval;
+	popword();
+	starval=runq->argv->words;
+	runq->argv->words=0;
+	poplist();
+	start(func->fn, func->pc, (struct var *)0);
+	runq->local=newvar(strdup("*"), runq->local);
+	runq->local->val=starval;
+	runq->local->changed=1;
+}
+int dochdir(char *word){
+	/* report to /dev/wdir if it exists and we're interactive */
+	static int wdirfd = -2;
+	if(chdir(word)<0) return -1;
+	if(flag['i']!=0){
+		if(wdirfd==-2)	/* try only once */
+			wdirfd = open("/dev/wdir", OWRITE|OCEXEC);
+		if(wdirfd>=0)
+			write(wdirfd, word, strlen(word));
+	}
+	return 1;
+}
+void execcd(void){
+	word *a=runq->argv->words;
+	word *cdpath;
+	char dir[512];
+	setstatus("can't cd");
+	cdpath=vlook("cdpath")->val;
+	switch(count(a)){
+	default:
+		pfmt(err, "Usage: cd [directory]\n");
+		break;
+	case 2:
+		if(a->next->word[0]=='/' || cdpath==0) cdpath=&nullpath;
+		for(;cdpath;cdpath=cdpath->next){
+			strcpy(dir, cdpath->word);
+			if(dir[0]) strcat(dir, "/");
+			strcat(dir, a->next->word);
+			if(dochdir(dir)>=0){
+				if(strlen(cdpath->word)
+				&& strcmp(cdpath->word, ".")!=0)
+					pfmt(err, "%s\n", dir);
+				setstatus("");
+				break;
+			}
+		}
+		if(cdpath==0) pfmt(err, "Can't cd %s: %r\n", a->next->word);
+		break;
+	case 1:
+		a=vlook("home")->val;
+		if(count(a)>=1){
+			if(dochdir(a->word)>=0)
+				setstatus("");
+			else
+				pfmt(err, "Can't cd %s: %r\n", a->word);
+		}
+		else
+			pfmt(err, "Can't cd -- $home empty\n");
+		break;
+	}
+	poplist();
+}
+void execexit(void){
+	switch(count(runq->argv->words)){
+	default: pfmt(err, "Usage: exit [status]\nExiting anyway\n");
+	case 2: setstatus(runq->argv->words->next->word);
+	case 1:	Xexit();
+	}
+}
+void execshift(void){
+	int n;
+	word *a;
+	var *star;
+	switch(count(runq->argv->words)){
+	default:
+		pfmt(err, "Usage: shift [n]\n");
+		setstatus("shift usage");
+		poplist();
+		return;
+	case 2: n=atoi(runq->argv->words->next->word); break;
+	case 1: n=1; break;
+	}
+	star=vlook("*");
+	for(;n && star->val;--n){
+		a=star->val->next;
+		efree(star->val->word);
+		efree((char *)star->val);
+		star->val=a;
+		star->changed=1;
+	}
+	setstatus("");
+	poplist();
+}
+int octal(char *s)
+{
+	int n=0;
+	while(*s==' ' || *s=='\t' || *s=='\n') s++;
+	while('0'<=*s && *s<='7') n=n*8+*s++-'0';
+	return n;
+}
+int mapfd(int fd)
+{
+	redir *rp;
+	for(rp=runq->redir;rp;rp=rp->next){
+		switch(rp->type){
+		case RCLOSE:
+			if(rp->from==fd) fd=-1;
+			break;
+		case RDUP:
+		case ROPEN:
+			if(rp->to==fd) fd=rp->from;
+			break;
+		}
+	}
+	return fd;
+}
+union code rdcmds[4];
+void execcmds(io *f)
+{
+	static int first=1;
+	if(first){
+		rdcmds[0].i=1;
+		rdcmds[1].f=Xrdcmds;
+		rdcmds[2].f=Xreturn;
+		first=0;
+	}
+	start(rdcmds, 1, runq->local);
+	runq->cmdfd=f;
+	runq->iflast=0;
+}
+void execeval(void){
+	char *cmdline, *s, *t;
+	int len=0;
+	word *ap;
+	if(count(runq->argv->words)<=1){
+		Xerror1("Usage: eval cmd ...");
+		return;
+	}
+	eflagok=1;
+	for(ap=runq->argv->words->next;ap;ap=ap->next)
+		len+=1+strlen(ap->word);
+	cmdline=emalloc(len);
+	s=cmdline;
+	for(ap=runq->argv->words->next;ap;ap=ap->next){
+		for(t=ap->word;*t;) *s++=*t++;
+		*s++=' ';
+	}
+	s[-1]='\n';
+	poplist();
+	execcmds(opencore(cmdline, len));
+	efree(cmdline);
+}
+union code dotcmds[14];
+void execdot(void){
+	int iflag=0;
+	int fd;
+	list *av;
+	thread *p=runq;
+	char *zero;
+	static int first=1;
+	char file[512];
+	word *path;
+	if(first){
+		dotcmds[0].i=1;
+		dotcmds[1].f=Xmark;
+		dotcmds[2].f=Xword;
+		dotcmds[3].s="0";
+		dotcmds[4].f=Xlocal;
+		dotcmds[5].f=Xmark;
+		dotcmds[6].f=Xword;
+		dotcmds[7].s="*";
+		dotcmds[8].f=Xlocal;
+		dotcmds[9].f=Xrdcmds;
+		dotcmds[10].f=Xunlocal;
+		dotcmds[11].f=Xunlocal;
+		dotcmds[12].f=Xreturn;
+		first=0;
+	}
+	else
+		eflagok=1;
+	popword();
+	if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){
+		iflag=1;
+		popword();
+	}
+	/* get input file */
+	if(p->argv->words==0){
+		Xerror1("Usage: . [-i] file [arg ...]");
+		return;
+	}
+	zero=strdup(p->argv->words->word);
+	popword();
+	fd=-1;
+	for(path=searchpath(zero);path;path=path->next){
+		strcpy(file, path->word);
+		if(file[0]) strcat(file, "/");
+		strcat(file, zero);
+		if((fd=open(file, 0))>=0) break;
+		if(strcmp(file, "/dev/stdin")==0){	/* for sun & ucb */
+			fd=Dup1(0);
+			if(fd>=0) break;
+		}
+	}
+	if(fd<0){
+		pfmt(err, "%s: ", zero);
+		setstatus("can't open");
+		Xerror(".: can't open");
+		return;
+	}
+	/* set up for a new command loop */
+	start(dotcmds, 1, (struct var *)0);
+	pushredir(RCLOSE, fd, 0);
+	runq->cmdfile=zero;
+	runq->cmdfd=openfd(fd);
+	runq->iflag=iflag;
+	runq->iflast=0;
+	/* push $* value */
+	pushlist();
+	runq->argv->words=p->argv->words;
+	/* free caller's copy of $* */
+	av=p->argv;
+	p->argv=av->next;
+	efree((char *)av);
+	/* push $0 value */
+	pushlist();
+	pushword(zero);
+	ndot++;
+}
+void execflag(void){
+	char *letter, *val;
+	switch(count(runq->argv->words)){
+	case 2:
+		setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");
+		break;
+	case 3:
+		letter=runq->argv->words->next->word;
+		val=runq->argv->words->next->next->word;
+		if(strlen(letter)==1){
+			if(strcmp(val, "+")==0){
+				flag[(uchar)letter[0]]=flagset;
+				break;
+			}
+			if(strcmp(val, "-")==0){
+				flag[(uchar)letter[0]]=0;
+				break;
+			}
+		}
+	default:
+		Xerror1("Usage: flag [letter] [+-]");
+		return;
+	}
+	poplist();
+}
+void execwhatis(void){	/* mildly wrong -- should fork before writing */
+	word *a, *b, *path;
+	var *v;
+	struct builtin *bp;
+	char file[512];
+	struct io out[1];
+	int found, sep;
+	a=runq->argv->words->next;
+	if(a==0){
+		Xerror1("Usage: whatis name ...");
+		return;
+	}
+	setstatus("");
+	out->fd=mapfd(1);
+	out->bufp=out->buf;
+	out->ebuf=&out->buf[NBUF];
+	out->strp=0;
+	for(;a;a=a->next){
+		v=vlook(a->word);
+		if(v->val){
+			pfmt(out, "%s=", a->word);
+			if(v->val->next==0)
+				pfmt(out, "%q\n", v->val->word);
+			else{
+				sep='(';
+				for(b=v->val;b && b->word;b=b->next){
+					pfmt(out, "%c%q", sep, b->word);
+					sep=' ';
+				}
+				pfmt(out, ")\n");
+			}
+			found=1;
+		}
+		else
+			found=0;
+		v=gvlook(a->word);
+		if(v->fn) pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
+		else{
+			for(bp=Builtin;bp->name;bp++)
+				if(strcmp(a->word, bp->name)==0){
+					pfmt(out, "builtin %s\n", a->word);
+					break;
+				}
+			if(!bp->name){
+				for(path=searchpath(a->word);path;path=path->next){
+					strcpy(file, path->word);
+					if(file[0]) strcat(file, "/");
+					strcat(file, a->word);
+					if(Executable(file)){
+						pfmt(out, "%s\n", file);
+						break;
+					}
+				}
+				if(!path && !found){
+					pfmt(err, "%s: not found\n", a->word);
+					setstatus("not found");
+				}
+			}
+		}
+	}
+	poplist();
+	flush(err);
+}
+void execwait(void){
+	switch(count(runq->argv->words)){
+	default: Xerror1("Usage: wait [pid]"); return;
+	case 2: Waitfor(atoi(runq->argv->words->next->word), 0); break;
+	case 1: Waitfor(-1, 0); break;
+	}
+	poplist();
+}
blob - /dev/null
blob + 2b242242ba8825baeb43825df56372ef3e4abdad (mode 644)
--- /dev/null
+++ src/cmd/rc/subr.c
@@ -0,0 +1,59 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+char *emalloc(long n){
+	char *p=(char *)Malloc(n);
+	if(p==0) panic("Can't malloc %d bytes", n);
+/*	if(err){ pfmt(err, "malloc %d->%p\n", n, p); flush(err); } *//**/
+	return p;
+}
+void efree(char *p)
+{
+/*	pfmt(err, "free %p\n", p); flush(err); *//**/
+	if(p) free(p);
+	else pfmt(err, "free 0\n");
+}
+extern int lastword, lastdol;
+void yyerror(char *m)
+{
+	pfmt(err, "rc: ");
+	if(runq->cmdfile && !runq->iflag)
+		pfmt(err, "%s:%d: ", runq->cmdfile, runq->lineno);
+	else if(runq->cmdfile)
+		pfmt(err, "%s: ", runq->cmdfile);
+	else if(!runq->iflag)
+		pfmt(err, "line %d: ", runq->lineno);
+	if(tok[0] && tok[0]!='\n') pfmt(err, "token %q: ", tok);
+	pfmt(err, "%s\n", m);
+	flush(err);
+	lastword=0;
+	lastdol=0;
+	while(lastc!='\n' && lastc!=EOF) advance();
+	nerror++;
+	setvar("status", newword(m, (word *)0));
+}
+char *bp;
+void iacvt(int n){
+	if(n<0){
+		*bp++='-';
+		n=-n;	/* doesn't work for n==-inf */
+	}
+	if(n/10)
+		iacvt(n/10);
+	*bp++=n%10+'0';
+}
+void itoa(char *s, long n)
+{
+	bp=s;
+	iacvt(n);
+	*bp='\0';
+}
+void panic(char *s, int n)
+{
+	pfmt(err, "rc: ");
+	pfmt(err, s, n);
+	pchr(err, '\n');
+	flush(err);
+	Abort();
+}
blob - /dev/null
blob + 96ef364a6785c4ac125aa36de4f184f72af6e366 (mode 644)
--- /dev/null
+++ src/cmd/rc/trap.c
@@ -0,0 +1,34 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+#include "io.h"
+extern char *Signame[];
+void dotrap(void){
+	register int i;
+	register struct var *trapreq;
+	register struct word *starval;
+	starval=vlook("*")->val;
+	while(ntrap) for(i=0;i!=NSIG;i++) while(trap[i]){
+		--trap[i];
+		--ntrap;
+		if(getpid()!=mypid) Exit(getstatus());
+		trapreq=vlook(Signame[i]);
+		if(trapreq->fn){
+			start(trapreq->fn, trapreq->pc, (struct var *)0);
+			runq->local=newvar(strdup("*"), runq->local);
+			runq->local->val=copywords(starval, (struct word *)0);
+			runq->local->changed=1;
+			runq->redir=runq->startredir=0;
+		}
+		else if(i==SIGINT || i==SIGQUIT){
+			/*
+			 * run the stack down until we uncover the
+			 * command reading loop.  Xreturn will exit
+			 * if there is none (i.e. if this is not
+			 * an interactive rc.)
+			 */
+			while(!runq->iflag) Xreturn();
+		}
+		else Exit(getstatus());
+	}
+}
blob - /dev/null
blob + 9bf76d8efb1b7956e88b9c9d9219aaf55ebaeaf4 (mode 644)
--- /dev/null
+++ src/cmd/rc/tree.c
@@ -0,0 +1,114 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+tree *treenodes;
+/*
+ * create and clear a new tree node, and add it
+ * to the node list.
+ */
+tree *newtree(void){
+	tree *t=new(tree);
+	t->iskw=0;
+	t->str=0;
+	t->child[0]=t->child[1]=t->child[2]=0;
+	t->next=treenodes;
+	treenodes=t;
+	return t;
+}
+void freenodes(void){
+	tree *t, *u;
+	for(t=treenodes;t;t=u){
+		u=t->next;
+		if(t->str) efree(t->str);
+		efree((char *)t);
+	}
+	treenodes=0;
+}
+tree *tree1(int type, tree *c0)
+{
+	return tree3(type, c0, (tree *)0, (tree *)0);
+}
+tree *tree2(int type, tree *c0, tree *c1)
+{
+	return tree3(type, c0, c1, (tree *)0);
+}
+tree *tree3(int type, tree *c0, tree *c1, tree *c2)
+{
+	tree *t;
+	if(type==';'){
+		if(c0==0) return c1;
+		if(c1==0) return c0;
+	}
+	t=newtree();
+	t->type=type;
+	t->child[0]=c0;
+	t->child[1]=c1;
+	t->child[2]=c2;
+	return t;
+}
+tree *mung1(tree *t, tree *c0)
+{
+	t->child[0]=c0;
+	return t;
+}
+tree *mung2(tree *t, tree *c0, tree *c1)
+{
+	t->child[0]=c0;
+	t->child[1]=c1;
+	return t;
+}
+tree *mung3(tree *t, tree *c0, tree *c1, tree *c2)
+{
+	t->child[0]=c0;
+	t->child[1]=c1;
+	t->child[2]=c2;
+	return t;
+}
+tree *epimung(tree *comp, tree *epi)
+{
+	tree *p;
+	if(epi==0) return comp;
+	for(p=epi;p->child[1];p=p->child[1]);
+	p->child[1]=comp;
+	return epi;
+}
+/*
+ * Add a SIMPLE node at the root of t and percolate all the redirections
+ * up to the root.
+ */
+tree *simplemung(tree *t)
+{
+	tree *u;
+	struct io *s;
+	t=tree1(SIMPLE, t);
+	s=openstr();
+	pfmt(s, "%t", t);
+	t->str=strdup(s->strp);
+	closeio(s);
+	for(u=t->child[0];u->type==ARGLIST;u=u->child[0]){
+		if(u->child[1]->type==DUP
+		|| u->child[1]->type==REDIR){
+			u->child[1]->child[1]=t;
+			t=u->child[1];
+			u->child[1]=0;
+		}
+	}
+	return t;
+}
+tree *token(char *str, int type)
+{
+	tree *t=newtree();
+	t->type=type;
+	t->str=strdup(str);
+	return t;
+}
+void freetree(tree *p)
+{
+	if(p==0) return;	
+	freetree(p->child[0]);
+	freetree(p->child[1]);
+	freetree(p->child[2]);
+	if(p->str) efree(p->str);
+	efree((char *)p);
+}
blob - /dev/null
blob + 73e4e9aec52cf7161e0412cd85c17e46165a0f39 (mode 644)
--- /dev/null
+++ src/cmd/rc/var.c
@@ -0,0 +1,71 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+int hash(char *s, int n)
+{
+	register int h=0, i=1;
+	while(*s) h+=*s++*i++;
+	h%=n;
+	return h<0?h+n:h;
+}
+#define	NKW	30
+struct kw{
+	char *name;
+	int type;
+	struct kw *next;
+}*kw[NKW];
+void kenter(int type, char *name)
+{
+	register int h=hash(name, NKW);
+	register struct kw *p=new(struct kw);
+	p->type=type;
+	p->name=name;
+	p->next=kw[h];
+	kw[h]=p;
+}
+void kinit(void){
+	kenter(FOR, "for");
+	kenter(IN, "in");
+	kenter(WHILE, "while");
+	kenter(IF, "if");
+	kenter(NOT, "not");
+	kenter(TWIDDLE, "~");
+	kenter(BANG, "!");
+	kenter(SUBSHELL, "@");
+	kenter(SWITCH, "switch");
+	kenter(FN, "fn");
+}
+tree *klook(char *name)
+{
+	struct kw *p;
+	tree *t=token(name, WORD);
+	for(p=kw[hash(name, NKW)];p;p=p->next)
+		if(strcmp(p->name, name)==0){
+			t->type=p->type;
+			t->iskw=1;
+			break;
+		}
+	return t;
+}
+var *gvlook(char *name)
+{
+	int h=hash(name, NVAR);
+	var *v;
+	for(v=gvar[h];v;v=v->next) if(strcmp(v->name, name)==0) return v;
+	return gvar[h]=newvar(strdup(name), gvar[h]);
+}
+var *vlook(char *name)
+{
+	var *v;
+	if(runq)
+		for(v=runq->local;v;v=v->next)
+			if(strcmp(v->name, name)==0) return v;
+	return gvlook(name);
+}
+void setvar(char *name, word *val)
+{
+	register struct var *v=vlook(name);
+	freewords(v->val);
+	v->val=val;
+	v->changed=1;
+}