hlfw.ca

drawcpu

Download patch

ref: 271a7db1cd9afb4cf841101188199c3a0d0f4c62
parent: 1e72ca9a502a78a97523c2b79aa558e16ed4d8bd
author: halfwit <michaelmisch1985@gmail.com>
date: Sat Feb 24 15:07:23 PST 2024

Start trying to integrate with rc

--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,6 @@
 
 OFILES=\
 	main.$O\
-	session.$O\
 	aan.$O\
 	secstore.$O\
 	latin1.$O\
@@ -17,7 +16,7 @@
 	libauth/libauth.a\
 	libauthsrv/libauthsrv.a\
 	libsec/libsec.a\
-	librc/librc.a\
+	rc/librc.a\
 	libmp/libmp.a\
 	libmemdraw/libmemdraw.a\
 	libmemlayer/libmemlayer.a\
@@ -54,9 +53,6 @@
 libmp/libmp.a:
 	(cd libmp; $(MAKE))
 
-librc/librc.a:
-	(cd librc; $(MAKE))
-
 libsec/libsec.a:
 	(cd libsec; $(MAKE))
 
@@ -74,6 +70,9 @@
 
 libip/libip.a:
 	(cd libip; $(MAKE))
+
+rc/librc.a:
+	(cd rc; $(MAKE))
 
 gui-$(GUI)/libgui.a:
 	(cd gui-$(GUI); $(MAKE))
--- a/drawcpu.h
+++ b/drawcpu.h
@@ -3,8 +3,9 @@
 extern char *secstorefetch(char *addr, char *owner, char *passwd);
 extern char *authserver;
 extern int exportfs(int, int);
+extern void runcommand(int, char**);
 extern int dialfactotum(void);
 extern char *getuser(void);
-extern int session(int);
+extern int session(void);
 extern int aanclient(char*, int);
 extern int dbg;
\ No newline at end of file
--- a/include/rc.h
+++ /dev/null
@@ -1,159 +1,0 @@
-/*
- * Plan9 is defined for plan 9
- * otherwise its UNIX.
- * Please don't litter the code with ifdefs.  The three below (and one in
- * getflags) should be enough.
- */
-#ifdef Plan9
-#include <u.h>
-#include <libc.h>
-#define NSIG	32
-#define	SIGINT	2
-#define	SIGQUIT	3
-#else
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
-#include <signal.h>
-#ifndef NSIG
-#define NSIG 32
-#endif
-#endif
-
-#define	YYMAXDEPTH	500
-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 lexer lexer;
-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 */
-	int	line;
-	char	glob;			/* 0=string, 1=glob, 2=pattern see globprop() and noglobs() */
-	char	quoted;
-	char	iskw;
-	char	*str;
-	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*);
-tree *globprop(tree*);
-char *fnstr(tree*);
-void runscript(int, int, char**);
-/*
- * The first word of any code vector is a reference count
- * and the second word is a string for srcfile().
- * Code starts at pc 2. The last code word must be a zero
- * terminator for codefree().
- * 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;
-};
-
-#define	NTOK	8192
-
-struct lexer{
-	io	*input;
-	char	*file;
-	int	line;
-
-	char	*prolog;
-	char	*epilog;
-
-	int	peekc;
-	int	future;
-	int	lastc;
-
-	char	eof;
-	char	inquote;
-	char	incomm;
-	char	lastword;	/* was the last token read a word or compound word terminator? */
-	char	lastdol;	/* was the last token read '$' or '$#' or '"'? */
-	char	iflast;		/* static `if not' checking */
-
-	char	qflag;
-
-	char	tok[NTOK];
-};
-extern lexer *lex;		/* current lexer */
-lexer *newlexer(io*, char*);
-void freelexer(lexer*);
-
-#define	APPEND	1
-#define	WRITE	2
-#define	READ	3
-#define	HERE	4
-#define	DUPFD	5
-#define	CLOSE	6
-#define RDWR	7
-
-struct var{
-	var	*next;		/* next on hash or local list */
-	word	*val;		/* value */
-	code	*fn;		/* pointer to function's code vector */
-	int	pc;		/* pc of start of function */
-	char	fnchanged;
-	char	changed;
-	char	name[];
-};
-var *vlook(char*), *gvlook(char*), *newvar(char*, var*);
-void setvar(char*, word*), freevar(var*);
-
-#define	NVAR	521
-extern var *gvar[NVAR];		/* hash for globals */
-
-#define	new(type)	((type *)emalloc(sizeof(type)))
-
-void *emalloc(long);
-void *erealloc(void *, long);
-
-/*
- * 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)
-/*
- * Is c the first character of a utf sequence?
- */
-#define	onebyte(c)	(((c)&0x80)==0x00)
-#define twobyte(c)	(((c)&0xe0)==0xc0)
-#define threebyte(c)	(((c)&0xf0)==0xe0)
-#define fourbyte(c)	(((c)&0xf8)==0xf0)
-#define xbyte(c)	(((c)&0xc0)==0x80)
-
-extern char *argv0;
-extern int nerror;		/* number of errors encountered during compilation */
-extern int doprompt;		/* is it time for a prompt? */
-extern io *err;
-
-/*
- * 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[];
-extern char *Signame[];
--- a/librc/Makefile
+++ /dev/null
@@ -1,31 +1,0 @@
-ROOT=..
-include ../Make.config
-LIB=librc.a
-
-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\
-	havefork.$O\
-	drawcpu.$O\
-	y.tab.$O\
-
-default: $(LIB)
-$(LIB): $(OFILES)
-	$(AR) r $(LIB) $(OFILES)
-	$(RANLIB) $(LIB)
-
-%.$O: %.c
-	$(CC) $(CFLAGS) $*.c
-	
\ No newline at end of file
--- a/librc/code.c
+++ /dev/null
@@ -1,550 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-#include "io.h"
-#include "exec.h"
-#include "fns.h"
-#include "getflags.h"
-#include "y.tab.h"
-#define	c0	t->child[0]
-#define	c1	t->child[1]
-#define	c2	t->child[2]
-code *codebuf;
-static int codep, ncode, codeline;
-#define	emitf(x) ((codep!=ncode || morecode()), codebuf[codep].f = (x), codep++)
-#define	emiti(x) ((codep!=ncode || morecode()), codebuf[codep].i = (x), codep++)
-#define	emits(x) ((codep!=ncode || morecode()), codebuf[codep].s = (x), codep++)
-
-void stuffdot(int);
-void outcode(tree*, int);
-void codeswitch(tree*, int);
-int iscase(tree*);
-code *codecopy(code*);
-void codefree(code*);
-
-int
-morecode(void)
-{
-	ncode+=ncode;
-	codebuf = (code *)erealloc((char *)codebuf, 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 = emalloc(ncode*sizeof codebuf[0]);
-	codep = 0;
-	codeline = 0;			/* force source */
-	emiti(0);			/* reference count */
-	emits(estrdup(lex->file));	/* source file name */
-	outcode(t, !lex->qflag && flag['e']!=0);
-	if(nerror){
-		free(codebuf);
-		return 0;
-	}
-	emitf(Xreturn);
-	emitf(0);
-	return 1;
-}
-
-/*
- * called on a tree where we expect eigther
- * a pattern or a string instead of a glob to
- * remove the GLOB chars from the strings
- * or set glob to 2 for pattern so Xglob
- * is not inserted when compiling the tree.
- */
-void
-noglobs(tree *t, int pattern)
-{
-Again:
-	if(t==0)
-		return;
-	if(t->type==WORD && t->glob){
-		if(pattern)
-			t->glob=2;
-		else{
-			deglob(t->str);
-			t->glob=0;
-		}
-	}
-	if(t->type==PAREN || t->type==WORDS || t->type=='^'){
-		t->glob=0;
-		noglobs(c1, pattern);
-		t = c0;
-		goto Again;
-	}
-}
-
-void
-outcode(tree *t, int eflag)
-{
-	void (*f)(void);
-	int p, q;
-	tree *tt;
-	if(t==0)
-		return;
-	if(t->type!=NOT && t->type!=';')
-		lex->iflast = 0;
-	if(t->line != codeline){
-		codeline = t->line;
-		if(codebuf && codep >= 2 && codebuf[codep-2].f == Xsrcline)
-			codebuf[codep-1].i = codeline;
-		else {
-			emitf(Xsrcline);
-			emiti(codeline);
-		}
-	}
-	switch(t->type){
-	default:
-		pfmt(err, "bad type %d in outcode\n", t->type);
-		break;
-	case '$':
-		emitf(Xmark);
-		noglobs(c0, 0);
-		outcode(c0, eflag);
-		emitf(Xdol);
-		break;
-	case '"':
-		emitf(Xmark);
-		emitf(Xmark);
-		noglobs(c0, 0);
-		outcode(c0, eflag);
-		emitf(Xdol);
-		emitf(Xqw);
-		emitf(Xpush);
-		break;
-	case SUB:
-		emitf(Xmark);
-		noglobs(c0, 0);
-		outcode(c0, eflag);
-		emitf(Xmark);
-		noglobs(c1, 0);
-		outcode(c1, eflag);
-		emitf(Xsub);
-		break;
-	case '&':
-		emitf(Xasync);
-		p = emiti(0);
-
-		/* undocumented? */
-		emitf(Xmark);
-		emitf(Xword);
-		emits(estrdup("/dev/null"));
-		emitf(Xread);
-		emiti(0);
-
-		/* insert rfork s for plan9 */
-		f = builtinfunc("rfork");
-		if(f){
-			emitf(Xmark);
-			emitf(Xword);
-			emits(estrdup("s"));
-			emitf(Xword);
-			emits(estrdup("rfork"));
-			emitf(f);
-		}
-
-		codeline = 0;	/* force source */
-		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(Xmark);
-		if(c0){
-			noglobs(c0, 0);
-			outcode(c0, 0);
-		} else {
-			emitf(Xmark);
-			emitf(Xword);
-			emits(estrdup("ifs"));
-			emitf(Xdol);
-		}
-		emitf(Xqw);
-		emitf(Xbackq);
-		p = emiti(0);
-		codeline = 0;	/* force source */
-		outcode(c1, 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);
-		noglobs(c0, 0);
-		outcode(c0, eflag);
-		emitf(Xcount);
-		break;
-	case FN:
-		emitf(Xmark);
-		noglobs(c0, 0);
-		outcode(c0, eflag);
-		if(c1){
-			emitf(Xfn);
-			p = emiti(0);
-			emits(fnstr(c1));
-			codeline = 0;	/* force source */
-			outcode(c1, eflag);
-			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(!lex->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);
-		codeline = 0;	/* force source */
-		outcode(c0, eflag);
-		emitf(Xexit);
-		stuffdot(p);
-		if(eflag)
-			emitf(Xeflag);
-		break;
-	case SWITCH:
-		codeswitch(t, eflag);
-		break;
-	case TWIDDLE:
-		emitf(Xmark);
-		noglobs(c1, 1);
-		outcode(c1, eflag);
-		emitf(Xmark);
-		outcode(c0, eflag);
-		emitf(Xqw);
-		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);
-		}
-		else{
-			emitf(Xmark);
-			emitf(Xword);
-			emits(estrdup("*"));
-			emitf(Xdol);
-		}
-		emitf(Xmark);		/* dummy value for Xlocal */
-		emitf(Xmark);
-		noglobs(c0, 0);
-		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(t->str);
-		t->str=0;	/* passed ownership */
-		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);
-		codeline = 0;	/* force source */
-		outcode(c0, eflag);
-		emitf(Xexit);
-		stuffdot(p);
-		break;
-	case REDIR:
-		if(t->rtype!=HERE){
-			emitf(Xmark);
-			outcode(c0, eflag);
-		}
-		switch(t->rtype){
-		case APPEND:
-			emitf(Xappend);
-			break;
-		case WRITE:
-			emitf(Xwrite);
-			break;
-		case READ:
-			emitf(Xread);
-			break;
-		case RDWR:
-			emitf(Xrdwr);
-			break;
-		case HERE:
-			emitf(c0->quoted?Xhereq:Xhere);
-			emits(t->str);
-			t->str=0;	/* passed ownership */
-			break;
-		}
-		emiti(t->fd0);
-		outcode(c1, eflag);
-		emitf(Xpopredir);
-		break;
-	case '=':
-		tt = t;
-		for(;t && t->type=='=';t = c2);
-		if(t){					/* var=value cmd */
-			for(t = tt;t->type=='=';t = c2){
-				emitf(Xmark);
-				outcode(c1, eflag);
-				emitf(Xmark);
-				noglobs(c0, 0);
-				outcode(c0, eflag);
-				emitf(Xlocal);		/* push var for cmd */
-			}
-			outcode(t, eflag);		/* gen. code for cmd */
-			for(t = tt; t->type == '='; t = c2)
-				emitf(Xunlocal);	/* pop var */
-		}
-		else{					/* var=value */
-			for(t = tt;t;t = c2){
-				emitf(Xmark);
-				outcode(c1, eflag);
-				emitf(Xmark);
-				noglobs(c0, 0);
-				outcode(c0, eflag);
-				emitf(Xassign);	/* set var permanently */
-			}
-		}
-		t = tt;	/* so tests below will work */
-		break;
-	case PIPE:
-		emitf(Xpipe);
-		emiti(t->fd0);
-		emiti(t->fd1);
-		p = emiti(0);
-		q = emiti(0);
-		codeline = 0;	/* force source */
-		outcode(c0, eflag);
-		emitf(Xexit);
-		stuffdot(p);
-		codeline = 0;	/* force source */
-		outcode(c1, eflag);
-		emitf(Xreturn);
-		stuffdot(q);
-		emitf(Xpipewait);
-		break;
-	}
-	if(t->glob==1)
-		emitf(Xglob);
-	if(t->type!=NOT && t->type!=';')
-		lex->iflast = t->type==IF;
-	else if(c0)
-		lex->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]==0
-	|| c1->child[0]->type!=';'
-	|| !iscase(c1->child[0]->child[0])){
-		yyerror("case missing in switch");
-		return;
-	}
-	emitf(Xmark);
-	outcode(c0, eflag);
-	emitf(Xqw);
-	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) {
-			noglobs(c1, 1);
-			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+2;p->f;p++){
-		if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite
-		|| p->f==Xrdwr
-		|| p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse
-		|| p->f==Xfor || p->f==Xjump
-		|| p->f==Xsrcline
-		|| 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==Xhere || p->f==Xhereq) free(p[1].s), p+=2;
-		else if(p->f==Xword) free((++p)->s);
-		else if(p->f==Xfn){
-			free(p[2].s);
-			p+=2;
-		}
-	}
-	free(cp[1].s);
-	free(cp);
-}
--- a/librc/drawcpu.c
+++ /dev/null
@@ -1,275 +1,0 @@
-/*
- * Unix versions of system-specific functions
- *	By convention, exported routines herein have names beginning with an
- *	upper case letter.
- */
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-#include <errno.h>
-#include <string.h>
-#include "exec.h"
-#include "io.h"
-#include "fns.h"
-#include "getflags.h"
-
-static void execfinit(void);
-
-// TODO: add bind, mount, etc
-builtin Builtin[] = {
-	"cd",		execcd,
-	"whatis",	execwhatis,
-	"eval",		execeval,
-	"exec",		execexec,	/* but with popword first */
-	"exit",		execexit,
-	"shift",	execshift,
-	"wait",		execwait,
-	".",		execdot,
-	"flag",		execflag,
-	"finit",	execfinit,
-	0
-};
-
-char Rcmain[] = "./librc/rcmain.drawcpu";
-char Fdprefix[] = "/dev/fd/";
-char *Signame[] = {
-	"sigexit",	"sighup",	"sigint",	"sigquit",
-	"sigalrm",	"sigkill",	"sigfpe",	"sigterm",
-	0
-};
-
-/*
- * finit could be removed but is kept for
- * backwards compatibility, see: rcmain.plan9
- */
-static void
-execfinit(void)
-{
-	char *cmds = estrdup("for(i in '/env/fn#'*){. -bq $i}\n");
-	int line = runq->line;
-	poplist();
-	execcmds(openiocore(cmds, strlen(cmds)), estrdup(srcfile(runq)), runq->local, runq->redir);
-	runq->lex->line = line;
-	runq->lex->qflag = 1;
-}
-
-char*
-Env(char *name, int fn)
-{
-	static char buf[128];
-
-	strcpy(buf, "/env/");
-	if(fn) strcat(buf, "fn#");
-	return strncat(buf, name, sizeof(buf)-1);
-}
-/* TODO: Proper env read
-void
-Vinit(void)
-{
-	int dir, fd, i, n;
-	Dir *ent;
-
-	dir = open(Env("", 0), 0);
-	if(dir<0){
-		pfmt(err, "%s: can't open: %s\n", argv0, strerror(errno));
-		return;
-	}
-	for(;;){
-		ent = 0;
-		n = devdirread(dir, ent, sizeof ent);
-		if(n <= 0)
-			break;
-		for(i = 0; i<n; i++){
-			if(ent[i].length<=0 || strncmp(ent[i].name, "fn#", 3)==0)
-				continue;
-			if((fd = open(Env(ent[i].name, 0), 0))>=0){
-				io *f = openiofd(fd);
-				word *w = 0, **wp = &w;
-				char *s;
-				while((s = rstr(f, "")) != 0){
-					*wp = Newword(s, (word*)0);
-					wp = &(*wp)->next;
-				}
-				closeio(f);
-				setvar(ent[i].name, w);
-				vlook(ent[i].name)->changed = 0;
-			}
-		}
-		free(ent);
-	}
-	close(dir);
-}
-*/
-
-int
-Waitfor(int pid)
-{
-	thread *p;
-	Waitmsg *w;
-
-	if(pid >= 0 && !havewaitpid(pid))
-		return 0;
-
-	while((w = wait(pid)) != nil){
-		delwaitpid(w->pid);
-		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;
-				p->status = estrdup(w->msg);
-				break;
-			}
-		free(w);
-	}
-
-	if(strcmp(strerror(errno), "interrupted")==0) return -1;
-	return 0;
-}
-
-// TODO: /dev/env integration
-static void
-addenv(var *v)
-{
-	word *w;
-	int fd;
-	io *f;
-
-	if(v->changed){
-		v->changed = 0;
-		if((fd = create(Env(v->name, 0), ORDWR, 0644))<0)
-			pfmt(err, "%s: can't open: %s\n", argv0, strerror(errno));
-		else{
-			f = openiofd(fd);
-			for(w = v->val;w;w = w->next){
-				pstr(f, w->word);
-				pchr(f, '\0');
-			}
-			flushio(f);
-			closeio(f);
-		}
-	}
-	if(v->fnchanged){
-		v->fnchanged = 0;
-		if((fd = create(Env(v->name, 1), ORDWR, 0644))<0)
-			pfmt(err, "%s: can't open: %s\n", argv0, strerror(errno));
-		else{
-			f = openiofd(fd);
-			if(v->fn)
-				pfmt(f, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
-			flushio(f);
-			closeio(f);
-		}
-	}
-}
-
-static 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);
-	if(err)
-		flushio(err);
-}
-
-typedef struct rdir rdir;
-struct rdir {
-	Dir	*dbuf;
-	int	i, n;
-	int	fd;
-};
-
-static int
-trimdirs(Dir *d, int nd)
-{
-	int r, w;
-
-	for(r=w=0; r<nd; r++)
-		if(d[r].mode&DMDIR)
-			d[w++] = d[r];
-	return w;
-}
-
-static int interrupted = 0;
-static char *syssigname[] = {
-	"exit",		/* can't happen */
-	"hangup",
-	"interrupt",
-	"quit",		/* can't happen */
-	"alarm",
-	"kill",
-	"sys: fp: ",
-	"term",
-	0
-};
-
-void
-notifyf(void* q, char *s)
-{
-	int i;
-	USED(q);
-	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;
-	}
-	// TODO: Handle notes
-	//noted(NDFLT);
-	return;
-Out:
-	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
-		trap[i]++;
-		ntrap++;
-	}
-	// TODO: Handle notes
-	//noted(NCONT);
-}
-
-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;
-}
-
-void
-Noerror(void)
-{
-	interrupted = 0;
-}
-
-int
-Isatty(int fd)
-{
-	isatty(fd);
-}
-
-void
-Prompt(char *s)
-{
-	pstr(err, s);
-	flushio(err);
-}
-	
\ No newline at end of file
--- a/librc/exec.c
+++ /dev/null
@@ -1,1169 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-#include <errno.h>
-#include "getflags.h"
-#include "exec.h"
-#include "io.h"
-#include "fns.h"
-
-char *argv0="rc";
-io *err;
-int mypid;
-thread *runq;
-
-/*
- * Start executing the given code at the given pc with the given redirection
- */
-void
-start(code *c, int pc, var *local, redir *redir)
-{
-	thread *p = new(thread);
-	p->code = codecopy(c);
-	p->line = 0;
-	p->pc = pc;
-	p->argv = 0;
-	p->redir = p->startredir = redir;
-	p->lex = 0;
-	p->local = local;
-	p->iflag = 0;
-	p->pid = 0;
-	p->status = 0;
-	p->ret = runq;
-	runq = p;
-}
-
-void
-startfunc(var *func, word *starval, var *local, redir *redir)
-{
-	start(func->fn, func->pc, local, redir);
-	runq->local = newvar("*", runq->local);
-	runq->local->val = starval;
-	runq->local->changed = 1;
-}
-
-static void
-popthread(void)
-{
-	thread *p = runq;
-	while(p->argv) poplist();
-	while(p->local && (p->ret==0 || p->local!=p->ret->local))
-		Xunlocal();
-	runq = p->ret;
-	if(p->lex) freelexer(p->lex);
-	codefree(p->code);
-	free(p->status);
-	free(p);
-}
-
-word*
-Newword(char *s, word *next)
-{
-	word *p=new(word);
-	p->word = s;
-	p->next = next;
-	return p;
-}
-word*
-newword(char *s, word *next)
-{
-	return Newword(estrdup(s), next);
-}
-word*
-Pushword(char *s)
-{
-	word *p;
-	if(s==0)
-		panic("null pushword", 0);
-	if(runq->argv==0)
-		panic("pushword but no argv!", 0);
-	p = Newword(s, runq->argv->words);
-	runq->argv->words = p;
-	return p;
-}
-word*
-pushword(char *s)
-{
-	return Pushword(estrdup(s));
-}
-char*
-Freeword(word *p)
-{
-	char *s = p->word;
-	free(p);
-	return s;
-}
-void
-freewords(word *w)
-{
-	word *p;
-	while((p = w)!=0){
-		w = w->next;
-		free(Freeword(p));
-	}
-}
-char*
-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;
-	return Freeword(p);
-}
-void
-popword(void)
-{
-	free(Popword());
-}
-
-void
-pushlist(void)
-{
-	list *p = new(list);
-	p->words = 0;
-	p->next = runq->argv;
-	runq->argv = p;
-}
-word*
-Poplist(void)
-{
-	word *w;
-	list *p = runq->argv;
-	if(p==0)
-		panic("poplist but no argv", 0);
-	w = p->words;
-	runq->argv = p->next;
-	free(p);
-	return w;
-}
-void
-poplist(void)
-{
-	freewords(Poplist());
-}
-
-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;
-}
-
-static void
-dontclose(int fd)
-{
-	redir *rp;
-
-	if(fd<0)
-		return;
-	for(rp = runq->redir; rp != runq->startredir; rp = rp->next){
-		if(rp->type == RCLOSE && rp->from == fd){
-			rp->type = 0;
-			break;
-		}
-	}
-}
-
-/*
- * we are about to start a new thread that should exit on
- * return, so the current stack is not needed anymore.
- * free all the threads and lexers, but preserve the
- * redirections and anything referenced by local.
- */
-void
-turfstack(var *local)
-{
-	while(local){
-		thread *p;
-
-		for(p = runq; p && p->local == local; p = p->ret)
-			p->local = local->next;
-		local = local->next;
-	}
-	while(runq) {
-		if(runq->lex) dontclose(runq->lex->input->fd);
-		popthread();
-	}
-}
-
-void
-shuffleredir(void)
-{
-	redir **rr, *rp;
-
-	rp = runq->redir;
-	if(rp==0)
-		return;
-	runq->redir = rp->next;
-	rp->next = runq->startredir;
-	for(rr = &runq->redir; *rr != rp->next; rr = &((*rr)->next))
-		;
-	*rr = rp;
-}
-
-/*
- * get command line flags, initialize keywords & traps.
- * get values from environment.
- * set $pid, $cflag, $*
- * fabricate bootstrap code and start it (*=(argv);. -bq /usr/lib/rcmain $*)
- * start interpreting code
- */
-void
-runscript(int fd, int argc, char **argv)
-{
-	code bootstrap[20];
-	char num[12];
-	char *rcmain=Rcmain;
-	int i;
-	argv0 = argv[0];
-	argc = getflags(argc, argv, "srdiIlxebpvVc: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;
-	if(flag['m']) rcmain = flag['m'][0];
-	err = openiofd(2);
-	kinit();
-	notify(notifyf);
-	// Vinit();
-	inttoascii(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));
-	bootstrap[0].i = 1;
-	bootstrap[1].s="*bootstrap*";
-	bootstrap[2].f = Xmark;
-	bootstrap[3].f = Xword;
-	bootstrap[4].s="*";
-	bootstrap[5].f = Xassign;
-	bootstrap[6].f = Xmark;
-	bootstrap[7].f = Xmark;
-	bootstrap[8].f = Xword;
-	bootstrap[9].s="*";
-	bootstrap[10].f = Xdol;
-	bootstrap[11].f = Xword;
-	bootstrap[12].s = rcmain;
-	bootstrap[13].f = Xword;
-	bootstrap[14].s="-bq";
-	bootstrap[15].f = Xword;
-	bootstrap[16].s=".";
-	bootstrap[17].f = Xsimple;
-	bootstrap[18].f = Xexit;
-	bootstrap[19].f = 0;
-	start(bootstrap, 2, (var*)0, (redir*)0);
-
-	/* prime bootstrap argv */
-	pushlist();
-	for(i = argc-1;i!=0;--i) pushword(argv[i]);
-
-	for(;;){
-		if(flag['r'])
-			pfnc(err, runq);
-		(*runq->code[runq->pc++].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(split){... 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
- * Xdol(name)				get variable value
- * 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
- * Xglob(list)				glob a list of words inplace
- * 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
- * Xpush(words)				push words down a list
- * Xqw(words)				quote words inplace
- * Xrdwr(file)[fd]			open file for reading and writing
- * Xread(file)[fd]			open file to read
- * Xreturn				kill thread
- * Xsimple(args)			run command and wait
- * Xsrcline[line]			set current source line number
- * 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 fd;
-
-	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((fd = open(file, 1))<0 && (fd = create(file, ORDWR, 0644))<0){
-		Xerror3(">> can't open", file, strerror(errno));
-		return;
-	}
-	seek(fd, 0L, 2);
-	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
-	poplist();
-}
-
-void
-Xsettrue(void)
-{
-	setstatus("");
-}
-
-void
-Xbang(void)
-{
-	setstatus(truestatus()?"false":"");
-}
-
-void
-Xclose(void)
-{
-	pushredir(RCLOSE, runq->code[runq->pc++].i, 0);
-}
-
-void
-Xdup(void)
-{
-	pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
-	runq->pc+=2;
-}
-
-void
-Xeflag(void)
-{
-	if(!truestatus()) Xexit();
-}
-
-void
-Xexit(void)
-{
-	static int beenhere = 0;
-
-	if(getpid()==mypid && !beenhere){
-		var *trapreq = vlook("sigexit");
-		word *starval = vlook("*")->val;
-		if(trapreq->fn){
-			beenhere = 1;
-			--runq->pc;
-			startfunc(trapreq, copywords(starval, (word*)0), (var*)0, (redir*)0);
-			return;
-		}
-	}
-	_exit(0);
-}
-
-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
-Xpush(void)
-{
-	word *t, *h = Poplist();
-	for(t = h; t->next; t = t->next)
-		;
-	t->next = runq->argv->words;
-	runq->argv->words = h;
-}
-
-static int
-herefile(char *tmp)
-{
-	char *s = tmp+strlen(tmp)-1;
-	static int ser;
-	int fd, i;
-
-	i = ser++;
-	while(*s == 'Y'){
-		*s-- = (i%26) + 'A';
-		i = i/26;
-	}
-	i = getpid();
-	while(*s == 'X'){
-		*s-- = (i%10) + '0';
-		i = i/10;
-	}
-	s++;
-	for(i='a'; i<'z'; i++){
-		if(access(tmp, 0)!=0 && (fd = create(tmp, ORDWR, 0644))>=0)
-			return fd;
-		*s = i;
-	}
-	return -1;
-}
-
-void
-Xhere(void)
-{
-	char file[]="/tmp/hereXXXXXXXXXXYY";
-	int fd;
-	io *io;
-
-	if((fd = herefile(file))<0){
-		Xerror3("<< can't get temp file", file, strerror(errno));
-		return;
-	}
-	io = openiofd(fd);
-	psubst(io, (unsigned char*)runq->code[runq->pc++].s);
-	flushio(io);
-	closeio(io);
-
-	/* open for reading and unlink */
-	if((fd = open(file, 3))<0){
-		Xerror3("<< can't open", file, strerror(errno));
-		return;
-	}
-	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
-}
-
-void
-Xhereq(void)
-{
-	char file[]="/tmp/hereXXXXXXXXXXYY", *body;
-	int fd;
-
-	if((fd = herefile(file))<0){
-		Xerror3("<< can't get temp file", file, strerror(errno));
-		return;
-	}
-	body = runq->code[runq->pc++].s;
-	write(fd, body, strlen(body));
-	close(fd);
-
-	/* open for reading and unlink */
-	if((fd = open(file, 3))<0){
-		Xerror3("<< can't open", file, strerror(errno));
-		return;
-	}
-	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
-}
-
-void
-Xread(void)
-{
-	char *file;
-	int fd;
-
-	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((fd = open(file, 0))<0){
-		Xerror3("< can't open", file, strerror(errno));
-		return;
-	}
-	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
-	poplist();
-}
-
-void
-Xrdwr(void)
-{
-	char *file;
-	int fd;
-
-	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((fd = open(file, 2))<0){
-		Xerror3("<> can't open", file, strerror(errno));
-		return;
-	}
-	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
-	poplist();
-}
-
-void
-Xpopredir(void)
-{
-	redir *rp = runq->redir;
-
-	if(rp==0)
-		panic("Xpopredir null!", 0);
-	runq->redir = rp->next;
-	if(rp->type==ROPEN)
-		close(rp->from);
-	free(rp);
-}
-
-void
-Xreturn(void)
-{
-	while(runq->redir!=runq->startredir)
-		Xpopredir();
-	popthread();
-	if(runq==0)
-		_exit(0);
-}
-
-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 fd;
-
-	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((fd = create(file, ORDWR, 0644))<0){
-		Xerror3("> can't create", file, strerror(errno));
-		return;
-	}
-	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
-	poplist();
-}
-
-void
-Xmatch(void)
-{
-	word *p;
-	char *s;
-
-	setstatus("no match");
-	s = runq->argv->words->word;
-	for(p = runq->argv->next->words;p;p = p->next)
-		if(match(s, p->word, '\0')){
-			setstatus("");
-			break;
-		}
-	poplist();
-	poplist();
-}
-
-void
-Xcase(void)
-{
-	word *p;
-	char *s;
-	int ok = 0;
-
-	s = runq->argv->next->words->word;
-	for(p = runq->argv->words;p;p = p->next){
-		if(match(s, p->word, '\0')){
-			ok = 1;
-			break;
-		}
-	}
-	if(ok)
-		runq->pc++;
-	else
-		runq->pc = runq->code[runq->pc].i;
-	poplist();
-}
-
-static word*
-conclist(word *lp, word *rp, word *tail)
-{
-	word *v, *p, **end;
-	int ln, rn;
-
-	for(end = &v;;){
-		ln = strlen(lp->word), rn = strlen(rp->word);
-		p = Newword(emalloc(ln+rn+1), (word *)0);
-		memmove(p->word, lp->word, ln);
-		memmove(p->word+ln, rp->word, rn+1);
-		*end = p, end = &p->next;
-		if(lp->next == 0 && rp->next == 0)
-			break;
-		if(lp->next) lp = lp->next;
-		if(rp->next) rp = rp->next;
-	}
-	*end = tail;
-	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;
-	}
-	v = vlook(runq->argv->words->word);
-	poplist();
-	freewords(v->val);
-	v->val = Poplist();
-	v->changed = 1;
-}
-
-/*
- * 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;
-	}
-	n = 0;
-	s = runq->argv->words->word;
-	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
-Xqw(void)
-{
-	char *s, *d;
-	word *a, *p;
-	int n;
-
-	a = runq->argv->words;
-	if(a==0){
-		pushword("");
-		return;
-	}
-	if(a->next==0)
-		return;
-	n=0;
-	for(p=a;p;p=p->next)
-		n+=1+strlen(p->word);
-	s = emalloc(n+1);
-	d = s;
-	d += strlen(strcpy(d, a->word));
-	for(p=a->next;p;p=p->next){
-		*d++=' ';
-		d += strlen(strcpy(d, p->word));
-	}
-	free(a->word);
-	freewords(a->next);
-	a->word = s;
-	a->next = 0;
-}
-
-static word*
-copynwords(word *a, word *tail, int n)
-{
-	word *v, **end;
-	
-	v = 0;
-	end = &v;
-	while(n-- > 0){
-		*end = newword(a->word, 0);
-		end = &(*end)->next;
-		a = a->next;
-	}
-	*end = tail;
-	return v;
-}
-
-static word*
-subwords(word *val, int len, word *sub, word *a)
-{
-	int n, m;
-	char *s;
-
-	if(sub==0)
-		return a;
-	a = subwords(val, len, sub->next, a);
-	s = sub->word;
-	m = 0;
-	n = 0;
-	while('0'<=*s && *s<='9')
-		n = n*10+ *s++ -'0';
-	if(*s == '-'){
-		if(*++s == 0)
-			m = len - n;
-		else{
-			while('0'<=*s && *s<='9')
-				m = m*10+ *s++ -'0';
-			m -= n;
-		}
-	}
-	if(n<1 || n>len || m<0)
-		return a;
-	if(n+m>len)
-		m = len-n;
-	while(--n > 0)
-		val = val->next;
-	return copynwords(val, a, m+1);
-}
-
-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;
-	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, num[12];
-	int n;
-
-	if(count(runq->argv->words)!=1){
-		Xerror1("$# variable name not singleton!");
-		return;
-	}
-	n = 0;
-	s = runq->argv->words->word;
-	for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
-	if(n==0 || *t){
-		a = vlook(s)->val;
-		inttoascii(num, count(a));
-	}
-	else{
-		a = vlook("*")->val;
-		inttoascii(num, a && 1<=n && n<=count(a)?1:0);
-	}
-	poplist();
-	pushword(num);
-}
-
-void
-Xlocal(void)
-{
-	if(count(runq->argv->words)!=1){
-		Xerror1("local variable name must be singleton");
-		return;
-	}
-	runq->local = newvar(runq->argv->words->word, runq->local);
-	poplist();
-	runq->local->val = Poplist();
-	runq->local->changed = 1;
-}
-
-void
-Xunlocal(void)
-{
-	var *hid, *v = runq->local;
-	if(v==0)
-		panic("Xunlocal: no locals!", 0);
-	runq->local = v->next;
-	hid = vlook(v->name);
-	hid->changed = 1;
-	freevar(v);
-}
-
-void
-Xfn(void)
-{
-	var *v;
-	word *a;
-	int pc = runq->pc;
-	runq->pc = runq->code[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 = pc+2;
-		v->fnchanged = 1;
-	}
-	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();
-}
-
-static char*
-concstatus(char *s, char *t)
-{
-	int n, m;
-
-	if(t==0) return s;
-	if(s==0) return t;
-	n = strlen(s);
-	m = strlen(t);
-	s = erealloc(s, n+m+2);
-	if(n > 0) s[n++]='|';
-	memmove(s+n, t, m+1);
-	free(t);
-	return s;
-}
-
-void
-Xpipewait(void)
-{
-	char *old = Getstatus();
-	if(runq->pid==-1){
-		Setstatus(concstatus(runq->status, old));
-		runq->status=0;
-	}else{
-		while(Waitfor(runq->pid) < 0)
-			;
-		runq->pid=-1;
-		Setstatus(concstatus(Getstatus(), old));
-	}
-}
-
-static char *promptstr;
-
-void
-Xrdcmds(void)
-{
-	thread *p = runq;
-
-	if(flag['s'] && !truestatus())
-		pfmt(err, "status=%v\n", vlook("status")->val);
-	flushio(err);
-
-	lex = p->lex;
-	if(p->iflag){
-		word *prompt = vlook("prompt")->val;
-		if(prompt)
-			promptstr = prompt->word;
-		else
-			promptstr="% ";
-	}
-	Noerror();
-	nerror = 0;
-	if(yyparse()){
-		if(p->iflag && (!lex->eof || errno == EINTR)){
-			if(errno == EINTR){
-				pchr(err, '\n');
-				lex->eof = 0;
-			}
-			--p->pc;	/* go back for next command */
-		}
-	}
-	else{
-		if(lex->eof){
-			dontclose(lex->input->fd);
-			freelexer(lex);
-			p->lex = 0;
-		} else
-			--p->pc;	/* re-execute Xrdcmds after codebuf runs */
-		start(codebuf, 2, p->local, p->redir);
-	}
-	lex = 0;
-	freenodes();
-}
-
-void
-pprompt(void)
-{
-	word *prompt;
-
-	if(!runq->iflag)
-		return;
-
-	Prompt(promptstr);
-	doprompt = 0;
-
-	prompt = vlook("prompt")->val;
-	if(prompt && prompt->next)
-		promptstr = prompt->next->word;
-	else
-		promptstr = "\t";
-}
-
-char*
-srcfile(thread *p)
-{
-	return p->code[1].s;
-}
-
-void
-Xerror1(char *s)
-{
-	setstatus("error");
-	pfln(err, srcfile(runq), runq->line);
-	pfmt(err, ": %s\n", s);
-	flushio(err);
-	while(!runq->iflag) Xreturn();
-}
-void
-Xerror2(char *s, char *e)
-{
-	setstatus(e);
-	pfln(err, srcfile(runq), runq->line);
-	pfmt(err, ": %s: %s\n", s, e);
-	flushio(err);
-	while(!runq->iflag) Xreturn();
-}
-void
-Xerror3(char *s, char *m, char *e)
-{
-	setstatus(e);
-	pfln(err, srcfile(runq), runq->line);
-	pfmt(err, ": %s: %s: %s\n", s, m, e);
-	flushio(err);
-	while(!runq->iflag) Xreturn();
-}
-
-void
-Setstatus(char *s)
-{
-	setvar("status", Newword(s?s:estrdup(""), (word *)0));
-}
-void
-setstatus(char *s)
-{
-	Setstatus(estrdup(s));
-}
-char*
-Getstatus(void)
-{
-	var *status = vlook("status");
-	word *val = status->val;
-	if(val==0) return 0;
-	status->val=0;
-	status->changed=1;
-	freewords(val->next);
-	return Freeword(val);
-}
-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
-Xfor(void)
-{
-	word *a = runq->argv->words;
-	if(a==0){
-		poplist();
-		runq->pc = runq->code[runq->pc].i;
-	}
-	else{
-		runq->argv->words = a->next;
-		a->next = 0;
-		freewords(runq->local->val);
-		runq->local->val = a;
-		runq->local->changed = 1;
-		runq->pc++;
-	}
-}
-
-void
-Xglob(void)
-{
-	word *a, *x;
-
-	for(a = runq->argv->words; a; a = x){
-		x = a->next;
-		globword(a);
-	}
-}
-
-void
-Xsrcline(void)
-{
-	runq->line = runq->code[runq->pc++].i;
-}
--- a/librc/exec.h
+++ /dev/null
@@ -1,85 +1,0 @@
-/*
- * 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), Xqw(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), Xhere(void), Xhereq(void);
-extern void Xrdwr(void), Xsrcline(void);
-extern 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), Xpush(void);
-extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void);
-extern void Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void);
-extern void Xerror1(char*);
-extern void Xerror2(char*,char*);
-extern void Xerror3(char*,char*,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{
-	int type;			/* what to do */
-	int from, to;			/* what to do it to */
-	redir *next;			/* what else to do (reverse order) */
-};
-
-/*
- * redir types
- */
-#define	ROPEN	1			/* dup2(from, to); close(from); */
-#define	RDUP	2			/* dup2(from, to); */
-#define	RCLOSE	3			/* close(from); */
-void	shuffleredir(void);
-
-struct thread{
-	code *code;			/* code for this thread */
-	int pc;				/* code[pc] is the next instruction */
-	int line;			/* source code line for Xsrcline */
-	list *argv;			/* argument stack */
-	redir *redir;			/* redirection stack */
-	redir *startredir;		/* redir inheritance point */
-	var *local;			/* list of local variables */
-	lexer *lex;			/* lexer for Xrdcmds */
-	int iflag;			/* interactive? */
-	int pid;			/* process for Xpipewait to wait for */
-	char *status;			/* status for Xpipewait */
-	thread *ret;			/* who continues when this finishes */
-};
-extern thread *runq;
-void turfstack(var*);
-
-extern int mypid;
-extern int ntrap;			/* number of outstanding traps */
-extern int trap[NSIG];			/* number of outstanding traps per type */
-
-code *codecopy(code*);
-extern code *codebuf;			/* compiler output */
-extern int ifnot;
-
-struct builtin{
-	char *name;
-	void (*fnc)(void);
-};
-extern void (*builtinfunc(char *name))(void);
-
-void execcd(void), execwhatis(void), execeval(void), execexec(void);
-int execforkexec(void);
-void execexit(void), execshift(void);
-void execwait(void), execumask(void), execdot(void), execflag(void);
-void execfunc(var*), execcmds(io*, char*, var*, redir*);
-void startfunc(var*, word*, var*, redir*);
-
-char *srcfile(thread*);
-char *getstatus(void);
--- a/librc/fns.h
+++ /dev/null
@@ -1,54 +1,0 @@
-
-int	Executable(char*);
-char*	Freeword(word*);
-char*	Getstatus(void);
-int	Isatty(int);
-word*	Newword(char*,word*);
-void	Noerror(void);
-word*	Poplist(void);
-char*	Popword(void);
-word*	Pushword(char*);
-void	Setstatus(char*);
-void	Updenv(void);
-void	Vinit(void);
-int	Waitfor(int);
-void	addwaitpid(int);
-void	clearwaitpids(void);
-void	codefree(code*);
-int	compile(tree*);
-int	count(word*);
-char*	deglob(char*);
-void	delwaitpid(int);
-void	dotrap(void);
-void	freenodes(void);
-void    notifyf(void*, char*);
-void	freewords(word*);
-void	globword(word*);
-int	havewaitpid(int);
-int	idchr(int);
-void	inttoascii(char*, int);
-void	kinit(void);
-int	mapfd(int);
-int	match(char*, char*, int);
-char*	rcmakepath(char*, char*);
-void	pfln(io*, char*, int);
-void	poplist(void);
-void	popword(void);
-void	pprompt(void);
-void	Prompt(char*);
-void	psubst(io*, unsigned char*);
-void	pushlist(void);
-void	pushredir(int, int, int);
-word*	pushword(char*);
-void	readhere(io*);
-void	heredoc(tree*);
-void	setstatus(char*);
-void	skipnl(void);
-void	start(code*, int, var*, redir*);
-int	truestatus(void);
-void	usage(char*);
-int	wordchr(int);
-void	yyerror(char*);
-int	yylex(void);
-int	yyparse(void);
-#define notify(x)
\ No newline at end of file
--- a/librc/getflags.c
+++ /dev/null
@@ -1,234 +1,0 @@
-#include <rc.h>
-#include "getflags.h"
-#include "fns.h"
-char *flagset[] = {"<flag>"};
-char **flag[NFLAG];
-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;
-	int i, j, c, count;
-	flagarg = flags;
-	if(cmdname==0)
-		cmdname = argv[0];
-
-	i = 1;
-	while(i!=argc){
-		if(argv[i][0] != '-' || argv[i][1] == '\0'){
-			if(stop)		/* always true in rc */
-				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");
-	setstatus("bad flags");
-	_exit(0);
-}
-
-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;
-	}
-}
--- a/librc/getflags.h
+++ /dev/null
@@ -1,7 +1,0 @@
-#define	NFLAG	128
-
-extern char **flag[NFLAG];
-extern char *cmdname;
-extern char *flagset[];
-
-int getflags(int, char*[], char*, int);
--- a/librc/glob.c
+++ /dev/null
@@ -1,272 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <dirent.h>
-#include <rc.h>
-#include "exec.h"
-#include "fns.h"
-
-/*
- * delete all the GLOB marks from s, in place
- */
-char*
-deglob(char *s)
-{
-	char *r = strchr(s, GLOB);
-	if(r){
-		char *w = r++;
-		do{
-			if(*r==GLOB)
-				r++;
-			*w++=*r;
-		}while(*r++);
-	}
-	return s;
-}
-
-static int
-globcmp(const void *s, const void *t)
-{
-	return strcmp(*(char**)s, *(char**)t);
-}
-
-static 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((void *)list, n, sizeof(void *), globcmp);
-	for(a = left,n = 0;a!=right;a = a->next,n++) a->word = list[n];
-	free(list);
-}
-
-/*
- * 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
- */
-
-static 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, '/');
-}
-
-static void
-pappend(char **pdir, char *name)
-{
-	char *path = rcmakepath(*pdir, name);
-	free(*pdir);
-	*pdir = path;
-}
-
-char*
-nextdirent(void *arg)
-{
-	DIR *rd = arg;
-	struct dirent *ent = readdir(rd);
-	if(ent == NULL)
-		return 0;
-	return ent->d_name;
-}
-
-static word*
-globdir(word *list, char *pattern, char *name)
-{
-	char *slash, *glob, *entry;
-	void *dir;
-
-#ifdef Plan9
-	/* append slashes, readdir() already filtered directories */
-	while(*pattern=='/'){
-		pappend(&name, "/");
-		pattern++;
-	}
-#endif
-	if(*pattern=='\0')
-		return Newword(name, list);
-
-	/* scan the pattern looking for a component with a metacharacter in it */
-	glob=strchr(pattern, GLOB);
-
-	/* If we ran out of pattern, append the name if accessible */
-	if(glob==0){
-		pappend(&name, pattern);
-		if(access(name, 0)==0)
-			return Newword(name, list);
-		goto out;
-	}
-
-	*glob='\0';
-	slash=strrchr(pattern, '/');
-	if(slash){
-		*slash='\0';
-		pappend(&name, pattern);
-		*slash='/';
-		pattern=slash+1;
-	}
-	*glob=GLOB;
-
-	/* read the directory and recur for any entry that matches */
-	dir = opendir(name[0]?name:".");
-	if(dir==0)
-		goto out;
-	slash=strchr(glob, '/');
-	while((entry=nextdirent(dir)) != 0){
-		if(matchfn(entry, pattern))
-			list = globdir(list, slash?slash:"", rcmakepath(name, entry));
-	}
-	closedir(dir);
-out:
-	free(name);
-	return list;
-}
-
-/*
- * Subsitute a word with its glob in place.
- */
-void
-globword(word *w)
-{
-	word *left, *right;
-
-	if(w==0 || strchr(w->word, GLOB)==0)
-		return;
-	right = w->next;
-	left = globdir(right, w->word, estrdup(""));
-	if(left == right) {
-		deglob(w->word);
-	} else {
-		free(w->word);
-		globsort(left, right);
-		w->next = left->next;
-		w->word = Freeword(left);
-	}
-}
-
-/*
- * Return a pointer to the next utf code in the string,
- * not jumping past nuls in broken utf codes!
- */
-static char*
-nextutf(char *p)
-{
-	int i, n, c = *p;
-
-	if(onebyte(c))
-		return p+1;
-	if(twobyte(c))
-		n = 2;
-	else if(threebyte(c))
-		n = 3;
-	else
-		n = 4;
-	for(i = 1; i < n; i++)
-		if(!xbyte(p[i]))
-			break;
-	return p+i;
-}
-
-/*
- * Convert the utf code at *p to a unicode value
- */
-static int
-unicode(char *p)
-{
-	int c = *p;
-
-	if(onebyte(c))
-		return c&0xFF;
-	if(twobyte(c)){
-		if(xbyte(p[1]))
-			return ((c&0x1F)<<6) | (p[1]&0x3F);
-	} else if(threebyte(c)){
-		if(xbyte(p[1]) && xbyte(p[2]))
-			return ((c&0x0F)<<12) | ((p[1]&0x3F)<<6) | (p[2]&0x3F);
-	} else if(fourbyte(c)){
-		if(xbyte(p[1]) && xbyte(p[2]) && xbyte(p[3]))
-			return ((c&0x07)<<18) | ((p[1]&0x3F)<<12) | ((p[2]&0x3F)<<6) | (p[3]&0x3F);
-	}
-	return -1;
-}
-
-/*
- * Do p and q point at equal utf codes
- */
-static int
-equtf(char *p, char *q)
-{
-	if(*p!=*q)
- 		return 0;
-	return unicode(p) == unicode(q);
-}
-
-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';
-}
--- a/librc/havefork.c
+++ /dev/null
@@ -1,243 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-#include <errno.h>
-#include "getflags.h"
-#include "exec.h"
-#include "io.h"
-#include "fns.h"
-
-static int *waitpids;
-static int nwaitpids;
-
-void
-addwaitpid(int pid)
-{
-	waitpids = erealloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]);
-	waitpids[nwaitpids++] = pid;
-}
-
-void
-delwaitpid(int pid)
-{
-	int r, w;
-	
-	for(r=w=0; r<nwaitpids; r++)
-		if(waitpids[r] != pid)
-			waitpids[w++] = waitpids[r];
-	nwaitpids = w;
-}
-
-void
-clearwaitpids(void)
-{
-	nwaitpids = 0;
-}
-
-int
-havewaitpid(int pid)
-{
-	int i;
-
-	for(i=0; i<nwaitpids; i++)
-		if(waitpids[i] == pid)
-			return 1;
-	return 0;
-}
-
-void
-Xasync(void)
-{
-	int pid;
-	char npid[10];
-
-	switch(pid = fork()){
-	case -1:
-		Xerror2("try again", strerror(errno));
-		break;
-	case 0:
-		clearwaitpids();
-		start(runq->code, runq->pc+1, runq->local, runq->redir);
-		runq->ret = 0;
-		break;
-	default:
-		addwaitpid(pid);
-		runq->pc = runq->code[runq->pc].i;
-		inttoascii(npid, pid);
-		setvar("apid", newword(npid, (word *)0));
-		break;
-	}
-}
-
-void
-Xpipe(void)
-{
-	thread *p = runq;
-	int pid, pc = p->pc;
-	int lfd = p->code[pc++].i;
-	int rfd = p->code[pc++].i;
-	int pfd[2];
-
-	if(pipe(pfd)<0){
-		Xerror2("can't get pipe", strerror(errno));
-		return;
-	}
-	switch(pid = fork()){
-	case -1:
-		Xerror2("try again", strerror(errno));
-		break;
-	case 0:
-		clearwaitpids();
-		close(pfd[PRD]);
-		start(p->code, pc+2, runq->local, runq->redir);
-		runq->ret = 0;
-		pushredir(ROPEN, pfd[PWR], lfd);
-		break;
-	default:
-		addwaitpid(pid);
-		close(pfd[PWR]);
-		start(p->code, p->code[pc].i, runq->local, runq->redir);
-		pushredir(ROPEN, pfd[PRD], rfd);
-		p->pc = p->code[pc+1].i;
-		p->pid = pid;
-		break;
-	}
-}
-
-/*
- * Who should wait for the exit from the fork?
- */
-
-void
-Xbackq(void)
-{
-	int pid, pfd[2];
-	char *s, *split;
-	word *end, **link;
-	io *f;
-
-	if(pipe(pfd)<0){
-		Xerror2("can't make pipe", strerror(errno));
-		return;
-	}
-	switch(pid = fork()){
-	case -1:
-		Xerror2("try again", strerror(errno));
-		close(pfd[PRD]);
-		close(pfd[PWR]);
-		return;
-	case 0:
-		clearwaitpids();
-		close(pfd[PRD]);
-		start(runq->code, runq->pc+1, runq->local, runq->redir);
-		pushredir(ROPEN, pfd[PWR], 1);
-		return;
-	default:
-		addwaitpid(pid);
-		close(pfd[PWR]);
-
-		split = Popword();
-		poplist();
-		f = openiofd(pfd[PRD]);
-		end = runq->argv->words;
-		link = &runq->argv->words;
-		while((s = rstr(f, split)) != 0){
-			*link = Newword(s, (word*)0);
-			link = &(*link)->next;
-		}
-		*link = end;
-		closeio(f);
-		free(split);
-
-		Waitfor(pid);
-
-		runq->pc = runq->code[runq->pc].i;
-		return;
-	}
-}
-
-void
-Xpipefd(void)
-{
-	thread *p = runq;
-	int pid, pc = p->pc;
-	char name[40];
-	int pfd[2];
-	int sidefd, mainfd;
-
-	if(pipe(pfd)<0){
-		Xerror2("can't get pipe", strerror(errno));
-		return;
-	}
-	if(p->code[pc].i==READ){
-		sidefd = pfd[PWR];
-		mainfd = pfd[PRD];
-	}
-	else{
-		sidefd = pfd[PRD];
-		mainfd = pfd[PWR];
-	}
-	switch(pid = fork()){
-	case -1:
-		Xerror2("try again", strerror(errno));
-		break;
-	case 0:
-		clearwaitpids();
-		close(mainfd);
-		start(p->code, pc+2, runq->local, runq->redir);
-		pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0);
-		runq->ret = 0;
-		break;
-	default:
-		addwaitpid(pid);
-		close(sidefd);
-		pushredir(ROPEN, mainfd, mainfd);
-		shuffleredir();	/* shuffle redir to bottom of stack for Xpopredir() */
-		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:
-		Xerror2("try again", strerror(errno));
-		break;
-	case 0:
-		clearwaitpids();
-		start(runq->code, runq->pc+1, runq->local, runq->redir);
-		runq->ret = 0;
-		break;
-	default:
-		addwaitpid(pid);
-		while(Waitfor(pid) < 0)
-			;
-		runq->pc = runq->code[runq->pc].i;
-		break;
-	}
-}
-
-int
-execforkexec(void)
-{
-	int pid;
-
-	switch(pid = fork()){
-	case -1:
-		return -1;
-	case 0:
-		clearwaitpids();
-		pushword("exec");
-		execexec();
-		/* does not return */
-	}
-	addwaitpid(pid);
-	return pid;
-}
--- a/librc/here.c
+++ /dev/null
@@ -1,138 +1,0 @@
-#include <rc.h>
-#include "exec.h"
-#include "io.h"
-#include "fns.h"
-#include "y.tab.h"
-
-void psubst(io*, unsigned char*);
-void pstrs(io*, word*);
-
-static char*
-readhere1(tree *tag, io *in)
-{
-	io *out;
-	char c, *m;
-
-	pprompt();
-	out = openiostr();
-	m = tag->str;
-	while((c = rchr(in)) != EOF){
-		if(c=='\0'){
-			yyerror("NUL bytes in here doc");
-			closeio(out);
-			return 0;
-		}
-		if(c=='\n'){
-			lex->line++;
-			if(m && *m=='\0'){
-				out->bufp -= m - tag->str;
-				*out->bufp='\0';
-				break;
-			}
-			pprompt();
-			m = tag->str;
-		} else if(m){
-			if(*m == c){
-				m++;
-			} else {
-				m = 0;
-			}
-		}
-		pchr(out, c);
-	}
-	doprompt = 1;
-	return closeiostr(out);
-}
-
-static tree *head, *tail;
-
-void
-heredoc(tree *redir)
-{
-	if(redir->child[0]->type!=WORD){
-		yyerror("Bad here tag");
-		return;
-	}
-	redir->child[2]=0;
-	if(head)
-		tail->child[2]=redir;
-	else
-		head=redir;
-	tail=redir;
-}
-
-void
-readhere(io *in)
-{
-	while(head){
-		tail=head->child[2];
-		head->child[2]=0;
-		head->str=readhere1(head->child[0], in);
-		head=tail;
-	}
-}
-
-void
-psubst(io *f, unsigned char *s)
-{
-	unsigned char *t, *u;
-	word *star;
-	int savec, n;
-
-	while(*s){
-		if(*s!='$'){
-			if(0xa0 <= *s && *s <= 0xf5){
-				pchr(f, *s++);
-				if(*s=='\0')
-					break;
-			}
-			else if(0xf6 <= *s && *s <= 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((char *)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);
-	}
-}
--- a/librc/io.c
+++ /dev/null
@@ -1,304 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-#include "exec.h"
-#include "io.h"
-#include "fns.h"
-
-enum {
-	NBUF = 8192,
-};
-
-void
-vpfmt(io *f, char *fmt, va_list ap)
-{
-	for(;*fmt;fmt++) {
-		if(*fmt!='%') {
-			pchr(f, *fmt);
-			continue;
-		}
-		if(*++fmt == '\0')		/* "blah%"? */
-			break;
-		switch(*fmt){
-		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':
-			pptr(f, va_arg(ap, void*));
-			break;
-		case 'Q':
-			pquo(f, va_arg(ap, char *));
-			break;
-		case 'q':
-			pwrd(f, va_arg(ap, char *));
-			break;
-		case 's':
-			pstr(f, va_arg(ap, char *));
-			break;
-		case 't':
-			pcmd(f, va_arg(ap, tree *));
-			break;
-		case 'v':
-			pval(f, va_arg(ap, word *));
-			break;
-		default:
-			pchr(f, *fmt);
-			break;
-		}
-	}
-}
-
-void
-pfmt(io *f, char *fmt, ...)
-{
-	va_list ap;
-	va_start(ap, fmt);
-	vpfmt(f, fmt, ap);
-	va_end(ap);
-}
-
-void
-pchr(io *b, int c)
-{
-	if(b->bufp>=b->ebuf)
-		flushio(b);
-	*b->bufp++=c;
-}
-
-int
-rchr(io *b)
-{
-	if(b->bufp>=b->ebuf)
-		return emptyiobuf(b);
-	return *b->bufp++;
-}
-
-char*
-rstr(io *b, char *stop)
-{
-	char *s, *p;
-	int l, m, n;
-
-	do {
-		l = rchr(b);
-		if(l == EOF)
-			return 0;
-	} while(l && strchr(stop, l));
-	b->bufp--;
-
-	s = 0;
-	l = 0;
-	for(;;){
-		p = (char*)b->bufp;
-		n = (char*)b->ebuf - p;
-		if(n > 0){
-			for(m = 0; m < n; m++){
-				if(strchr(stop, p[m])==0)
-					continue;
-
-				b->bufp += m+1;
-				if(m > 0 || s==0){
-					s = erealloc(s, l+m+1);
-					memmove(s+l, p, m);
-					l += m;
-				}
-				s[l]='\0';
-				return s;
-			}
-			s = erealloc(s, l+m+1);
-			memmove(s+l, p, m);
-			l += m;
-			b->bufp += m;
-		}
-		if(emptyiobuf(b) == EOF){
-			if(s) s[l]='\0';
-			return s;
-		}
-		b->bufp--;
-	}
-}
-
-void
-pquo(io *f, char *s)
-{
-	pchr(f, '\'');
-	for(;*s;s++){
-		if(*s=='\'')
-			pchr(f, *s);
-		pchr(f, *s);
-	}
-	pchr(f, '\'');
-}
-
-void
-pwrd(io *f, char *s)
-{
-	char *t;
-	for(t = s;*t;t++) if(*t >= 0 && (*t <= ' ' || strchr("`^#*[]=|\\?${}()'<>&;", *t))) break;
-	if(t==s || *t)
-		pquo(f, s);
-	else pstr(f, s);
-}
-
-void
-pptr(io *f, void *p)
-{
-	static char hex[] = "0123456789ABCDEF";
-	unsigned long v;
-	int n;
-
-	v = (unsigned long)p;
-	if(sizeof(v) == sizeof(p) && v>>32)
-		for(n = 60;n>=32;n-=4) pchr(f, hex[(v>>n)&0xF]);
-	for(n = 28;n>=0;n-=4) pchr(f, hex[(v>>n)&0xF]);
-}
-
-void
-pstr(io *f, char *s)
-{
-	if(s==0)
-		s="(null)";
-	while(*s) pchr(f, *s++);
-}
-
-void
-pdec(io *f, int 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, unsigned n)
-{
-	if(n>7)
-		poct(f, n>>3);
-	pchr(f, (n&7)+'0');
-}
-
-void
-pval(io *f, word *a)
-{
-	if(a==0)
-		return;
-	while(a->next && a->next->word){
-		pwrd(f, (char *)a->word);
-		pchr(f, ' ');
-		a = a->next;
-	}
-	pwrd(f, (char *)a->word);
-}
-
-io*
-newio(unsigned char *buf, int len, int fd)
-{
-	io *f = new(io);
-	f->buf = buf;
-	f->bufp = buf;
-	f->ebuf = buf+len;
-	f->fd = fd;
-	return f;
-}
-
-/*
- * Open a string buffer for writing.
- */
-io*
-openiostr(void)
-{
-	unsigned char *buf = emalloc(100+1);
-	memset(buf, '\0', 100+1);
-	return newio(buf, 100, -1);
-}
-
-/*
- * Return the buf, free the io
- */
-char*
-closeiostr(io *f)
-{
-	void *buf = f->buf;
-	free(f);
-	return buf;
-}
-
-/*
- * Use a open file descriptor for reading.
- */
-io*
-openiofd(int fd)
-{
-	return newio(emalloc(NBUF), 0, fd);
-}
-
-/*
- * Open a corebuffer to read.  EOF occurs after reading len
- * characters from buf.
- */
-io*
-openiocore(void *buf, int len)
-{
-	return newio(buf, len, -1);
-}
-
-void
-flushio(io *f)
-{
-	int n;
-
-	if(f->fd<0){
-		n = f->ebuf - f->buf;
-		f->buf = erealloc(f->buf, n+n+1);
-		f->bufp = f->buf + n;
-		f->ebuf = f->bufp + n;
-		memset(f->bufp, '\0', n+1);
-	}
-	else{
-		n = f->bufp - f->buf;
-		if(n && write(f->fd, f->buf, n) != n){
-			write(2, "Write error\n", 12);
-			if(ntrap)
-				dotrap();
-		}
-		f->bufp = f->buf;
-		f->ebuf = f->buf+NBUF;
-	}
-}
-
-void
-closeio(io *f)
-{
-	if(f->fd>=0) close(f->fd);
-	free(closeiostr(f));
-}
-
-int
-emptyiobuf(io *f)
-{
-	int n;
-	if(f->fd<0 || (n = read(f->fd, f->buf, NBUF))<=0) return EOF;
-	f->bufp = f->buf;
-	f->ebuf = f->buf + n;
-	return *f->bufp++;
-}
--- a/librc/io.h
+++ /dev/null
@@ -1,28 +1,0 @@
-#define	EOF	(-1)
-
-struct io{
-	int	fd;
-	unsigned char *buf, *bufp, *ebuf;
-	io	*next;
-};
-
-io *openiofd(int), *openiostr(void), *openiocore(void*, int);
-void pchr(io*, int);
-int rchr(io*);
-char *rstr(io*, char*);
-char *closeiostr(io*);
-void closeio(io*);
-int emptyiobuf(io*);
-void flushio(io*);
-void pdec(io*, int);
-void poct(io*, unsigned);
-void pptr(io*, void*);
-void pquo(io*, char*);
-void pwrd(io*, char*);
-void pstr(io*, char*);
-void pcmd(io*, tree*);
-void pval(io*, word*);
-void pfun(io*, void(*)(void));
-void pfnc(io*, thread*);
-void pfmt(io*, char*, ...);
-void vpfmt(io*, char*, va_list);
--- a/librc/lex.c
+++ /dev/null
@@ -1,436 +1,0 @@
-#include <rc.h>
-#include "io.h"
-#include "getflags.h"
-#include "fns.h"
-#include "y.tab.h"
-
-lexer *lex;
-
-int doprompt = 1;
-int nerror;
-
-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);
-}
-
-lexer*
-newlexer(io *input, char *file)
-{
-	lexer *n = new(struct lexer);
-	n->input = input;
-	n->file = file;
-	n->line = 1;
-	n->eof = 0;
-	n->future = EOF;
-	n->peekc = '{';
-	n->epilog = "}\n";
-	n->lastc = 0;
-	n->inquote = 0;
-	n->incomm = 0;
-	n->lastword = 0;
-	n->lastdol = 0;
-	n->iflast = 0;
-	n->qflag = 0;
-	n->tok[0] = 0;
-	return n;
-}
-
-void
-freelexer(lexer *p)
-{
-	closeio(p->input);
-	free(p->file);
-	free(p);
-}
-
-/*
- * read a character from the input stream
- */	
-static int
-getnext(void)
-{
-	int c;
-
-	if(lex->peekc!=EOF){
-		c = lex->peekc;
-		lex->peekc = EOF;
-		return c;
-	}
-	if(lex->eof){
-epilog:
-		if(*lex->epilog)
-			return *lex->epilog++;
-		doprompt = 1;
-		return EOF;
-	}
-	if(doprompt)
-		pprompt();
-	c = rchr(lex->input);
-	if(c=='\\' && !lex->inquote){
-		c = rchr(lex->input);
-		if(c=='\n' && !lex->incomm){		/* don't continue a comment */
-			doprompt = 1;
-			c=' ';
-		}
-		else{
-			lex->peekc = c;
-			c='\\';
-		}
-	}
-	if(c==EOF){
-		lex->eof = 1;
-		goto epilog;
-	} else {
-		if(c=='\n')
-			doprompt = 1;
-		if((!lex->qflag && flag['v']!=0) || flag['V'])
-			pchr(err, c);
-	}
-	return c;
-}
-
-/*
- * Look ahead in the input stream
- */
-static int
-nextc(void)
-{
-	if(lex->future==EOF)
-		lex->future = getnext();
-	return lex->future;
-}
-
-/*
- * Consume the lookahead character.
- */
-static int
-advance(void)
-{
-	int c = nextc();
-	lex->lastc = lex->future;
-	lex->future = EOF;
-	if(c == '\n')
-		lex->line++;
-	return c;
-}
-
-static void
-skipwhite(void)
-{
-	int c;
-	for(;;){
-		c = nextc();
-		/* Why did this used to be  if(!inquote && c=='#') ?? */
-		if(c=='#'){
-			lex->incomm = 1;
-			for(;;){
-				c = nextc();
-				if(c=='\n' || c==EOF) {
-					lex->incomm = 0;
-					break;
-				}
-				advance();
-			}
-		}
-		if(c==' ' || c=='\t')
-			advance();
-		else return;
-	}
-}
-
-void
-skipnl(void)
-{
-	int c;
-	for(;;){
-		skipwhite();
-		c = nextc();
-		if(c!='\n')
-			return;
-		advance();
-	}
-}
-
-static int
-nextis(int c)
-{
-	if(nextc()==c){
-		advance();
-		return 1;
-	}
-	return 0;
-}
-
-static char*
-addtok(char *p, int val)
-{
-	if(p==0)
-		return 0;
-	if(p==&lex->tok[NTOK-1]){
-		*p = 0;
-		yyerror("token buffer too short");
-		return 0;
-	}
-	*p++=val;
-	return p;
-}
-
-static char*
-addutf(char *p, int c)
-{
-	int i, n;
-
-	p = addtok(p, c);	/* 1-byte UTF runes are special */
-	if(onebyte(c))
-		return p;
-	if(twobyte(c))
-		n = 2;
-	else if(threebyte(c))
-		n = 3;
-	else
-		n = 4;
-	for(i = 1; i < n; i++) {
-		c = nextc();
-		if(c == EOF || !xbyte(c))
-			break;
-		p = addtok(p, advance());
-	}
-	return p;
-}
-
-int
-yylex(void)
-{
-	int glob, c, d = nextc();
-	char *tok = lex->tok;
-	char *w = tok;
-	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(lex->lastword){
-		lex->lastword = 0;
-		if(d=='('){
-			advance();
-			strcpy(tok, "( [SUB]");
-			return SUB;
-		}
-		if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
-			strcpy(tok, "^");
-			return '^';
-		}
-	}
-	lex->inquote = 0;
-	skipwhite();
-	switch(c = advance()){
-	case EOF:
-		lex->lastdol = 0;
-		strcpy(tok, "EOF");
-		return EOF;
-	case '$':
-		lex->lastdol = 1;
-		if(nextis('#')){
-			strcpy(tok, "$#");
-			return COUNT;
-		}
-		if(nextis('"')){
-			strcpy(tok, "$\"");
-			return '"';
-		}
-		strcpy(tok, "$");
-		return '$';
-	case '&':
-		lex->lastdol = 0;
-		if(nextis('&')){
-			skipnl();
-			strcpy(tok, "&&");
-			return ANDAND;
-		}
-		strcpy(tok, "&");
-		return '&';
-	case '|':
-		lex->lastdol = 0;
-		if(nextis(c)){
-			skipnl();
-			strcpy(tok, "||");
-			return OROR;
-		}
-	case '<':
-	case '>':
-		lex->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 if (nextis('>')){
-				t->rtype = RDWR;
-				*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 '\'':
-		lex->lastdol = 0;
-		lex->lastword = 1;
-		lex->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)){
-		lex->lastdol = 0;
-		tok[0] = c;
-		tok[1]='\0';
-		return c;
-	}
-	glob = 0;
-	for(;;){
-		if(c=='*' || c=='[' || c=='?' || c==GLOB){
-			glob = 1;
-			w = addtok(w, GLOB);
-		}
-		w = addutf(w, c);
-		c = nextc();
-		if(lex->lastdol?!idchr(c):!wordchr(c)) break;
-		advance();
-	}
-
-	lex->lastword = 1;
-	lex->lastdol = 0;
-	if(w!=0)
-		*w='\0';
-	t = klook(tok);
-	if(t->type!=WORD)
-		lex->lastword = 0;
-	else
-		t->glob = glob;
-	t->quoted = 0;
-	yylval.tree = t;
-	return t->type;
-}
-
-void
-yyerror(char *m)
-{
-	pfln(err, lex->file, lex->line);
-	pstr(err, ": ");
-	if(lex->tok[0] && lex->tok[0]!='\n')
-		pfmt(err, "token %q: ", lex->tok);
-	pfmt(err, "%s\n", m);
-	flushio(err);
-
-	lex->lastword = 0;
-	lex->lastdol = 0;
-	while(lex->lastc!='\n' && lex->lastc!=EOF) advance();
-	nerror++;
-
-	setstatus(m);
-}
--- a/librc/pcmd.c
+++ /dev/null
@@ -1,170 +1,0 @@
-#include <rc.h>
-#include "io.h"
-#include "fns.h"
-#include "y.tab.h"
-
-#define	c0	t->child[0]
-#define	c1	t->child[1]
-#define	c2	t->child[2]
-
-static void
-pdeglob(io *f, char *s)
-{
-	while(*s){
-		if(*s==GLOB)
-			s++;
-		pchr(f, *s++);
-	}
-}
-
-static int ntab = 0;
-
-static char*
-tabs(void)
-{
-	return "\t\t\t\t\t\t\t\t"+8-(ntab%8);
-}
-
-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%t", c0, c1);
-	break;
-	case ANDAND:	pfmt(f, "%t && %t", c0, c1);
-	break;
-	case BANG:	pfmt(f, "! %t", c0);
-	break;
-	case BRACE:
-			ntab++;
-			pfmt(f, "{\n%s%t", tabs(), c0);
-			ntab--;
-			pfmt(f, "\n%s}", tabs());
-	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){
-			pfmt(f, "%t", c0);
-			if(c1){
-				if(c0->line==c1->line)
-					pstr(f, "; ");
-				else
-					pfmt(f, "\n%s", tabs());
-				pfmt(f, "%t", c1);
-			}
-		}
-		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:
-		pchr(f, ' ');
-		switch(t->rtype){
-		case HERE:
-			if(c1)
-				pfmt(f, "%t ", c1);
-			pchr(f, '<');
-		case READ:
-		case RDWR:
-			pchr(f, '<');
-			if(t->rtype==RDWR)
-				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(t->rtype == HERE)
-			pfmt(f, "\n%s%s\n", t->str, c0->str);
-		else 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;
-	}
-}
--- a/librc/pfnc.c
+++ /dev/null
@@ -1,78 +1,0 @@
-#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",
-	Xpush, "Xpush",
-	Xrdwr, "Xrdwr",
-	Xread, "Xread",
-	Xhere, "Xhere",
-	Xhereq, "Xhereq",
-	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",
-	Xpopredir, "Xpopredir",
-	Xrdcmds, "Xrdcmds",
-	Xbackq, "Xbackq",
-	Xpipefd, "Xpipefd",
-	Xsubshell, "Xsubshell",
-	Xfor, "Xfor",
-	Xglob, "Xglob",
-	Xsimple, "Xsimple",
-	Xqw, "Xqw",
-	Xsrcline, "Xsrcline",
-0};
-
-void
-pfun(io *f, void (*fn)(void))
-{
-	int i;
-	for(i = 0;fname[i].f;i++) if(fname[i].f==fn){
-		pstr(f, fname[i].name);
-		return;
-	}
-	pfmt(f, "%p", fn);
-}
-
-void
-pfnc(io *f, thread *t)
-{
-	list *a;
-
-	pfln(f, srcfile(t), t->line);
-	pfmt(f, " pid %d cycle %p %d ", getpid(), t->code, t->pc);
-	pfun(f, t->code[t->pc].f);
-	for(a = t->argv;a;a = a->next) pfmt(f, " (%v)", a->words);
-	pchr(f, '\n');
-	flushio(f);
-}
--- a/librc/rc.1
+++ /dev/null
@@ -1,1025 +1,0 @@
-.TH RC 1
-.SH NAME
-rc, cd, eval, exec, exit, flag, rfork, shift, wait, whatis, ., ~ \- command language
-.SH SYNOPSIS
-.B rc
-[
-.B -srdiIlxebpvV
-]
-[
-.B -c
-.I command
-]
-[
-.B -m
-.I initial
-]
-[
-.I file
-[
-.I arg ...
-]]
-.SH DESCRIPTION
-.I Rc
-is the Plan 9 shell.
-It executes command lines read from a terminal or a file or, with the
-.B -c
-flag, from
-.I rc's
-argument list.
-.SS Command Lines
-A command line is a sequence of commands, separated by ampersands or semicolons
-.RB ( &
-or
-.BR ; ),
-terminated by a newline.
-The commands are executed in sequence
-from left to right.
-.I Rc
-does not wait for a command followed by
-.B &
-to finish executing before starting
-the following command.
-Whenever a command followed by
-.B &
-is executed, its process id is assigned to the
-.I rc
-variable
-.BR $apid .
-Whenever a command
-.I not
-followed by
-.B &
-exits or is terminated, the
-.I rc
-variable
-.B $status
-gets the process's wait message (see
-.IR wait (2));
-it will be the null string if the command was successful.
-.PP
-A long command line may be continued on subsequent lines by typing
-a backslash
-.RB ( \e )
-followed by a newline.
-This sequence is treated as though it were a blank.
-Backslash is not otherwise a special character.
-.PP
-A number-sign
-.RB ( # )
-and any following characters up to (but not including) the next newline
-are ignored, except in quotation marks.
-.SS Simple Commands
-A simple command is a sequence of arguments interspersed with I/O redirections.
-If the first argument is the name of an
-.I rc
-function or of one of
-.I rc's
-built-in commands, it is executed by
-.IR rc .
-Otherwise if the name starts with a slash
-.RB ( / ),
-it must be the path name of the program to be executed.
-Names containing no initial slash are searched for in
-a list of directory names stored in
-.BR $path .
-The first executable file of the given name found
-in a directory in
-.B $path
-is the program to be executed.
-To be executable, the user must have execute permission (see
-.IR stat (2))
-and the file must be either an executable binary
-for the current machine's CPU type, or a shell script.
-Shell scripts begin with a line containing the full path name of a shell
-(usually
-.BR /bin/rc ),
-prefixed by
-.LR #! .
-.PP
-The first word of a simple command cannot be a keyword unless it is
-quoted or otherwise disguised.
-The keywords are
-.EX
-	for in while if not switch fn ~ ! @
-.EE
-.SS Arguments and Variables
-A number of constructions may be used where
-.I rc's
-syntax requires an argument to appear.
-In many cases a construction's
-value will be a list of arguments rather than a single string.
-.PP
-The simplest kind of argument is the unquoted word:
-a sequence of one or more characters none of which is a blank, tab,
-newline, or any of the following:
-.EX
-	# ; & | ^ $ = ` ' { } ( ) < >
-.EE
-An unquoted word that contains any of the characters
-.B *
-.B ?
-.B [
-is a pattern for matching against file names.
-The character
-.B *
-matches any sequence of characters,
-.B ?
-matches any single character, and
-.BI [ class ]
-matches any character in the
-.IR class .
-If the first character of
-.I class
-is
-.BR ~ ,
-the class is complemented.
-The
-.I class
-may also contain pairs of characters separated by
-.BR - ,
-standing for all characters lexically between the two.
-The character
-.B /
-must appear explicitly in a pattern, as must the
-first character of the path name components
-.B .
-and
-.BR .. .
-A pattern is replaced by a list of arguments, one for each path name matched,
-except that a pattern matching no names is not replaced by the empty list,
-but rather stands for itself.
-Pattern matching is done after all other
-operations.
-Thus,
-.EX
-	x=/tmp echo $x^/*.c
-.EE
-matches
-.BR /tmp/*.c ,
-rather than matching
-.B "/*.c
-and then prefixing
-.BR /tmp .
-.PP
-A quoted word is a sequence of characters surrounded by single quotes
-.RB ( ' ).
-A single quote is represented in a quoted word by a pair of quotes
-.RB ( '' ).
-.PP
-Each of the following is an argument.
-.PD 0
-.HP
-.BI ( arguments )
-.br
-The value of a sequence of arguments enclosed in parentheses is
-a list comprising the members of each element of the sequence.
-Argument lists have no recursive structure, although their syntax may
-suggest it.
-The following are entirely equivalent:
-.EX
-	echo hi there everybody
-	((echo) (hi there) everybody)
-.EE
-.HP
-.BI $ argument
-.HP
-.BI $ argument ( subscript )
-.br
-The
-.I argument
-after the
-.B $
-is the name of a variable whose value is substituted.
-Multiple levels
-of indirection are possible, but of questionable utility.
-Variable values
-are lists of strings.
-If
-.I argument
-is a number
-.IR n ,
-the value is the
-.IR n th
-element of
-.BR $* ,
-unless
-.B $*
-doesn't have
-.I n
-elements, in which case the value is empty.
-If
-.I argument
-is followed by a parenthesized list of subscripts, the
-value substituted is a list composed of the requested elements (origin 1).
-The parenthesis must follow the variable name with no spaces.
-Subscripts can also take the form
-.IB m - n
-or
-.IB m -
-to indicate a sequence of elements.
-Assignments to variables are described below.
-.HP
-.BI $# argument
-.br
-The value is the number of elements in the named variable.
-A variable
-never assigned a value has zero elements.
-.HP
-$"\c
-.I argument
-.br
-The value is a single string containing the components of the named variable
-separated by spaces.  A variable with zero elements yields the empty string.
-.HP
-.BI `{ command }
-.HP
-.BI ` "split " { command }
-.br
-.I rc
-executes the
-.I command
-and reads its standard output, splitting it into a list of arguments,
-using characters in
-.B $ifs
-as separators.
-If
-.B $ifs
-is not otherwise set, its value is
-.BR "'\ \et\en'" .
-In the second form of the command, split is used instead of
-.BR $ifs .
-.HP
-.BI <{ command }
-.HP
-.BI >{ command }
-.br
-The
-.I command
-is executed asynchronously with its standard output or standard input
-connected to a pipe.
-The value of the argument is the name of a file
-referring to the other end of the pipe.
-This allows the construction of
-non-linear pipelines.
-For example, the following runs two commands
-.B old
-and
-.B new
-and uses
-.B cmp
-to compare their outputs
-.EX
-	cmp <{old} <{new}
-.EE
-.HP
-.IB argument ^ argument
-.br
-The
-.B ^
-operator concatenates its two operands.
-If the two operands
-have the same number of components, they are concatenated pairwise.
-If not,
-then one operand must have one component, and the other must be non-empty,
-and concatenation is distributive.
-.PD
-.SS Free Carets
-In most circumstances,
-.I rc
-will insert the
-.B ^
-operator automatically between words that are not separated by white space.
-Whenever one of
-.B $
-.B '
-.B `
-follows a quoted or unquoted word or an unquoted word follows a quoted word
-with no intervening blanks or tabs,
-a
-.B ^
-is inserted between the two.
-If an unquoted word immediately follows a
-.BR $ 
-and contains a character other than an alphanumeric, underscore,
-or
-.BR * ,
-a
-.B ^
-is inserted before the first such character.
-Thus
-.IP
-.B cc -$flags $stem.c
-.LP
-is equivalent to
-.IP
-.B cc -^$flags $stem^.c
-.SS I/O Redirections
-The sequence
-.BI > file
-redirects the standard output file (file descriptor 1, normally the
-terminal) to the named
-.IR file ;
-.BI >> file
-appends standard output to the file.
-The standard input file (file descriptor 0, also normally the terminal)
-may be redirected from a file by the sequence
-.BI < file \f1,
-or from an inline `here document'
-by the sequence
-.BI << eof-marker\f1.
-The contents of a here document are lines of text taken from the command
-input stream up to a line containing nothing but the
-.IR eof-marker ,
-which may be either a quoted or unquoted word.
-If
-.I eof-marker
-is unquoted, variable names of the form
-.BI $ word
-have their values substituted from
-.I rc's
-environment.
-If
-.BI $ word
-is followed by a caret
-.RB ( ^ ),
-the caret is deleted.
-If
-.I eof-marker
-is quoted, no substitution occurs.
-The standard input file
-may also be redirected from a file by the sequence
-.BI <> file \f1,
-which opens
-.I file
-exactly once, for reading and writing.
-.PP
-Redirections may be applied to a file-descriptor other than standard input
-or output by qualifying the redirection operator
-with a number in square brackets.
-For example, the diagnostic output (file descriptor 2)
-may be redirected by writing
-.BR "cc junk.c >[2]junk" .
-.PP
-A file descriptor may be redirected to an already open descriptor by writing
-.BI >[ fd0 = fd1 ],
-.BI <>[ fd0 = fd1 ],
-or
-.BI <[ fd0 = fd1 ]\f1.
-.I Fd1
-is a previously opened file descriptor and
-.I fd0
-becomes a new copy (in the sense of 
-.IR dup (2))
-of it.
-A file descriptor may be closed by writing
-.BI >[ fd0 =]
-or
-.BI <[ fd0 =]\f1.
-.PP
-Redirections are executed from left to right.
-Therefore,
-.B cc junk.c >/dev/null >[2=1]
-and
-.B cc junk.c >[2=1] >/dev/null
-have different effects: the first puts standard output in
-.BR /dev/null
-and then puts diagnostic output in the same place, where the second
-directs diagnostic output to the terminal and sends standard output to
-.BR /dev/null .
-.PP
-.B newconn <>/net/tcp/clone >[1=0]
-opens
-.B /net/tcp/clone
-exactly once for reading and writing and puts it on standard input and output.
-.B lpd <>[3]/net/tcp/42/data
-opens
-.B /net/tcp/42/data
-exactly once for reading and writing and puts it on file descriptor 3.
-.SS Compound Commands
-A pair of commands separated by a pipe operator
-.RB ( | )
-is a command.
-The standard output of the left command is sent through a pipe
-to the standard input of the right command.
-The pipe operator may be decorated
-to use different file descriptors.
-.BI |[ fd ]
-connects the output end of the pipe to file descriptor
-.I fd
-rather than 1.
-.BI |[ fd0 = fd1 ]
-connects output to
-.I fd1
-of the left command and input to
-.I fd0
-of the right command.
-.PP
-A pair of commands separated by
-.B &&
-or
-.B ||
-is a command.
-In either case, the left command is executed and its exit status examined.
-If the operator is
-.B &&
-the right command is executed if the left command's status is null.
-.B ||
-causes the right command to be executed if the left command's status is non-null.
-.PP
-The exit status of a command may be inverted (non-null is changed to null, null
-is changed to non-null) by preceding it with a
-.BR ! .
-.PP
-The
-.B |
-operator has highest precedence, and is left-associative (i.e. binds tighter
-to the left than the right).
-.B !
-has intermediate precedence, and
-.B &&
-and
-.B ||
-have the lowest precedence.
-.PP
-The unary
-.B @
-operator, with precedence equal to
-.BR ! ,
-causes its operand to be executed in a subshell.
-.PP
-Each of the following is a command.
-.PD 0
-.HP
-.B if (
-.I list
-.B )
-.I command
-.br
-A
-.I list
-is a sequence of commands, separated by
-.BR & ,
-.BR ; ,
-or newline.
-It is executed and
-if its exit status is null, the
-.I command
-is executed.
-.HP
-.B if not
-.I command
-.br
-The immediately preceding command must have been
-.BI if( list )
-.IR command .
-If its condition was non-zero, the
-.I command
-is executed.
-.HP
-.BI for( name
-.B in
-.IB arguments )
-.I command
-.HP
-.BI for( name )
-.I command
-.br
-The
-.I command
-is executed once for each
-.IR argument 
-with that argument assigned to
-.IR name .
-If the argument list is omitted,
-.B $*
-is used.
-.HP
-.BI while( list )
-.I command
-.br
-The
-.I list
-is executed repeatedly until its exit status is non-null.
-Each time it returns null status, the
-.I command
-is executed.
-An empty
-.I list
-is taken to give null status.
-.HP
-.BI "switch(" argument "){" list }
-.br
-The
-.IR list
-is searched for simple commands beginning with the word
-.BR case .
-(The search is only at the `top level' of the
-.IR list .
-That is,
-.B cases
-in nested constructs are not found.)
-.I Argument
-is matched against each word following
-.B case
-using the pattern-matching algorithm described above, except that
-.B /
-and the first characters of
-.B .
-and
-.B ..
-need not be matched explicitly.
-When a match is found, commands in the list are executed up to the next
-following
-.B case
-command (at the top level) or the closing brace.
-.HP
-.BI { list }
-.br
-Braces serve to alter the grouping of commands implied by operator
-priorities.
-The
-.I body
-is a sequence of commands separated by
-.BR & ,
-.BR ; ,
-or newline.
-.HP
-.BI "fn " name { list }
-.HP
-.BI "fn " name
-.br
-The first form defines a function with the given
-.IR name .
-Subsequently, whenever a command whose first argument is
-.I name
-is encountered, the current value of
-the remainder of the command's argument list will be assigned to
-.BR $* ,
-after saving its current value, and
-.I rc
-will execute the
-.IR list .
-The second form removes
-.IR name 's
-function definition.
-.HP
-.BI "fn " note { list }
-.br
-.HP
-.BI "fn " note
-.br
-A function with a special name will be called when
-.I rc
-receives a corresponding note; see
-.IR notify (2).
-The valid note names (and corresponding notes) are
-.B sighup
-.RB ( hangup ),
-.B sigint
-.RB ( interrupt ),
-.BR sigalrm
-.RB ( alarm ),
-and
-.B sigfpe
-(floating point trap).
-By default
-.I rc
-exits on receiving any signal, except when run interactively,
-in which case interrupts and quits normally cause
-.I rc
-to stop whatever it's doing and start reading a new command.
-The second form causes
-.I rc
-to handle a signal in the default manner.
-.I Rc
-recognizes an artificial note,
-.BR sigexit ,
-which occurs when
-.I rc
-is about to finish executing.
-.HP
-.IB name = "argument command"
-.br
-Any command may be preceded by a sequence of assignments
-interspersed with redirections.
-The assignments remain in effect until the end of the command, unless
-the command is empty (i.e. the assignments stand alone), in which case
-they are effective until rescinded by later assignments.
-.PD
-.SS Built-in Commands
-These commands are executed internally by
-.IR rc ,
-usually because their execution changes or depends on
-.IR rc 's
-internal state.
-.PD 0
-.HP
-.BI . " [-biq] file ..."
-.br
-Execute commands from
-.IR file .
-.B $*
-is set for the duration to the remainder of the argument list following
-.IR file .
-.I File
-is searched for using
-.BR $path .
-The flags
-.B -b
-and
-.B -i
-can be set for the new commands
-(see description below).
-The
-.B -q
-flag suppresses errors,
-inhibiting the effect of
-.B -e
-and
-.B -v
-flags of the main interpreter.
-.HP
-.BI builtin " command ..."
-.br
-Execute
-.I command
-as usual except that any function named
-.I command
-is ignored in favor of the built-in meaning.
-.HP
-.BI "cd [" dir "]"
-.br
-Change the current directory to
-.IR dir .
-The default argument is
-.BR $home .
-.I dir
-is searched for in each of the directories mentioned in
-.BR $cdpath .
-.HP
-.BI "eval [" "arg ..." "]"
-.br
-The arguments are concatenated separated by spaces into a single string,
-read as input to
-.IR rc ,
-and executed.
-.HP
-.BI "exec [" "command ..." "]"
-.br
-This instance of
-.I rc
-replaces itself with the given (non-built-in)
-.IR command .
-.HP
-.BI "flag " f " [+-]"
-.br
-Either set
-.RB ( + ),
-clear
-.RB ( - ),
-or test (neither
-.B +
-nor
-.BR - )
-the flag
-.IR f ,
-where
-.I f
-is a single character, one of the command line flags (see Invocation, below).
-.HP
-.BI "exit [" status "]"
-.br
-Exit with the given exit status.
-If none is given, the current value of
-.B $status
-is used.
-.HP
-.BR "rfork " [ nNeEsfFm ]
-.br
-Become a new process group using
-.BI rfork( flags )
-where
-.I flags
-is composed of the bitwise OR of the
-.B rfork
-flags specified by the option letters
-(see
-.IR fork (2)).
-If no
-.I flags
-are given, they default to
-.BR ens .
-The
-.I flags
-and their meanings are:
-.B n
-is
-.BR RFNAMEG ;
-.B N
-is
-.BR RFCNAMEG ;
-.B e
-is
-.BR RFENVG ;
-.B E
-is
-.BR RFCENVG ;
-.B s
-is
-.BR RFNOTEG ;
-.B f
-is
-.BR RFFDG ;
-.B F
-is
-.BR RFCFDG ;
-and
-.B m
-is
-.BR RFNOMNT .
-.HP
-.BI "shift [" n "]"
-.br
-Delete the first
-.IR n
-(default 1)
-elements of
-.BR $* .
-.HP
-.BI "wait [" pid "]"
-.br
-Wait for the process with the given
-.I pid
-to exit.
-If no
-.I pid
-is given, all outstanding processes are waited for.
-.HP
-.BI whatis " name ..."
-.br
-Print the value of each
-.I name
-in a form suitable for input to
-.IR rc .
-The output is
-an assignment to any variable,
-the definition of any function,
-a call to
-.B builtin
-for any built-in command, or
-the completed pathname of any executable file.
-.HP
-.BI ~ " subject pattern ..."
-.br
-The
-.I subject
-is matched against each
-.I pattern
-in sequence.
-If it matches any pattern,
-.B $status
-is set to zero.
-Otherwise,
-.B $status
-is set to one.
-Patterns are the same as for file name matching, except that
-.B /
-and the first character of
-.B .
-and
-.B ..
-need not be matched explicitly.
-The
-.I patterns
-are not subjected to
-file name matching before the
-.B ~
-command is executed, so they need not be enclosed in quotation marks.
-.PD
-.SS Environment
-The
-.I environment
-is a list of strings made available to executing binaries by the
-.B env
-device
-(see
-.IR env (3)).
-.I Rc
-creates an environment entry for each variable whose value is non-empty,
-and for each function.
-The string for a variable entry has the variable's name followed by
-.B =
-and its value.
-If the value has more than one component, these
-are separated by nul
-.RB ( '\e000' )
-characters.
-The string for a function is just the
-.I rc
-input that defines the function.
-The name of a function in the environment is the function name
-preceded by
-.LR fn# .
-.PP
-When
-.I rc
-starts executing it reads variable and function definitions from its
-environment.
-.SS Special Variables
-The following variables are set or used by
-.IR rc .
-.PD 0
-.TP \w'\fL$promptXX'u
-.B $*
-Set to
-.IR rc 's
-argument list during initialization.
-Whenever a
-.B .
-command or a function is executed, the current value is saved and
-.B $*
-receives the new argument list.
-The saved value is restored on completion of the
-.B .
-or function.
-.TP
-.B $apid
-Whenever a process is started asynchronously with
-.BR & ,
-.B $apid
-is set to its process id.
-.TP
-.B $home
-The default directory for
-.BR cd .
-.TP
-.B $ifs
-The input field separators used in backquote substitutions.
-If
-.B $ifs
-is not otherwise set, its value is
-.BR "'\ \et\en'" .
-.TP
-.B $path
-The search path used to find commands and input files
-for the
-.B .
-command.
-If not set in the environment, it is initialized by
-.BR "path=(.\ /bin)" .
-Its use is discouraged; instead use
-.IR bind (1)
-to build a
-.B /bin
-containing what's needed.
-.TP
-.B $pid
-Set during initialization to
-.IR rc 's
-process id.
-.TP
-.B $prompt
-When
-.I rc
-is run interactively, the first component of
-.B $prompt
-is printed before reading each command.
-The second component is printed whenever a newline is typed and more lines
-are required to complete the command.
-If not set in the environment, it is initialized by
-.BR "prompt=('%\ '\ '\ ')" .
-.TP
-.B $status
-Set to the wait message of the last-executed program.
-(unless started with
-.BR &).
-.B !
-and
-.B ~
-also change
-.BR $status .
-Its value is used to control execution in
-.BR && ,
-.BR || ,
-.B if
-and
-.B while
-commands.
-When
-.I rc
-exits at end-of-file of its input or on executing an
-.B exit
-command with no argument,
-.B $status
-is its exit status.
-.PD
-.SS Invocation
-If
-.I rc
-is started with no arguments it reads commands from standard input.
-Otherwise its first non-flag argument is the name of a file from which
-to read commands (but see
-.B -c
-below).
-Subsequent arguments become the initial value of
-.BR $* .
-.I Rc
-accepts the following command-line flags.
-.PD 0
-.TP \w'\fL-c\ \fIstring\fLXX'u
-.BI -c " string"
-Commands are read from
-.IR string .
-.TP
-.B -s
-Print out exit status after any command where the status is non-null.
-.TP
-.B -e
-Exit if
-.B $status
-is non-null after executing a simple command.
-.TP
-.B -i
-If
-.B -i
-is present, or
-.I rc
-is given no arguments and its standard input is a terminal,
-it runs interactively.
-Commands are prompted for using
-.BR $prompt .
-.TP
-.B -I
-Makes sure
-.I rc
-is not run interactively.
-.TP
-.B -l
-If
-.B -l
-is given or the first character of argument zero is
-.BR - ,
-.I rc
-reads commands from
-.BR $home/lib/profile ,
-if it exists, before reading its normal input.
-.TP
-.B -m
-Read commands to initialize
-.I rc
-from
-.I initial
-instead of from
-.BR /rc/lib/rcmain .
-.TP
-.B -p
-A no-op.
-.TP
-.B -d
-A no-op.
-.TP
-.B -v
-Echo input on file descriptor 2 as it is read.
-.TP
-.B -x
-Print each simple command before executing it.
-.TP
-.B -r
-Print debugging information (internal form of commands
-as they are executed).
-.TP
-.B -b
-Compile the command file as a whole before executing.
-This allows syntax checking of the whole file.
-.PD
-.SH FILES
-.TF $home/lib/profile
-.TP
-.B $home/lib/profile
-the user's local rc start script
-.TF /rc/lib/rcmain
-.TP
-.B /rc/lib/rcmain
-System rc start script
-.TF /rc/lib/rcmain.local
-.TP
-.B /rc/lib/rcmain.local
-Site specific system rc start script
-.SH SOURCE
-.B /sys/src/cmd/rc
-.SH "SEE ALSO"
-Tom Duff,
-``Rc \- The Plan 9 Shell''.
-.SH BUGS
-There should be a way to match patterns against whole lists rather than
-just single strings.
-.PP
-Using
-.B ~
-to check the value of
-.B $status
-changes
-.BR $status .
-.PP
-Free carets don't get inserted next to keywords.
--- a/librc/rcmain.drawcpu
+++ /dev/null
@@ -1,38 +1,0 @@
-# rcmain: drawcpu version
-if(~ $#home 0) home=$HOME
-if(~ $#ifs 0) ifs=' 	
-'
-profile=$home/.rcrc
-switch($#prompt){
-case 0
-	prompt=('% ' '	')
-case 1
-	prompt=($prompt '	')
-}
-if(~ $rcname ?.out) prompt=('broken! ' '	')
-if(flag p) path=/bin
-if not {
-	finit
-	if(~ $#path 0) path=(. /bin /usr/bin /usr/local/bin)
-}
-fn sigexit
-if(! ~ $#cflag 0){
-	if(flag l) {
-		. -q $profile
-	}
-	status=''
-	eval $cflag
-}
-if not if(flag i){
-	if(flag l) {
-		. -q $profile
-	}
-	status=''
-	if(! ~ $#* 0) . $*
-	. -i /dev/fd/0
-}
-if not if(~ $#* 0) . /dev/fd/0
-if not{
-	status=''
-	. $*
-}
--- a/librc/simple.c
+++ /dev/null
@@ -1,538 +1,0 @@
-/*
- * Maybe `simple' is a misnomer.
- */
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-#include <errno.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){
-	int i=ifnot;
-	thread *p=runq;
-	code *c;
-loop:
-	c=&p->code[p->pc];
-	while(1){
-		if(c->f==Xpopredir || c->f==Xunlocal)
-			c++;
-		else if(c->f==Xsrcline)
-			c += 2;
-		else if(c->f==Xwastrue){
-			c++;
-			i=0;
-		}
-		else if(c->f==Xifnot){
-			if(i)
-				c += 2;
-			else
-				c = &p->code[c[1].i];
-		}
-		else if(c->f==Xreturn){
-			p = p->ret;
-			if(p==0)
-				return 1;
-			goto loop;
-		}else
-			break;
-	}
-	return c->f==Xexit;
-}
-
-void (*builtinfunc(char *name))(void)
-{
-	extern builtin Builtin[];
-	builtin *bp;
-
-	for(bp = Builtin;bp->name;bp++)
-		if(strcmp(name, bp->name)==0)
-			return bp->fnc;
-	return 0;
-}
-
-void
-Xsimple(void)
-{
-	void (*f)(void);
-	word *a;
-	var *v;
-	int pid;
-
-	a = runq->argv->words;
-	if(a==0){
-		Xerror1("empty argument list");
-		return;
-	}
-	if(flag['x'])
-		pfmt(err, "%v\n", a); /* wrong, should do redirs */
-	v = gvlook(a->word);
-	if(v->fn)
-		execfunc(v);
-	else{
-		if(strcmp(a->word, "builtin")==0){
-			a = a->next;
-			if(a==0){
-				Xerror1("builtin: empty argument list");
-				return;
-			}
-			popword();	/* "builtin" */
-		}
-		f = builtinfunc(a->word);
-		if(f){
-			(*f)();
-			return;
-		}
-		if(exitnext()){
-			/* fork and wait is redundant */
-			pushword("exec");
-			execexec();
-			/* does not return */
-		}
-		else{
-			if((pid = execforkexec()) < 0){
-				Xerror2("try again", strerror(errno));
-				return;
-			}
-			poplist();
-
-			/* interrupts don't get us out */
-			while(Waitfor(pid) < 0)
-				;
-		}
-	}
-}
-
-static void
-doredir(redir *rp)
-{
-	if(rp){
-		doredir(rp->next);
-		switch(rp->type){
-		case ROPEN:
-			if(rp->from!=rp->to){
-				dup2(rp->from, rp->to);
-				close(rp->from);
-			}
-			break;
-		case RDUP:
-			dup2(rp->from, rp->to);
-			break;
-		case RCLOSE:
-			close(rp->from);
-			break;
-		}
-	}
-}
-
-word*
-searchpath(char *w, char *v)
-{
-	static struct word nullpath = { "", 0 };
-	word *path;
-
-	if(w[0] && w[0] != '/' && w[0] != '#' &&
-	  (w[0] != '.' || (w[1] && w[1] != '/' && (w[1] != '.' || w[2] && w[2] != '/')))){
-		path = vlook(v)->val;
-		if(path)
-			return path;
-	}
-	return &nullpath;
-}
-
-char*
-rcmakepath(char *dir, char *file)
-{
-	char *path;
-	int m, n = strlen(dir);
-	if(n==0) return estrdup(file);
-	while (n > 0 && dir[n-1]=='/') n--;
-	while (file[0]=='/') file++;
-	m = strlen(file);
-	path = emalloc(n + m + 2);
-	if(n>0) memmove(path, dir, n);
-	path[n++]='/';
-	memmove(path+n, file, m+1);
-	return path;
-}
-
-static char**
-mkargv(word *a)
-{
-	char **argv = (char **)emalloc((count(a)+2)*sizeof(char *));
-	char **argp = argv+1;
-	for(;a;a = a->next) *argp++=a->word;
-	*argp = 0;
-	return argv;
-}
-
-void
-execexec(void)
-{
-	char **argv;
-	word *path;
-
-	popword();	/* "exec" */
-	if(runq->argv->words==0){
-		Xerror1("exec: empty argument list");
-		return;
-	}
-	argv = mkargv(runq->argv->words);
-	Updenv();
-	doredir(runq->redir);
-	for(path = searchpath(argv[1], "path"); path; path = path->next){
-		argv[0] = rcmakepath(path->word, argv[1]);
-		// TODO: execve + env from our fs
-		execv(argv[0], argv+1);
-	}
-	setstatus(strerror(errno));
-	pfln(err, srcfile(runq), runq->line);
-	pfmt(err, ": %s: %s\n", argv[1], getstatus());
-	Xexit();
-}
-
-void
-execfunc(var *func)
-{
-	popword();	/* name */
-	startfunc(func, Poplist(), runq->local, runq->redir);
-}
-
-void
-execcd(void)
-{
-	word *a = runq->argv->words;
-	word *cdpath;
-	char *dir;
-
-	setstatus("can't cd");
-	switch(count(a)){
-	default:
-		pfmt(err, "Usage: cd [directory]\n");
-		break;
-	case 2:
-		a = a->next;
-		for(cdpath = searchpath(a->word, "cdpath"); cdpath; cdpath = cdpath->next){
-			dir = rcmakepath(cdpath->word, a->word);
-			if(chdir(dir)>=0){
-				if(cdpath->word[0] != '\0' && strcmp(cdpath->word, ".") != 0)
-					pfmt(err, "%s\n", dir);
-				free(dir);
-				setstatus("");
-				break;
-			}
-			free(dir);
-		}
-		if(cdpath==0)
-			pfmt(err, "Can't cd %s: %s\n", a->word, strerror(errno));
-		break;
-	case 1:
-		a = vlook("home")->val;
-		if(a){
-			if(chdir(a->word)>=0)
-				setstatus("");
-			else
-				pfmt(err, "Can't cd %s: %s\n", a->word, strerror(errno));
-		}
-		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>0 && star->val;--n){
-		a = star->val->next;
-		free(Freeword(star->val));
-		star->val = a;
-		star->changed = 1;
-	}
-	setstatus("");
-	poplist();
-}
-
-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;
-}
-
-void
-execcmds(io *input, char *file, var *local, redir *redir)
-{
-	static union code rdcmds[5];
-
-	if(rdcmds[0].i==0){
-		rdcmds[0].i = 1;
-		rdcmds[1].s="*rdcmds*";
-		rdcmds[2].f = Xrdcmds;
-		rdcmds[3].f = Xreturn;
-		rdcmds[4].f = 0;
-	}
-
-	if(exitnext()) turfstack(local);
-
-	start(rdcmds, 2, local, redir);
-	runq->lex = newlexer(input, file);
-}
-
-void
-execeval(void)
-{
-	char *cmds;
-	int len;
-	io *f;
-
-	popword();	/* "eval" */
-
-	if(runq->argv->words==0){
-		Xerror1("Usage: eval cmd ...");
-		return;
-	}
-	Xqw();		/* make into single word */
-	cmds = Popword();
-	len = strlen(cmds);
-	cmds[len++] = '\n';
-	poplist();
-
-	f = openiostr();
-	pfln(f, srcfile(runq), runq->line);
-	pstr(f, " *eval*");
-
-	execcmds(openiocore(cmds, len), closeiostr(f), runq->local, runq->redir);
-}
-
-void
-execdot(void)
-{
-	int fd, bflag, iflag, qflag;
-	word *path, *argv;
-	char *file;
-
-	popword();	/* "." */
-
-	bflag = iflag = qflag = 0;
-	while(runq->argv->words && runq->argv->words->word[0]=='-'){
-		char *f = runq->argv->words->word+1;
-		if(*f == '-'){
-			popword();
-			break;
-		}
-		for(; *f; f++){
-			switch(*f){
-			case 'b':
-				bflag = 1;
-				continue;
-			case 'i':
-				iflag = 1;
-				continue;
-			case 'q':
-				qflag = 1;
-				continue;
-			}
-			goto Usage;
-		}
-		popword();
-	}
-
-	/* get input file */
-	if(runq->argv->words==0){
-Usage:
-		Xerror1("Usage: . [-biq] file [arg ...]");
-		return;
-	}
-	argv = Poplist();
-		
-	file = 0;
-	fd = -1;
-	for(path = searchpath(argv->word, "path"); path; path = path->next){
-		file = rcmakepath(path->word, argv->word);
-		fd = open(file, 0);
-		if(fd >= 0)
-			break;
-		free(file);
-	}
-	if(fd<0){
-		if(!qflag)
-			Xerror3(". can't open", argv->word, strerror(errno));
-		freewords(argv);
-		return;
-	}
-
-	execcmds(openiofd(fd), file, (var*)0, runq->redir);
-	pushredir(RCLOSE, fd, 0);
-	runq->lex->qflag = qflag;
-	runq->iflag = iflag;
-	if(iflag || !bflag && flag['b']==0){
-		runq->lex->peekc=EOF;
-		runq->lex->epilog="";
-	}
-
-	runq->local = newvar("*", runq->local);
-	runq->local->val = argv->next;
-	argv->next=0;
-	runq->local->changed = 1;
-
-	runq->local = newvar("0", runq->local);
-	runq->local->val = argv;
-	runq->local->changed = 1;
-}
-
-void
-execflag(void)
-{
-	char *letter, *val;
-	switch(count(runq->argv->words)){
-	case 2:
-		setstatus(flag[(unsigned char)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[(unsigned char)letter[0]] = flagset;
-				setstatus("");
-				break;
-			}
-			if(strcmp(val, "-")==0){
-				flag[(unsigned char)letter[0]] = 0;
-				setstatus("");
-				break;
-			}
-		}
-	default:
-		Xerror1("Usage: flag [letter] [+-]");
-		return;
-	}
-	poplist();
-}
-
-void
-execwhatis(void){	/* mildly wrong -- should fork before writing */
-	word *a, *b, *path;
-	var *v;
-	char *file;
-	io *out;
-	int found, sep;
-	a = runq->argv->words->next;
-	if(a==0){
-		Xerror1("Usage: whatis name ...");
-		return;
-	}
-	setstatus("");
-	out = openiofd(mapfd(1));
-	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=' ';
-				}
-				pstr(out, ")\n");
-			}
-			found = 1;
-		}
-		else
-			found = 0;
-		v = gvlook(a->word);
-		if(v->fn)
-			pfmt(out, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
-		else{
-			if(builtinfunc(a->word))
-				pfmt(out, "builtin %s\n", a->word);
-			else {
-				for(path = searchpath(a->word, "path"); path; path = path->next){
-					file = rcmakepath(path->word, a->word);
-					if(Executable(file)){
-						pfmt(out, "%s\n", file);
-						free(file);
-						break;
-					}
-					free(file);
-				}
-				if(!path && !found){
-					pfmt(err, "%s: not found\n", a->word);
-					setstatus("not found");
-				}
-			}
-		}
-		flushio(out);
-	}
-	poplist();
-	free(closeiostr(out));	/* don't close fd */
-}
-
-void
-execwait(void)
-{
-	switch(count(runq->argv->words)){
-	default:
-		Xerror1("Usage: wait [pid]");
-		return;
-	case 2:
-		Waitfor(atoi(runq->argv->words->next->word));
-		break;
-	case 1:
-		Waitfor(-1);
-		break;
-	}
-	poplist();
-}
--- a/librc/subr.c
+++ /dev/null
@@ -1,56 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-#include "io.h"
-#include "fns.h"
-
-void *
-emalloc(long n)
-{
-	void *p = malloc(n);
-	if(p==0)
-		panic("Can't malloc %d bytes", n);
-	return p;
-}
-
-void*
-erealloc(void *p, long n)
-{
-	p = realloc(p, n);
-	if(p==0 && n!=0)
-		panic("Can't realloc %d bytes\n", n);
-	return p;
-}
-
-void
-pfln(io *fd, char *file, int line)
-{
-	if(file && line)
-		pfmt(fd, "%s:%d", file, line);
-	else if(file)
-		pstr(fd, file);
-	else
-		pstr(fd, argv0);
-}
-
-static char *bp;
-
-static 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
-inttoascii(char *s, int n)
-{
-	bp = s;
-	iacvt(n);
-	*bp='\0';
-}
--- a/librc/syn.y
+++ /dev/null
@@ -1,92 +1,0 @@
-%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN
-%term WORD REDIR DUP PIPE SUB
-%term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */
-/* operator priorities -- lowest first */
-%left IF WHILE FOR SWITCH ')' NOT
-%left ANDAND OROR
-%left BANG SUBSHELL
-%left PIPE
-%left '^'
-%right '$' COUNT '"'
-%left SUB
-%{
-#include <rc.h>
-#include "fns.h"
-%}
-%union{
-	struct tree *tree;
-};
-%type<tree> line paren brace body cmdsa cmdsan assign epilog redir
-%type<tree> cmd simple first word comword keyword words
-%type<tree> NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN
-%type<tree> WORD REDIR DUP PIPE
-%%
-rc:				{ return 1;}
-|	line '\n'		{readhere(lex->input); return !compile($1);}
-line:	cmd
-|	cmdsa line		{$$=tree2(';', $1, $2);}
-body:	cmd
-|	cmdsan body		{$$=tree2(';', $1, $2);}
-cmdsa:	cmd ';'
-|	cmd '&'			{$$=tree1('&', $1);}
-cmdsan:	cmdsa
-|	cmd '\n'		{readhere(lex->input);}
-brace:	'{' body '}'		{$$=tree1(BRACE, $2);}
-paren:	'(' body ')'		{$$=tree1(PCMD, $2);}
-assign:	first '=' word		{$$=tree2('=', $1, $3);}
-epilog:				{$$=0;}
-|	redir epilog		{$$=mung2($1, $1->child[0], $2);}
-redir:	REDIR word		{$$=mung1($1, $2); if($$->rtype==HERE) heredoc($$);}
-|	DUP
-cmd:				{$$=0;}
-|	brace epilog		{$$=epimung($1, $2);}
-|	IF paren {skipnl();} cmd
-				{$$=mung2($1, $2, $4);}
-|	IF NOT {skipnl();} cmd	{$$=mung1($2, $4);}
-|	FOR '(' word IN words ')' {skipnl();} cmd
-	/*
-	 * if ``words'' is nil, we need a tree element to distinguish between 
-	 * for(i in ) and for(i), the former being a loop over the empty set
-	 * and the latter being the implicit argument loop.  so if $5 is nil
-	 * (the empty set), we represent it as "()".  don't parenthesize non-nil
-	 * functions, to avoid growing parentheses every time we reread the
-	 * definition.
-	 */
-				{$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);}
-|	FOR '(' word ')' {skipnl();} cmd
-				{$$=mung3($1, $3, (tree*)0, $6);}
-|	WHILE paren {skipnl();} cmd
-				{$$=mung2($1, $2, $4);}
-|	SWITCH word {skipnl();} brace
-				{$$=tree2(SWITCH, $2, $4);}
-|	simple			{$$=simplemung($1);}
-|	TWIDDLE word words	{$$=mung2($1, $2, $3);}
-|	cmd ANDAND cmd		{$$=tree2(ANDAND, $1, $3);}
-|	cmd OROR cmd		{$$=tree2(OROR, $1, $3);}
-|	cmd PIPE cmd		{$$=mung2($2, $1, $3);}
-|	redir cmd  %prec BANG	{$$=mung2($1, $1->child[0], $2);}
-|	assign cmd %prec BANG	{$$=mung3($1, $1->child[0], $1->child[1], $2);}
-|	BANG cmd		{$$=mung1($1, $2);}
-|	SUBSHELL cmd		{$$=mung1($1, $2);}
-|	FN words brace		{$$=tree2(FN, $2, $3);}
-|	FN words		{$$=tree1(FN, $2);}
-simple:	first
-|	simple word		{$$=globprop(tree2(ARGLIST, $1, $2));}
-|	simple redir		{$$=globprop(tree2(ARGLIST, $1, $2));}
-first:	comword	
-|	first '^' word		{$$=globprop(tree2('^', $1, $3));}
-word:	keyword			{lex->lastword=1; $1->type=WORD;}
-|	comword
-|	word '^' word		{$$=globprop(tree2('^', $1, $3));}
-comword: '$' word		{$$=tree1('$', $2);}
-|	'$' word SUB words ')'	{$$=tree2(SUB, $2, $4);}
-|	'"' word		{$$=tree1('"', $2);}
-|	COUNT word		{$$=tree1(COUNT, $2);}
-|	WORD
-|	'`' brace		{$$=tree2('`', (tree*)0, $2);}
-|	'`' word brace		{$$=tree2('`', $2, $3);}
-|	'(' words ')'		{$$=globprop(tree1(PAREN, $2));}
-|	REDIR brace		{$$=mung1($1, $2); $$->type=PIPEFD;}
-keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
-words:				{$$=(tree*)0;}
-|	words word		{$$=tree2(WORDS, $1, $2);}
--- a/librc/trap.c
+++ /dev/null
@@ -1,35 +1,0 @@
-#include <rc.h>
-#include "exec.h"
-#include "fns.h"
-#include "io.h"
-#include "y.tab.h"
-
-int ntrap;
-int trap[NSIG];
-
-void
-dotrap(void)
-{
-	int i;
-	var *trapreq;
-	word *starval;
-	starval = vlook("*")->val;
-	while(ntrap) for(i = 0;i<NSIG;i++) while(trap[i]){
-		--trap[i];
-		--ntrap;
-		if(getpid()!=mypid) _exit(0);
-		trapreq = vlook(Signame[i]);
-		if(trapreq->fn)
-			startfunc(trapreq, copywords(starval, (word*)0), (var*)0, (redir*)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(0);
-	}
-}
--- a/librc/tree.c
+++ /dev/null
@@ -1,192 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-#include "io.h"
-#include "fns.h"
-#include "y.tab.h"
-/*
- * create and clear a new tree node, and add it
- * to the node list.
- */
-static tree *treefree, *treenodes;
-
-tree*
-newtree(void)
-{
-	tree *t;
-
-	t = treefree;
-	if(t==0)
-		t = new(tree);
-	else
-		treefree = t->next;
-	t->quoted = 0;
-	t->glob = 0;
-	t->iskw = 0;
-	t->str = 0;
-	t->child[0] = t->child[1] = t->child[2] = 0;
-	t->line = lex->line;
-	t->next = treenodes;
-	treenodes = t;
-	return t;
-}
-
-void
-freenodes(void)
-{
-	tree *t;
-
-	t = treenodes;
-	while(t){
-		if(t->str){
-			free(t->str);
-			t->str = 0;
-		}
-		t->child[0] = t->child[1] = t->child[2] = 0;
-		if(t->next==0){
-			t->next = treefree;
-			treefree = treenodes;
-			break;
-		}
-		t = t->next;
-	}
-	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;
-
-	if(c0)
-		t->line = c0->line;
-	else if(c1)
-		t->line = c1->line;
-	else if(c2)
-		t->line = c2->line;
-	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;
-
-	t = tree1(SIMPLE, t);
-	t->str = fnstr(t);
-	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;
-}
-
-char*
-fnstr(tree *t)
-{
-	io *f = openiostr();
-	pfmt(f, "%t", t);
-	return closeiostr(f);
-}
-
-tree*
-globprop(tree *t)
-{
-	tree *c0 = t->child[0];
-	tree *c1 = t->child[1];
-	if(c1==0){
-		while(c0 && c0->type==WORDS){
-			c1 = c0->child[1];
-			if(c1 && c1->glob){
-				c1->glob=2;
-				t->glob=1;
-			}
-			c0 = c0->child[0];
-		}
-	} else {
-		if(c0->glob){
-			c0->glob=2;
-			t->glob=1;
-		}
-		if(c1->glob){
-			c1->glob=2;
-			t->glob=1;
-		}
-	}
-	return t;
-}
-
-tree*
-token(char *str, int type)
-{
-	tree *t = newtree();
-	t->str = estrdup(str);
-	t->type = type;
-	return t;
-}
--- a/librc/var.c
+++ /dev/null
@@ -1,110 +1,0 @@
-#include <rc.h>
-#include "exec.h"
-#include "fns.h"
-#include "y.tab.h"
-
-var *gvar[NVAR];
-
-int
-hash(char *s, int n)
-{
-	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)
-{
-	int h = hash(name, NKW);
-	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*
-newvar(char *name, var *next)
-{
-	int n = strlen(name)+1;
-	var *v = emalloc(sizeof(var)+n);
-	memmove(v->name, name, n);
-	v->next = next;
-	v->val = 0;
-	v->fn = 0;
-	v->changed = 0;
-	v->fnchanged = 0;
-	return v;
-}
-
-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(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)
-{
-	var *v = vlook(name);
-	freewords(v->val);
-	v->val = val;
-	v->changed = 1;
-}
-
-void
-freevar(var *v)
-{
-	freewords(v->val);
-	free(v);
-}
--- a/librc/y.tab.c
+++ /dev/null
@@ -1,1989 +1,0 @@
-/* A Bison parser, made by GNU Bison 2.3.  */
-
-/* Skeleton implementation for Bison's Yacc-like parsers in C
-
-   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
-   Free Software Foundation, Inc.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.  */
-
-/* As a special exception, you may create a larger work that contains
-   part or all of the Bison parser skeleton and distribute that work
-   under terms of your choice, so long as that work isn't itself a
-   parser generator using the skeleton or a modified version thereof
-   as a parser skeleton.  Alternatively, if you modify or redistribute
-   the parser skeleton itself, you may (at your option) remove this
-   special exception, which will cause the skeleton and the resulting
-   Bison output files to be licensed under the GNU General Public
-   License without this special exception.
-
-   This special exception was added by the Free Software Foundation in
-   version 2.2 of Bison.  */
-
-/* C LALR(1) parser skeleton written by Richard Stallman, by
-   simplifying the original so-called "semantic" parser.  */
-
-/* All symbols defined below should begin with yy or YY, to avoid
-   infringing on user name space.  This should be done even for local
-   variables, as they might otherwise be expanded by user macros.
-   There are some unavoidable exceptions within include files to
-   define necessary library symbols; they are noted "INFRINGES ON
-   USER NAME SPACE" below.  */
-
-/* Identify Bison output.  */
-#define YYBISON 1
-
-/* Bison version.  */
-#define YYBISON_VERSION "2.3"
-
-/* Skeleton name.  */
-#define YYSKELETON_NAME "yacc.c"
-
-/* Pure parsers.  */
-#define YYPURE 0
-
-/* Using locations.  */
-#define YYLSP_NEEDED 0
-
-
-
-/* Tokens.  */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
-   /* Put the tokens into the symbol table, so that GDB and other debuggers
-      know about them.  */
-   enum yytokentype {
-     FOR = 258,
-     IN = 259,
-     WHILE = 260,
-     IF = 261,
-     NOT = 262,
-     TWIDDLE = 263,
-     BANG = 264,
-     SUBSHELL = 265,
-     SWITCH = 266,
-     FN = 267,
-     WORD = 268,
-     REDIR = 269,
-     DUP = 270,
-     PIPE = 271,
-     SUB = 272,
-     SIMPLE = 273,
-     ARGLIST = 274,
-     WORDS = 275,
-     BRACE = 276,
-     PAREN = 277,
-     PCMD = 278,
-     PIPEFD = 279,
-     OROR = 280,
-     ANDAND = 281,
-     COUNT = 282
-   };
-#endif
-/* Tokens.  */
-#define FOR 258
-#define IN 259
-#define WHILE 260
-#define IF 261
-#define NOT 262
-#define TWIDDLE 263
-#define BANG 264
-#define SUBSHELL 265
-#define SWITCH 266
-#define FN 267
-#define WORD 268
-#define REDIR 269
-#define DUP 270
-#define PIPE 271
-#define SUB 272
-#define SIMPLE 273
-#define ARGLIST 274
-#define WORDS 275
-#define BRACE 276
-#define PAREN 277
-#define PCMD 278
-#define PIPEFD 279
-#define OROR 280
-#define ANDAND 281
-#define COUNT 282
-
-
-
-
-/* Copy the first part of user declarations.  */
-#line 12 "syn.y"
-
-#include <rc.h>
-#include "fns.h"
-
-
-/* Enabling traces.  */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-
-/* Enabling verbose error messages.  */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
-
-/* Enabling the token table.  */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
-#endif
-
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-typedef union YYSTYPE
-#line 16 "syn.y"
-{
-	struct tree *tree;
-}
-/* Line 193 of yacc.c.  */
-#line 159 "y.tab.c"
-	YYSTYPE;
-# define yystype YYSTYPE /* obsolescent; will be withdrawn */
-# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
-#endif
-
-
-
-/* Copy the second part of user declarations.  */
-
-
-/* Line 216 of yacc.c.  */
-#line 172 "y.tab.c"
-
-#ifdef short
-# undef short
-#endif
-
-#ifdef YYTYPE_UINT8
-typedef YYTYPE_UINT8 yytype_uint8;
-#else
-typedef unsigned char yytype_uint8;
-#endif
-
-#ifdef YYTYPE_INT8
-typedef YYTYPE_INT8 yytype_int8;
-#elif (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-typedef signed char yytype_int8;
-#else
-typedef short int yytype_int8;
-#endif
-
-#ifdef YYTYPE_UINT16
-typedef YYTYPE_UINT16 yytype_uint16;
-#else
-typedef unsigned short int yytype_uint16;
-#endif
-
-#ifdef YYTYPE_INT16
-typedef YYTYPE_INT16 yytype_int16;
-#else
-typedef short int yytype_int16;
-#endif
-
-#ifndef YYSIZE_T
-# ifdef __SIZE_TYPE__
-#  define YYSIZE_T __SIZE_TYPE__
-# elif defined size_t
-#  define YYSIZE_T size_t
-# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
-#  define YYSIZE_T size_t
-# else
-#  define YYSIZE_T unsigned int
-# endif
-#endif
-
-#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
-
-#ifndef YY_
-# if defined YYENABLE_NLS && YYENABLE_NLS
-#  if ENABLE_NLS
-#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
-#   define YY_(msgid) dgettext ("bison-runtime", msgid)
-#  endif
-# endif
-# ifndef YY_
-#  define YY_(msgid) msgid
-# endif
-#endif
-
-/* Suppress unused-variable warnings by "using" E.  */
-#if ! defined lint || defined __GNUC__
-# define YYUSE(e) ((void) (e))
-#else
-# define YYUSE(e) /* empty */
-#endif
-
-/* Identity function, used to suppress warnings about constant conditions.  */
-#ifndef lint
-# define YYID(n) (n)
-#else
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-static int
-YYID (int i)
-#else
-static int
-YYID (i)
-    int i;
-#endif
-{
-  return i;
-}
-#endif
-
-#if ! defined yyoverflow || YYERROR_VERBOSE
-
-/* The parser invokes alloca or malloc; define the necessary symbols.  */
-
-# ifdef YYSTACK_USE_ALLOCA
-#  if YYSTACK_USE_ALLOCA
-#   ifdef __GNUC__
-#    define YYSTACK_ALLOC __builtin_alloca
-#   elif defined __BUILTIN_VA_ARG_INCR
-#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
-#   elif defined _AIX
-#    define YYSTACK_ALLOC __alloca
-#   elif defined _MSC_VER
-#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
-#    define alloca _alloca
-#   else
-#    define YYSTACK_ALLOC alloca
-#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-#     ifndef _STDLIB_H
-#      define _STDLIB_H 1
-#     endif
-#    endif
-#   endif
-#  endif
-# endif
-
-# ifdef YYSTACK_ALLOC
-   /* Pacify GCC's `empty if-body' warning.  */
-#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
-#  ifndef YYSTACK_ALLOC_MAXIMUM
-    /* The OS might guarantee only one guard page at the bottom of the stack,
-       and a page size can be as small as 4096 bytes.  So we cannot safely
-       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
-       to allow for a few compiler-allocated temporary stack slots.  */
-#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
-#  endif
-# else
-#  define YYSTACK_ALLOC YYMALLOC
-#  define YYSTACK_FREE YYFREE
-#  ifndef YYSTACK_ALLOC_MAXIMUM
-#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
-#  endif
-#  if (defined __cplusplus && ! defined _STDLIB_H \
-       && ! ((defined YYMALLOC || defined malloc) \
-	     && (defined YYFREE || defined free)))
-#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-#   ifndef _STDLIB_H
-#    define _STDLIB_H 1
-#   endif
-#  endif
-#  ifndef YYMALLOC
-#   define YYMALLOC malloc
-#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
-#   endif
-#  endif
-#  ifndef YYFREE
-#   define YYFREE free
-#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-void free (void *); /* INFRINGES ON USER NAME SPACE */
-#   endif
-#  endif
-# endif
-#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
-
-
-#if (! defined yyoverflow \
-     && (! defined __cplusplus \
-	 || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
-
-/* A type that is properly aligned for any stack member.  */
-union yyalloc
-{
-  yytype_int16 yyss;
-  YYSTYPE yyvs;
-  };
-
-/* The size of the maximum gap between one aligned stack and the next.  */
-# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
-
-/* The size of an array large to enough to hold all stacks, each with
-   N elements.  */
-# define YYSTACK_BYTES(N) \
-     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
-      + YYSTACK_GAP_MAXIMUM)
-
-/* Copy COUNT objects from FROM to TO.  The source and destination do
-   not overlap.  */
-# ifndef YYCOPY
-#  if defined __GNUC__ && 1 < __GNUC__
-#   define YYCOPY(To, From, Count) \
-      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
-#  else
-#   define YYCOPY(To, From, Count)		\
-      do					\
-	{					\
-	  YYSIZE_T yyi;				\
-	  for (yyi = 0; yyi < (Count); yyi++)	\
-	    (To)[yyi] = (From)[yyi];		\
-	}					\
-      while (YYID (0))
-#  endif
-# endif
-
-/* Relocate STACK from its old location to the new one.  The
-   local variables YYSIZE and YYSTACKSIZE give the old and new number of
-   elements in the stack, and YYPTR gives the new location of the
-   stack.  Advance YYPTR to a properly aligned location for the next
-   stack.  */
-# define YYSTACK_RELOCATE(Stack)					\
-    do									\
-      {									\
-	YYSIZE_T yynewbytes;						\
-	YYCOPY (&yyptr->Stack, Stack, yysize);				\
-	Stack = &yyptr->Stack;						\
-	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
-	yyptr += yynewbytes / sizeof (*yyptr);				\
-      }									\
-    while (YYID (0))
-
-#endif
-
-/* YYFINAL -- State number of the termination state.  */
-#define YYFINAL  63
-/* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   347
-
-/* YYNTOKENS -- Number of terminals.  */
-#define YYNTOKENS  40
-/* YYNNTS -- Number of nonterminals.  */
-#define YYNNTS  24
-/* YYNRULES -- Number of rules.  */
-#define YYNRULES  72
-/* YYNRULES -- Number of states.  */
-#define YYNSTATES  118
-
-/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
-#define YYUNDEFTOK  2
-#define YYMAXUTOK   282
-
-#define YYTRANSLATE(YYX)						\
-  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
-
-/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
-static const yytype_uint8 yytranslate[] =
-{
-       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-      32,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,    30,     2,    29,     2,    34,     2,
-      37,    25,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,    33,
-       2,    38,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,    28,     2,    39,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,    35,     2,    36,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
-       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
-      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
-      26,    27,    31
-};
-
-#if YYDEBUG
-/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
-   YYRHS.  */
-static const yytype_uint8 yyprhs[] =
-{
-       0,     0,     3,     4,     7,     9,    12,    14,    17,    20,
-      23,    25,    28,    32,    36,    40,    41,    44,    47,    49,
-      50,    53,    54,    59,    60,    65,    66,    75,    76,    83,
-      84,    89,    90,    95,    97,   101,   105,   109,   113,   116,
-     119,   122,   125,   129,   132,   134,   137,   140,   142,   146,
-     148,   150,   154,   157,   163,   166,   169,   171,   174,   178,
-     182,   185,   187,   189,   191,   193,   195,   197,   199,   201,
-     203,   205,   206
-};
-
-/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
-static const yytype_int8 yyrhs[] =
-{
-      41,     0,    -1,    -1,    42,    32,    -1,    51,    -1,    44,
-      42,    -1,    51,    -1,    45,    43,    -1,    51,    33,    -1,
-      51,    34,    -1,    44,    -1,    51,    32,    -1,    35,    43,
-      36,    -1,    37,    43,    25,    -1,    59,    38,    60,    -1,
-      -1,    50,    49,    -1,    14,    60,    -1,    15,    -1,    -1,
-      46,    49,    -1,    -1,     6,    47,    52,    51,    -1,    -1,
-       6,     7,    53,    51,    -1,    -1,     3,    37,    60,     4,
-      63,    25,    54,    51,    -1,    -1,     3,    37,    60,    25,
-      55,    51,    -1,    -1,     5,    47,    56,    51,    -1,    -1,
-      11,    60,    57,    46,    -1,    58,    -1,     8,    60,    63,
-      -1,    51,    27,    51,    -1,    51,    26,    51,    -1,    51,
-      16,    51,    -1,    50,    51,    -1,    48,    51,    -1,     9,
-      51,    -1,    10,    51,    -1,    12,    63,    46,    -1,    12,
-      63,    -1,    59,    -1,    58,    60,    -1,    58,    50,    -1,
-      61,    -1,    59,    28,    60,    -1,    62,    -1,    61,    -1,
-      60,    28,    60,    -1,    29,    60,    -1,    29,    60,    17,
-      63,    25,    -1,    30,    60,    -1,    31,    60,    -1,    13,
-      -1,    39,    46,    -1,    39,    60,    46,    -1,    37,    63,
-      25,    -1,    14,    46,    -1,     3,    -1,     4,    -1,     5,
-      -1,     6,    -1,     7,    -1,     8,    -1,     9,    -1,    10,
-      -1,    11,    -1,    12,    -1,    -1,    63,    60,    -1
-};
-
-/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
-static const yytype_uint8 yyrline[] =
-{
-       0,    24,    24,    25,    26,    27,    28,    29,    30,    31,
-      32,    33,    34,    35,    36,    37,    38,    39,    40,    41,
-      42,    43,    43,    45,    45,    46,    46,    56,    56,    58,
-      58,    60,    60,    62,    63,    64,    65,    66,    67,    68,
-      69,    70,    71,    72,    73,    74,    75,    76,    77,    78,
-      79,    80,    81,    82,    83,    84,    85,    86,    87,    88,
-      89,    90,    90,    90,    90,    90,    90,    90,    90,    90,
-      90,    91,    92
-};
-#endif
-
-#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
-/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
-   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
-static const char *const yytname[] =
-{
-  "$end", "error", "$undefined", "FOR", "IN", "WHILE", "IF", "NOT",
-  "TWIDDLE", "BANG", "SUBSHELL", "SWITCH", "FN", "WORD", "REDIR", "DUP",
-  "PIPE", "SUB", "SIMPLE", "ARGLIST", "WORDS", "BRACE", "PAREN", "PCMD",
-  "PIPEFD", "')'", "OROR", "ANDAND", "'^'", "'$'", "'\"'", "COUNT",
-  "'\\n'", "';'", "'&'", "'{'", "'}'", "'('", "'='", "'`'", "$accept",
-  "rc", "line", "body", "cmdsa", "cmdsan", "brace", "paren", "assign",
-  "epilog", "redir", "cmd", "@1", "@2", "@3", "@4", "@5", "@6", "simple",
-  "first", "word", "comword", "keyword", "words", 0
-};
-#endif
-
-# ifdef YYPRINT
-/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
-   token YYLEX-NUM.  */
-static const yytype_uint16 yytoknum[] =
-{
-       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
-     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
-     275,   276,   277,   278,   279,    41,   280,   281,    94,    36,
-      34,   282,    10,    59,    38,   123,   125,    40,    61,    96
-};
-# endif
-
-/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
-static const yytype_uint8 yyr1[] =
-{
-       0,    40,    41,    41,    42,    42,    43,    43,    44,    44,
-      45,    45,    46,    47,    48,    49,    49,    50,    50,    51,
-      51,    52,    51,    53,    51,    54,    51,    55,    51,    56,
-      51,    57,    51,    51,    51,    51,    51,    51,    51,    51,
-      51,    51,    51,    51,    58,    58,    58,    59,    59,    60,
-      60,    60,    61,    61,    61,    61,    61,    61,    61,    61,
-      61,    62,    62,    62,    62,    62,    62,    62,    62,    62,
-      62,    63,    63
-};
-
-/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
-static const yytype_uint8 yyr2[] =
-{
-       0,     2,     0,     2,     1,     2,     1,     2,     2,     2,
-       1,     2,     3,     3,     3,     0,     2,     2,     1,     0,
-       2,     0,     4,     0,     4,     0,     8,     0,     6,     0,
-       4,     0,     4,     1,     3,     3,     3,     3,     2,     2,
-       2,     2,     3,     2,     1,     2,     2,     1,     3,     1,
-       1,     3,     2,     5,     2,     2,     1,     2,     3,     3,
-       2,     1,     1,     1,     1,     1,     1,     1,     1,     1,
-       1,     0,     2
-};
-
-/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
-   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
-   means the default is an error.  */
-static const yytype_uint8 yydefact[] =
-{
-      19,     0,     0,     0,     0,    19,    19,     0,    71,    56,
-       0,    18,     0,     0,     0,    19,    71,     0,     0,     0,
-      19,    15,    19,    19,     4,    33,    44,    47,     0,    19,
-      29,    23,    21,    61,    62,    63,    64,    65,    66,    67,
-      68,    69,    70,     0,    71,    50,    49,    40,    41,    31,
-      43,    60,    17,    52,    54,    55,     0,    10,    19,     6,
-       0,    57,     0,     1,     3,     5,     0,    20,    15,    39,
-      38,    19,    19,    19,     8,     9,    46,    45,     0,     0,
-       0,     0,    19,    19,    19,     0,    34,     0,    42,    72,
-      71,    12,     7,    11,    59,    58,    16,    37,    36,    35,
-      48,    14,    71,    27,    13,    30,    24,    22,    51,    32,
-       0,     0,    19,    53,    25,    28,    19,    26
-};
-
-/* YYDEFGOTO[NTERM-NUM].  */
-static const yytype_int8 yydefgoto[] =
-{
-      -1,    18,    19,    56,    57,    58,    21,    30,    22,    67,
-      23,    59,    84,    83,   116,   112,    82,    87,    25,    26,
-      89,    45,    46,    50
-};
-
-/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
-   STATE-NUM.  */
-#define YYPACT_NINF -28
-static const yytype_int16 yypact[] =
-{
-     122,   -27,   -13,    -3,   296,   308,   308,   296,   -28,   -28,
-     135,   -28,   296,   296,   296,   308,   -28,   135,    25,     5,
-     308,     4,   308,   308,    84,   172,   -26,   -28,   296,   308,
-     -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,
-     -28,   -28,   -28,     9,    15,   -28,   -28,    20,    20,    15,
-     135,   -28,    15,    30,   -28,   -28,    13,   -28,   308,    36,
-     185,   -28,   -19,   -28,   -28,   -28,   296,   -28,     4,    20,
-      20,   308,   308,   308,   -28,   -28,   -28,    15,   296,   296,
-      23,    32,   308,   308,   308,   296,   296,     9,   -28,    15,
-     -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,    20,    20,
-     -28,    15,   -28,   -28,   -28,    38,    38,    38,   -28,   -28,
-     222,   259,   308,   -28,   -28,    38,   308,    38
-};
-
-/* YYPGOTO[NTERM-NUM].  */
-static const yytype_int8 yypgoto[] =
-{
-     -28,   -28,    35,   -12,     1,   -28,    16,    57,   -28,    -7,
-     -18,     8,   -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,
-      28,     0,   -28,    -5
-};
-
-/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
-   positive, shift that token.  If negative, reduce the rule which
-   number is the opposite.  If zero, do what YYDEFACT says.
-   If YYTABLE_NINF, syntax error.  */
-#define YYTABLE_NINF -3
-static const yytype_int8 yytable[] =
-{
-      27,    20,    78,    68,    31,    27,    27,    76,    24,    85,
-      28,    60,    79,    47,    48,    27,    15,    81,    66,    11,
-      27,    20,    27,    27,    29,    63,    51,   102,    24,    27,
-      69,    70,    44,    61,    29,    49,    71,    64,    52,    86,
-      53,    54,    55,    85,    15,    62,    92,    90,   103,    91,
-      68,    85,    71,    77,    71,    65,    80,   104,    27,    51,
-      32,    96,    72,    73,    72,    73,    88,     0,    93,    74,
-      75,    27,    27,    27,     0,     0,     0,     0,    95,    97,
-      98,    99,    27,    27,    27,   110,     0,     0,     0,     0,
-     105,   106,   107,     0,    52,     0,     0,   111,     0,     0,
-      71,     0,     0,   109,     0,     0,   100,   101,     0,     0,
-      72,    73,    27,   108,     0,     0,    27,    74,    75,     0,
-     115,     0,    -2,     0,   117,     1,     0,     2,     3,     0,
-       4,     5,     6,     7,     8,     9,    10,    11,    33,    34,
-      35,    36,    37,    38,    39,    40,    41,    42,     9,    43,
-       0,    12,    13,    14,     0,     0,     0,    15,     0,    16,
-       0,    17,     0,     0,    12,    13,    14,     0,     0,     0,
-      15,     0,    16,     0,    17,    33,    34,    35,    36,    37,
-      38,    39,    40,    41,    42,     9,    10,    11,    33,    34,
-      35,    36,    37,    38,    39,    40,    41,    42,     9,    43,
-       0,    12,    13,    14,     0,     0,     0,     0,     0,    16,
-      94,    17,     0,     0,    12,    13,    14,     0,     0,     0,
-       0,     0,    16,     0,    17,    33,    34,    35,    36,    37,
-      38,    39,    40,    41,    42,     9,    43,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,   113,     0,     0,
-       0,    12,    13,    14,     0,     0,     0,     0,     0,    16,
-       0,    17,    33,    34,    35,    36,    37,    38,    39,    40,
-      41,    42,     9,    43,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,   114,     0,     0,     0,    12,    13,
-      14,     0,     0,     0,     0,     0,    16,     0,    17,    33,
-      34,    35,    36,    37,    38,    39,    40,    41,    42,     9,
-      43,     1,     0,     2,     3,     0,     4,     5,     6,     7,
-       8,     9,    10,    11,     0,    12,    13,    14,     0,     0,
-       0,     0,     0,    16,     0,    17,     0,    12,    13,    14,
-       0,     0,     0,    15,     0,    16,     0,    17
-};
-
-static const yytype_int8 yycheck[] =
-{
-       0,     0,    28,    21,     7,     5,     6,    25,     0,    28,
-      37,    16,    38,     5,     6,    15,    35,    29,    14,    15,
-      20,    20,    22,    23,    37,     0,    10,     4,    20,    29,
-      22,    23,     4,    17,    37,     7,    16,    32,    10,    44,
-      12,    13,    14,    28,    35,    17,    58,    17,    25,    36,
-      68,    28,    16,    25,    16,    20,    28,    25,    58,    43,
-       3,    68,    26,    27,    26,    27,    50,    -1,    32,    33,
-      34,    71,    72,    73,    -1,    -1,    -1,    -1,    62,    71,
-      72,    73,    82,    83,    84,    90,    -1,    -1,    -1,    -1,
-      82,    83,    84,    -1,    66,    -1,    -1,   102,    -1,    -1,
-      16,    -1,    -1,    87,    -1,    -1,    78,    79,    -1,    -1,
-      26,    27,   112,    85,    -1,    -1,   116,    33,    34,    -1,
-     112,    -1,     0,    -1,   116,     3,    -1,     5,     6,    -1,
-       8,     9,    10,    11,    12,    13,    14,    15,     3,     4,
-       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
-      -1,    29,    30,    31,    -1,    -1,    -1,    35,    -1,    37,
-      -1,    39,    -1,    -1,    29,    30,    31,    -1,    -1,    -1,
-      35,    -1,    37,    -1,    39,     3,     4,     5,     6,     7,
-       8,     9,    10,    11,    12,    13,    14,    15,     3,     4,
-       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
-      -1,    29,    30,    31,    -1,    -1,    -1,    -1,    -1,    37,
-      25,    39,    -1,    -1,    29,    30,    31,    -1,    -1,    -1,
-      -1,    -1,    37,    -1,    39,     3,     4,     5,     6,     7,
-       8,     9,    10,    11,    12,    13,    14,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    25,    -1,    -1,
-      -1,    29,    30,    31,    -1,    -1,    -1,    -1,    -1,    37,
-      -1,    39,     3,     4,     5,     6,     7,     8,     9,    10,
-      11,    12,    13,    14,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    25,    -1,    -1,    -1,    29,    30,
-      31,    -1,    -1,    -1,    -1,    -1,    37,    -1,    39,     3,
-       4,     5,     6,     7,     8,     9,    10,    11,    12,    13,
-      14,     3,    -1,     5,     6,    -1,     8,     9,    10,    11,
-      12,    13,    14,    15,    -1,    29,    30,    31,    -1,    -1,
-      -1,    -1,    -1,    37,    -1,    39,    -1,    29,    30,    31,
-      -1,    -1,    -1,    35,    -1,    37,    -1,    39
-};
-
-/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
-   symbol of state STATE-NUM.  */
-static const yytype_uint8 yystos[] =
-{
-       0,     3,     5,     6,     8,     9,    10,    11,    12,    13,
-      14,    15,    29,    30,    31,    35,    37,    39,    41,    42,
-      44,    46,    48,    50,    51,    58,    59,    61,    37,    37,
-      47,     7,    47,     3,     4,     5,     6,     7,     8,     9,
-      10,    11,    12,    14,    60,    61,    62,    51,    51,    60,
-      63,    46,    60,    60,    60,    60,    43,    44,    45,    51,
-      63,    46,    60,     0,    32,    42,    14,    49,    50,    51,
-      51,    16,    26,    27,    33,    34,    50,    60,    28,    38,
-      60,    43,    56,    53,    52,    28,    63,    57,    46,    60,
-      17,    36,    43,    32,    25,    46,    49,    51,    51,    51,
-      60,    60,     4,    25,    25,    51,    51,    51,    60,    46,
-      63,    63,    55,    25,    25,    51,    54,    51
-};
-
-#define yyerrok		(yyerrstatus = 0)
-#define yyclearin	(yychar = YYEMPTY)
-#define YYEMPTY		(-2)
-#define YYEOF		0
-
-#define YYACCEPT	goto yyacceptlab
-#define YYABORT		goto yyabortlab
-#define YYERROR		goto yyerrorlab
-
-
-/* Like YYERROR except do call yyerror.  This remains here temporarily
-   to ease the transition to the new meaning of YYERROR, for GCC.
-   Once GCC version 2 has supplanted version 1, this can go.  */
-
-#define YYFAIL		goto yyerrlab
-
-#define YYRECOVERING()  (!!yyerrstatus)
-
-#define YYBACKUP(Token, Value)					\
-do								\
-  if (yychar == YYEMPTY && yylen == 1)				\
-    {								\
-      yychar = (Token);						\
-      yylval = (Value);						\
-      yytoken = YYTRANSLATE (yychar);				\
-      YYPOPSTACK (1);						\
-      goto yybackup;						\
-    }								\
-  else								\
-    {								\
-      yyerror (YY_("syntax error: cannot back up")); \
-      YYERROR;							\
-    }								\
-while (YYID (0))
-
-
-#define YYTERROR	1
-#define YYERRCODE	256
-
-
-/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
-   If N is 0, then set CURRENT to the empty location which ends
-   the previous symbol: RHS[0] (always defined).  */
-
-#define YYRHSLOC(Rhs, K) ((Rhs)[K])
-#ifndef YYLLOC_DEFAULT
-# define YYLLOC_DEFAULT(Current, Rhs, N)				\
-    do									\
-      if (YYID (N))                                                    \
-	{								\
-	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
-	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
-	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
-	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
-	}								\
-      else								\
-	{								\
-	  (Current).first_line   = (Current).last_line   =		\
-	    YYRHSLOC (Rhs, 0).last_line;				\
-	  (Current).first_column = (Current).last_column =		\
-	    YYRHSLOC (Rhs, 0).last_column;				\
-	}								\
-    while (YYID (0))
-#endif
-
-
-/* YY_LOCATION_PRINT -- Print the location on the stream.
-   This macro was not mandated originally: define only if we know
-   we won't break user code: when these are the locations we know.  */
-
-#ifndef YY_LOCATION_PRINT
-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
-#  define YY_LOCATION_PRINT(File, Loc)			\
-     fprintf (File, "%d.%d-%d.%d",			\
-	      (Loc).first_line, (Loc).first_column,	\
-	      (Loc).last_line,  (Loc).last_column)
-# else
-#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-# endif
-#endif
-
-
-/* YYLEX -- calling `yylex' with the right arguments.  */
-
-#ifdef YYLEX_PARAM
-# define YYLEX yylex (YYLEX_PARAM)
-#else
-# define YYLEX yylex ()
-#endif
-
-/* Enable debugging if requested.  */
-#if YYDEBUG
-
-# ifndef YYFPRINTF
-#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
-#  define YYFPRINTF fprintf
-# endif
-
-# define YYDPRINTF(Args)			\
-do {						\
-  if (yydebug)					\
-    YYFPRINTF Args;				\
-} while (YYID (0))
-
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
-do {									  \
-  if (yydebug)								  \
-    {									  \
-      YYFPRINTF (stderr, "%s ", Title);					  \
-      yy_symbol_print (stderr,						  \
-		  Type, Value); \
-      YYFPRINTF (stderr, "\n");						  \
-    }									  \
-} while (YYID (0))
-
-
-/*--------------------------------.
-| Print this symbol on YYOUTPUT.  |
-`--------------------------------*/
-
-/*ARGSUSED*/
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-static void
-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
-#else
-static void
-yy_symbol_value_print (yyoutput, yytype, yyvaluep)
-    FILE *yyoutput;
-    int yytype;
-    YYSTYPE const * const yyvaluep;
-#endif
-{
-  if (!yyvaluep)
-    return;
-# ifdef YYPRINT
-  if (yytype < YYNTOKENS)
-    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
-# else
-  YYUSE (yyoutput);
-# endif
-  switch (yytype)
-    {
-      default:
-	break;
-    }
-}
-
-
-/*--------------------------------.
-| Print this symbol on YYOUTPUT.  |
-`--------------------------------*/
-
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-static void
-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
-#else
-static void
-yy_symbol_print (yyoutput, yytype, yyvaluep)
-    FILE *yyoutput;
-    int yytype;
-    YYSTYPE const * const yyvaluep;
-#endif
-{
-  if (yytype < YYNTOKENS)
-    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
-  else
-    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
-
-  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
-  YYFPRINTF (yyoutput, ")");
-}
-
-/*------------------------------------------------------------------.
-| yy_stack_print -- Print the state stack from its BOTTOM up to its |
-| TOP (included).                                                   |
-`------------------------------------------------------------------*/
-
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-static void
-yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
-#else
-static void
-yy_stack_print (bottom, top)
-    yytype_int16 *bottom;
-    yytype_int16 *top;
-#endif
-{
-  YYFPRINTF (stderr, "Stack now");
-  for (; bottom <= top; ++bottom)
-    YYFPRINTF (stderr, " %d", *bottom);
-  YYFPRINTF (stderr, "\n");
-}
-
-# define YY_STACK_PRINT(Bottom, Top)				\
-do {								\
-  if (yydebug)							\
-    yy_stack_print ((Bottom), (Top));				\
-} while (YYID (0))
-
-
-/*------------------------------------------------.
-| Report that the YYRULE is going to be reduced.  |
-`------------------------------------------------*/
-
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-static void
-yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
-#else
-static void
-yy_reduce_print (yyvsp, yyrule)
-    YYSTYPE *yyvsp;
-    int yyrule;
-#endif
-{
-  int yynrhs = yyr2[yyrule];
-  int yyi;
-  unsigned long int yylno = yyrline[yyrule];
-  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
-	     yyrule - 1, yylno);
-  /* The symbols being reduced.  */
-  for (yyi = 0; yyi < yynrhs; yyi++)
-    {
-      fprintf (stderr, "   $%d = ", yyi + 1);
-      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
-		       &(yyvsp[(yyi + 1) - (yynrhs)])
-		       		       );
-      fprintf (stderr, "\n");
-    }
-}
-
-# define YY_REDUCE_PRINT(Rule)		\
-do {					\
-  if (yydebug)				\
-    yy_reduce_print (yyvsp, Rule); \
-} while (YYID (0))
-
-/* Nonzero means print parse trace.  It is left uninitialized so that
-   multiple parsers can coexist.  */
-int yydebug;
-#else /* !YYDEBUG */
-# define YYDPRINTF(Args)
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
-# define YY_STACK_PRINT(Bottom, Top)
-# define YY_REDUCE_PRINT(Rule)
-#endif /* !YYDEBUG */
-
-
-/* YYINITDEPTH -- initial size of the parser's stacks.  */
-#ifndef	YYINITDEPTH
-# define YYINITDEPTH 200
-#endif
-
-/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
-   if the built-in stack extension method is used).
-
-   Do not make this value too large; the results are undefined if
-   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
-   evaluated with infinite-precision integer arithmetic.  */
-
-#ifndef YYMAXDEPTH
-# define YYMAXDEPTH 10000
-#endif
-
-
-
-#if YYERROR_VERBOSE
-
-# ifndef yystrlen
-#  if defined __GLIBC__ && defined _STRING_H
-#   define yystrlen strlen
-#  else
-/* Return the length of YYSTR.  */
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-static YYSIZE_T
-yystrlen (const char *yystr)
-#else
-static YYSIZE_T
-yystrlen (yystr)
-    const char *yystr;
-#endif
-{
-  YYSIZE_T yylen;
-  for (yylen = 0; yystr[yylen]; yylen++)
-    continue;
-  return yylen;
-}
-#  endif
-# endif
-
-# ifndef yystpcpy
-#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
-#   define yystpcpy stpcpy
-#  else
-/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
-   YYDEST.  */
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-static char *
-yystpcpy (char *yydest, const char *yysrc)
-#else
-static char *
-yystpcpy (yydest, yysrc)
-    char *yydest;
-    const char *yysrc;
-#endif
-{
-  char *yyd = yydest;
-  const char *yys = yysrc;
-
-  while ((*yyd++ = *yys++) != '\0')
-    continue;
-
-  return yyd - 1;
-}
-#  endif
-# endif
-
-# ifndef yytnamerr
-/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
-   quotes and backslashes, so that it's suitable for yyerror.  The
-   heuristic is that double-quoting is unnecessary unless the string
-   contains an apostrophe, a comma, or backslash (other than
-   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
-   null, do not copy; instead, return the length of what the result
-   would have been.  */
-static YYSIZE_T
-yytnamerr (char *yyres, const char *yystr)
-{
-  if (*yystr == '"')
-    {
-      YYSIZE_T yyn = 0;
-      char const *yyp = yystr;
-
-      for (;;)
-	switch (*++yyp)
-	  {
-	  case '\'':
-	  case ',':
-	    goto do_not_strip_quotes;
-
-	  case '\\':
-	    if (*++yyp != '\\')
-	      goto do_not_strip_quotes;
-	    /* Fall through.  */
-	  default:
-	    if (yyres)
-	      yyres[yyn] = *yyp;
-	    yyn++;
-	    break;
-
-	  case '"':
-	    if (yyres)
-	      yyres[yyn] = '\0';
-	    return yyn;
-	  }
-    do_not_strip_quotes: ;
-    }
-
-  if (! yyres)
-    return yystrlen (yystr);
-
-  return yystpcpy (yyres, yystr) - yyres;
-}
-# endif
-
-/* Copy into YYRESULT an error message about the unexpected token
-   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
-   including the terminating null byte.  If YYRESULT is null, do not
-   copy anything; just return the number of bytes that would be
-   copied.  As a special case, return 0 if an ordinary "syntax error"
-   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
-   size calculation.  */
-static YYSIZE_T
-yysyntax_error (char *yyresult, int yystate, int yychar)
-{
-  int yyn = yypact[yystate];
-
-  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
-    return 0;
-  else
-    {
-      int yytype = YYTRANSLATE (yychar);
-      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
-      YYSIZE_T yysize = yysize0;
-      YYSIZE_T yysize1;
-      int yysize_overflow = 0;
-      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
-      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
-      int yyx;
-
-# if 0
-      /* This is so xgettext sees the translatable formats that are
-	 constructed on the fly.  */
-      YY_("syntax error, unexpected %s");
-      YY_("syntax error, unexpected %s, expecting %s");
-      YY_("syntax error, unexpected %s, expecting %s or %s");
-      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
-      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
-# endif
-      char *yyfmt;
-      char const *yyf;
-      static char const yyunexpected[] = "syntax error, unexpected %s";
-      static char const yyexpecting[] = ", expecting %s";
-      static char const yyor[] = " or %s";
-      char yyformat[sizeof yyunexpected
-		    + sizeof yyexpecting - 1
-		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
-		       * (sizeof yyor - 1))];
-      char const *yyprefix = yyexpecting;
-
-      /* Start YYX at -YYN if negative to avoid negative indexes in
-	 YYCHECK.  */
-      int yyxbegin = yyn < 0 ? -yyn : 0;
-
-      /* Stay within bounds of both yycheck and yytname.  */
-      int yychecklim = YYLAST - yyn + 1;
-      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
-      int yycount = 1;
-
-      yyarg[0] = yytname[yytype];
-      yyfmt = yystpcpy (yyformat, yyunexpected);
-
-      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
-	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
-	  {
-	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
-	      {
-		yycount = 1;
-		yysize = yysize0;
-		yyformat[sizeof yyunexpected - 1] = '\0';
-		break;
-	      }
-	    yyarg[yycount++] = yytname[yyx];
-	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
-	    yysize_overflow |= (yysize1 < yysize);
-	    yysize = yysize1;
-	    yyfmt = yystpcpy (yyfmt, yyprefix);
-	    yyprefix = yyor;
-	  }
-
-      yyf = YY_(yyformat);
-      yysize1 = yysize + yystrlen (yyf);
-      yysize_overflow |= (yysize1 < yysize);
-      yysize = yysize1;
-
-      if (yysize_overflow)
-	return YYSIZE_MAXIMUM;
-
-      if (yyresult)
-	{
-	  /* Avoid sprintf, as that infringes on the user's name space.
-	     Don't have undefined behavior even if the translation
-	     produced a string with the wrong number of "%s"s.  */
-	  char *yyp = yyresult;
-	  int yyi = 0;
-	  while ((*yyp = *yyf) != '\0')
-	    {
-	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
-		{
-		  yyp += yytnamerr (yyp, yyarg[yyi++]);
-		  yyf += 2;
-		}
-	      else
-		{
-		  yyp++;
-		  yyf++;
-		}
-	    }
-	}
-      return yysize;
-    }
-}
-#endif /* YYERROR_VERBOSE */
-
-
-/*-----------------------------------------------.
-| Release the memory associated to this symbol.  |
-`-----------------------------------------------*/
-
-/*ARGSUSED*/
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-static void
-yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
-#else
-static void
-yydestruct (yymsg, yytype, yyvaluep)
-    const char *yymsg;
-    int yytype;
-    YYSTYPE *yyvaluep;
-#endif
-{
-  YYUSE (yyvaluep);
-
-  if (!yymsg)
-    yymsg = "Deleting";
-  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
-
-  switch (yytype)
-    {
-
-      default:
-	break;
-    }
-}
-
-
-/* Prevent warnings from -Wmissing-prototypes.  */
-
-#ifdef YYPARSE_PARAM
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void *YYPARSE_PARAM);
-#else
-int yyparse ();
-#endif
-#else /* ! YYPARSE_PARAM */
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void);
-#else
-int yyparse ();
-#endif
-#endif /* ! YYPARSE_PARAM */
-
-
-
-/* The look-ahead symbol.  */
-int yychar;
-
-/* The semantic value of the look-ahead symbol.  */
-YYSTYPE yylval;
-
-/* Number of syntax errors so far.  */
-int yynerrs;
-
-
-
-/*----------.
-| yyparse.  |
-`----------*/
-
-#ifdef YYPARSE_PARAM
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-int
-yyparse (void *YYPARSE_PARAM)
-#else
-int
-yyparse (YYPARSE_PARAM)
-    void *YYPARSE_PARAM;
-#endif
-#else /* ! YYPARSE_PARAM */
-#if (defined __STDC__ || defined __C99__FUNC__ \
-     || defined __cplusplus || defined _MSC_VER)
-int
-yyparse (void)
-#else
-int
-yyparse ()
-
-#endif
-#endif
-{
-  
-  int yystate;
-  int yyn;
-  int yyresult;
-  /* Number of tokens to shift before error messages enabled.  */
-  int yyerrstatus;
-  /* Look-ahead token as an internal (translated) token number.  */
-  int yytoken = 0;
-#if YYERROR_VERBOSE
-  /* Buffer for error messages, and its allocated size.  */
-  char yymsgbuf[128];
-  char *yymsg = yymsgbuf;
-  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
-#endif
-
-  /* Three stacks and their tools:
-     `yyss': related to states,
-     `yyvs': related to semantic values,
-     `yyls': related to locations.
-
-     Refer to the stacks thru separate pointers, to allow yyoverflow
-     to reallocate them elsewhere.  */
-
-  /* The state stack.  */
-  yytype_int16 yyssa[YYINITDEPTH];
-  yytype_int16 *yyss = yyssa;
-  yytype_int16 *yyssp;
-
-  /* The semantic value stack.  */
-  YYSTYPE yyvsa[YYINITDEPTH];
-  YYSTYPE *yyvs = yyvsa;
-  YYSTYPE *yyvsp;
-
-
-
-#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
-
-  YYSIZE_T yystacksize = YYINITDEPTH;
-
-  /* The variables used to return semantic value and location from the
-     action routines.  */
-  YYSTYPE yyval;
-
-
-  /* The number of symbols on the RHS of the reduced rule.
-     Keep to zero when no symbol should be popped.  */
-  int yylen = 0;
-
-  YYDPRINTF ((stderr, "Starting parse\n"));
-
-  yystate = 0;
-  yyerrstatus = 0;
-  yynerrs = 0;
-  yychar = YYEMPTY;		/* Cause a token to be read.  */
-
-  /* Initialize stack pointers.
-     Waste one element of value and location stack
-     so that they stay on the same level as the state stack.
-     The wasted elements are never initialized.  */
-
-  yyssp = yyss;
-  yyvsp = yyvs;
-
-  goto yysetstate;
-
-/*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate.  |
-`------------------------------------------------------------*/
- yynewstate:
-  /* In all cases, when you get here, the value and location stacks
-     have just been pushed.  So pushing a state here evens the stacks.  */
-  yyssp++;
-
- yysetstate:
-  *yyssp = yystate;
-
-  if (yyss + yystacksize - 1 <= yyssp)
-    {
-      /* Get the current used size of the three stacks, in elements.  */
-      YYSIZE_T yysize = yyssp - yyss + 1;
-
-#ifdef yyoverflow
-      {
-	/* Give user a chance to reallocate the stack.  Use copies of
-	   these so that the &'s don't force the real ones into
-	   memory.  */
-	YYSTYPE *yyvs1 = yyvs;
-	yytype_int16 *yyss1 = yyss;
-
-
-	/* Each stack pointer address is followed by the size of the
-	   data in use in that stack, in bytes.  This used to be a
-	   conditional around just the two extra args, but that might
-	   be undefined if yyoverflow is a macro.  */
-	yyoverflow (YY_("memory exhausted"),
-		    &yyss1, yysize * sizeof (*yyssp),
-		    &yyvs1, yysize * sizeof (*yyvsp),
-
-		    &yystacksize);
-
-	yyss = yyss1;
-	yyvs = yyvs1;
-      }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
-      goto yyexhaustedlab;
-# else
-      /* Extend the stack our own way.  */
-      if (YYMAXDEPTH <= yystacksize)
-	goto yyexhaustedlab;
-      yystacksize *= 2;
-      if (YYMAXDEPTH < yystacksize)
-	yystacksize = YYMAXDEPTH;
-
-      {
-	yytype_int16 *yyss1 = yyss;
-	union yyalloc *yyptr =
-	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
-	if (! yyptr)
-	  goto yyexhaustedlab;
-	YYSTACK_RELOCATE (yyss);
-	YYSTACK_RELOCATE (yyvs);
-
-#  undef YYSTACK_RELOCATE
-	if (yyss1 != yyssa)
-	  YYSTACK_FREE (yyss1);
-      }
-# endif
-#endif /* no yyoverflow */
-
-      yyssp = yyss + yysize - 1;
-      yyvsp = yyvs + yysize - 1;
-
-
-      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
-		  (unsigned long int) yystacksize));
-
-      if (yyss + yystacksize - 1 <= yyssp)
-	YYABORT;
-    }
-
-  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
-
-  goto yybackup;
-
-/*-----------.
-| yybackup.  |
-`-----------*/
-yybackup:
-
-  /* Do appropriate processing given the current state.  Read a
-     look-ahead token if we need one and don't already have one.  */
-
-  /* First try to decide what to do without reference to look-ahead token.  */
-  yyn = yypact[yystate];
-  if (yyn == YYPACT_NINF)
-    goto yydefault;
-
-  /* Not known => get a look-ahead token if don't already have one.  */
-
-  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
-  if (yychar == YYEMPTY)
-    {
-      YYDPRINTF ((stderr, "Reading a token: "));
-      yychar = YYLEX;
-    }
-
-  if (yychar <= YYEOF)
-    {
-      yychar = yytoken = YYEOF;
-      YYDPRINTF ((stderr, "Now at end of input.\n"));
-    }
-  else
-    {
-      yytoken = YYTRANSLATE (yychar);
-      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
-    }
-
-  /* If the proper action on seeing token YYTOKEN is to reduce or to
-     detect an error, take that action.  */
-  yyn += yytoken;
-  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
-    goto yydefault;
-  yyn = yytable[yyn];
-  if (yyn <= 0)
-    {
-      if (yyn == 0 || yyn == YYTABLE_NINF)
-	goto yyerrlab;
-      yyn = -yyn;
-      goto yyreduce;
-    }
-
-  if (yyn == YYFINAL)
-    YYACCEPT;
-
-  /* Count tokens shifted since error; after three, turn off error
-     status.  */
-  if (yyerrstatus)
-    yyerrstatus--;
-
-  /* Shift the look-ahead token.  */
-  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
-
-  /* Discard the shifted token unless it is eof.  */
-  if (yychar != YYEOF)
-    yychar = YYEMPTY;
-
-  yystate = yyn;
-  *++yyvsp = yylval;
-
-  goto yynewstate;
-
-
-/*-----------------------------------------------------------.
-| yydefault -- do the default action for the current state.  |
-`-----------------------------------------------------------*/
-yydefault:
-  yyn = yydefact[yystate];
-  if (yyn == 0)
-    goto yyerrlab;
-  goto yyreduce;
-
-
-/*-----------------------------.
-| yyreduce -- Do a reduction.  |
-`-----------------------------*/
-yyreduce:
-  /* yyn is the number of a rule to reduce with.  */
-  yylen = yyr2[yyn];
-
-  /* If YYLEN is nonzero, implement the default value of the action:
-     `$$ = $1'.
-
-     Otherwise, the following line sets YYVAL to garbage.
-     This behavior is undocumented and Bison
-     users should not rely upon it.  Assigning to YYVAL
-     unconditionally makes the parser a bit smaller, and it avoids a
-     GCC warning that YYVAL may be used uninitialized.  */
-  yyval = yyvsp[1-yylen];
-
-
-  YY_REDUCE_PRINT (yyn);
-  switch (yyn)
-    {
-        case 2:
-#line 24 "syn.y"
-    { return 1;}
-    break;
-
-  case 3:
-#line 25 "syn.y"
-    {readhere(lex->input); return !compile((yyvsp[(1) - (2)].tree));}
-    break;
-
-  case 5:
-#line 27 "syn.y"
-    {(yyval.tree)=tree2(';', (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 7:
-#line 29 "syn.y"
-    {(yyval.tree)=tree2(';', (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 9:
-#line 31 "syn.y"
-    {(yyval.tree)=tree1('&', (yyvsp[(1) - (2)].tree));}
-    break;
-
-  case 11:
-#line 33 "syn.y"
-    {readhere(lex->input);}
-    break;
-
-  case 12:
-#line 34 "syn.y"
-    {(yyval.tree)=tree1(BRACE, (yyvsp[(2) - (3)].tree));}
-    break;
-
-  case 13:
-#line 35 "syn.y"
-    {(yyval.tree)=tree1(PCMD, (yyvsp[(2) - (3)].tree));}
-    break;
-
-  case 14:
-#line 36 "syn.y"
-    {(yyval.tree)=tree2('=', (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
-    break;
-
-  case 15:
-#line 37 "syn.y"
-    {(yyval.tree)=0;}
-    break;
-
-  case 16:
-#line 38 "syn.y"
-    {(yyval.tree)=mung2((yyvsp[(1) - (2)].tree), (yyvsp[(1) - (2)].tree)->child[0], (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 17:
-#line 39 "syn.y"
-    {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)); if((yyval.tree)->rtype==HERE) heredoc((yyval.tree));}
-    break;
-
-  case 19:
-#line 41 "syn.y"
-    {(yyval.tree)=0;}
-    break;
-
-  case 20:
-#line 42 "syn.y"
-    {(yyval.tree)=epimung((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 21:
-#line 43 "syn.y"
-    {skipnl();}
-    break;
-
-  case 22:
-#line 44 "syn.y"
-    {(yyval.tree)=mung2((yyvsp[(1) - (4)].tree), (yyvsp[(2) - (4)].tree), (yyvsp[(4) - (4)].tree));}
-    break;
-
-  case 23:
-#line 45 "syn.y"
-    {skipnl();}
-    break;
-
-  case 24:
-#line 45 "syn.y"
-    {(yyval.tree)=mung1((yyvsp[(2) - (4)].tree), (yyvsp[(4) - (4)].tree));}
-    break;
-
-  case 25:
-#line 46 "syn.y"
-    {skipnl();}
-    break;
-
-  case 26:
-#line 55 "syn.y"
-    {(yyval.tree)=mung3((yyvsp[(1) - (8)].tree), (yyvsp[(3) - (8)].tree), (yyvsp[(5) - (8)].tree) ? (yyvsp[(5) - (8)].tree) : tree1(PAREN, (yyvsp[(5) - (8)].tree)), (yyvsp[(8) - (8)].tree));}
-    break;
-
-  case 27:
-#line 56 "syn.y"
-    {skipnl();}
-    break;
-
-  case 28:
-#line 57 "syn.y"
-    {(yyval.tree)=mung3((yyvsp[(1) - (6)].tree), (yyvsp[(3) - (6)].tree), (tree*)0, (yyvsp[(6) - (6)].tree));}
-    break;
-
-  case 29:
-#line 58 "syn.y"
-    {skipnl();}
-    break;
-
-  case 30:
-#line 59 "syn.y"
-    {(yyval.tree)=mung2((yyvsp[(1) - (4)].tree), (yyvsp[(2) - (4)].tree), (yyvsp[(4) - (4)].tree));}
-    break;
-
-  case 31:
-#line 60 "syn.y"
-    {skipnl();}
-    break;
-
-  case 32:
-#line 61 "syn.y"
-    {(yyval.tree)=tree2(SWITCH, (yyvsp[(2) - (4)].tree), (yyvsp[(4) - (4)].tree));}
-    break;
-
-  case 33:
-#line 62 "syn.y"
-    {(yyval.tree)=simplemung((yyvsp[(1) - (1)].tree));}
-    break;
-
-  case 34:
-#line 63 "syn.y"
-    {(yyval.tree)=mung2((yyvsp[(1) - (3)].tree), (yyvsp[(2) - (3)].tree), (yyvsp[(3) - (3)].tree));}
-    break;
-
-  case 35:
-#line 64 "syn.y"
-    {(yyval.tree)=tree2(ANDAND, (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
-    break;
-
-  case 36:
-#line 65 "syn.y"
-    {(yyval.tree)=tree2(OROR, (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
-    break;
-
-  case 37:
-#line 66 "syn.y"
-    {(yyval.tree)=mung2((yyvsp[(2) - (3)].tree), (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
-    break;
-
-  case 38:
-#line 67 "syn.y"
-    {(yyval.tree)=mung2((yyvsp[(1) - (2)].tree), (yyvsp[(1) - (2)].tree)->child[0], (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 39:
-#line 68 "syn.y"
-    {(yyval.tree)=mung3((yyvsp[(1) - (2)].tree), (yyvsp[(1) - (2)].tree)->child[0], (yyvsp[(1) - (2)].tree)->child[1], (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 40:
-#line 69 "syn.y"
-    {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 41:
-#line 70 "syn.y"
-    {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 42:
-#line 71 "syn.y"
-    {(yyval.tree)=tree2(FN, (yyvsp[(2) - (3)].tree), (yyvsp[(3) - (3)].tree));}
-    break;
-
-  case 43:
-#line 72 "syn.y"
-    {(yyval.tree)=tree1(FN, (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 45:
-#line 74 "syn.y"
-    {(yyval.tree)=globprop(tree2(ARGLIST, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)));}
-    break;
-
-  case 46:
-#line 75 "syn.y"
-    {(yyval.tree)=globprop(tree2(ARGLIST, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)));}
-    break;
-
-  case 48:
-#line 77 "syn.y"
-    {(yyval.tree)=globprop(tree2('^', (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree)));}
-    break;
-
-  case 49:
-#line 78 "syn.y"
-    {lex->lastword=1; (yyvsp[(1) - (1)].tree)->type=WORD;}
-    break;
-
-  case 51:
-#line 80 "syn.y"
-    {(yyval.tree)=globprop(tree2('^', (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree)));}
-    break;
-
-  case 52:
-#line 81 "syn.y"
-    {(yyval.tree)=tree1('$', (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 53:
-#line 82 "syn.y"
-    {(yyval.tree)=tree2(SUB, (yyvsp[(2) - (5)].tree), (yyvsp[(4) - (5)].tree));}
-    break;
-
-  case 54:
-#line 83 "syn.y"
-    {(yyval.tree)=tree1('"', (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 55:
-#line 84 "syn.y"
-    {(yyval.tree)=tree1(COUNT, (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 57:
-#line 86 "syn.y"
-    {(yyval.tree)=tree2('`', (tree*)0, (yyvsp[(2) - (2)].tree));}
-    break;
-
-  case 58:
-#line 87 "syn.y"
-    {(yyval.tree)=tree2('`', (yyvsp[(2) - (3)].tree), (yyvsp[(3) - (3)].tree));}
-    break;
-
-  case 59:
-#line 88 "syn.y"
-    {(yyval.tree)=globprop(tree1(PAREN, (yyvsp[(2) - (3)].tree)));}
-    break;
-
-  case 60:
-#line 89 "syn.y"
-    {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)); (yyval.tree)->type=PIPEFD;}
-    break;
-
-  case 71:
-#line 91 "syn.y"
-    {(yyval.tree)=(tree*)0;}
-    break;
-
-  case 72:
-#line 92 "syn.y"
-    {(yyval.tree)=tree2(WORDS, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
-    break;
-
-
-/* Line 1267 of yacc.c.  */
-#line 1776 "y.tab.c"
-      default: break;
-    }
-  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
-
-  YYPOPSTACK (yylen);
-  yylen = 0;
-  YY_STACK_PRINT (yyss, yyssp);
-
-  *++yyvsp = yyval;
-
-
-  /* Now `shift' the result of the reduction.  Determine what state
-     that goes to, based on the state we popped back to and the rule
-     number reduced by.  */
-
-  yyn = yyr1[yyn];
-
-  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
-  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
-    yystate = yytable[yystate];
-  else
-    yystate = yydefgoto[yyn - YYNTOKENS];
-
-  goto yynewstate;
-
-
-/*------------------------------------.
-| yyerrlab -- here on detecting error |
-`------------------------------------*/
-yyerrlab:
-  /* If not already recovering from an error, report this error.  */
-  if (!yyerrstatus)
-    {
-      ++yynerrs;
-#if ! YYERROR_VERBOSE
-      yyerror (YY_("syntax error"));
-#else
-      {
-	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
-	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
-	  {
-	    YYSIZE_T yyalloc = 2 * yysize;
-	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
-	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
-	    if (yymsg != yymsgbuf)
-	      YYSTACK_FREE (yymsg);
-	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
-	    if (yymsg)
-	      yymsg_alloc = yyalloc;
-	    else
-	      {
-		yymsg = yymsgbuf;
-		yymsg_alloc = sizeof yymsgbuf;
-	      }
-	  }
-
-	if (0 < yysize && yysize <= yymsg_alloc)
-	  {
-	    (void) yysyntax_error (yymsg, yystate, yychar);
-	    yyerror (yymsg);
-	  }
-	else
-	  {
-	    yyerror (YY_("syntax error"));
-	    if (yysize != 0)
-	      goto yyexhaustedlab;
-	  }
-      }
-#endif
-    }
-
-
-
-  if (yyerrstatus == 3)
-    {
-      /* If just tried and failed to reuse look-ahead token after an
-	 error, discard it.  */
-
-      if (yychar <= YYEOF)
-	{
-	  /* Return failure if at end of input.  */
-	  if (yychar == YYEOF)
-	    YYABORT;
-	}
-      else
-	{
-	  yydestruct ("Error: discarding",
-		      yytoken, &yylval);
-	  yychar = YYEMPTY;
-	}
-    }
-
-  /* Else will try to reuse look-ahead token after shifting the error
-     token.  */
-  goto yyerrlab1;
-
-
-/*---------------------------------------------------.
-| yyerrorlab -- error raised explicitly by YYERROR.  |
-`---------------------------------------------------*/
-yyerrorlab:
-
-  /* Pacify compilers like GCC when the user code never invokes
-     YYERROR and the label yyerrorlab therefore never appears in user
-     code.  */
-  if (/*CONSTCOND*/ 0)
-     goto yyerrorlab;
-
-  /* Do not reclaim the symbols of the rule which action triggered
-     this YYERROR.  */
-  YYPOPSTACK (yylen);
-  yylen = 0;
-  YY_STACK_PRINT (yyss, yyssp);
-  yystate = *yyssp;
-  goto yyerrlab1;
-
-
-/*-------------------------------------------------------------.
-| yyerrlab1 -- common code for both syntax error and YYERROR.  |
-`-------------------------------------------------------------*/
-yyerrlab1:
-  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
-
-  for (;;)
-    {
-      yyn = yypact[yystate];
-      if (yyn != YYPACT_NINF)
-	{
-	  yyn += YYTERROR;
-	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
-	    {
-	      yyn = yytable[yyn];
-	      if (0 < yyn)
-		break;
-	    }
-	}
-
-      /* Pop the current state because it cannot handle the error token.  */
-      if (yyssp == yyss)
-	YYABORT;
-
-
-      yydestruct ("Error: popping",
-		  yystos[yystate], yyvsp);
-      YYPOPSTACK (1);
-      yystate = *yyssp;
-      YY_STACK_PRINT (yyss, yyssp);
-    }
-
-  if (yyn == YYFINAL)
-    YYACCEPT;
-
-  *++yyvsp = yylval;
-
-
-  /* Shift the error token.  */
-  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
-
-  yystate = yyn;
-  goto yynewstate;
-
-
-/*-------------------------------------.
-| yyacceptlab -- YYACCEPT comes here.  |
-`-------------------------------------*/
-yyacceptlab:
-  yyresult = 0;
-  goto yyreturn;
-
-/*-----------------------------------.
-| yyabortlab -- YYABORT comes here.  |
-`-----------------------------------*/
-yyabortlab:
-  yyresult = 1;
-  goto yyreturn;
-
-#ifndef yyoverflow
-/*-------------------------------------------------.
-| yyexhaustedlab -- memory exhaustion comes here.  |
-`-------------------------------------------------*/
-yyexhaustedlab:
-  yyerror (YY_("memory exhausted"));
-  yyresult = 2;
-  /* Fall through.  */
-#endif
-
-yyreturn:
-  if (yychar != YYEOF && yychar != YYEMPTY)
-     yydestruct ("Cleanup: discarding lookahead",
-		 yytoken, &yylval);
-  /* Do not reclaim the symbols of the rule which action triggered
-     this YYABORT or YYACCEPT.  */
-  YYPOPSTACK (yylen);
-  YY_STACK_PRINT (yyss, yyssp);
-  while (yyssp != yyss)
-    {
-      yydestruct ("Cleanup: popping",
-		  yystos[*yyssp], yyvsp);
-      YYPOPSTACK (1);
-    }
-#ifndef yyoverflow
-  if (yyss != yyssa)
-    YYSTACK_FREE (yyss);
-#endif
-#if YYERROR_VERBOSE
-  if (yymsg != yymsgbuf)
-    YYSTACK_FREE (yymsg);
-#endif
-  /* Make sure YYID is used.  */
-  return YYID (yyresult);
-}
-
-
-
--- a/librc/y.tab.h
+++ /dev/null
@@ -1,114 +1,0 @@
-/* A Bison parser, made by GNU Bison 2.3.  */
-
-/* Skeleton interface for Bison's Yacc-like parsers in C
-
-   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
-   Free Software Foundation, Inc.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.  */
-
-/* As a special exception, you may create a larger work that contains
-   part or all of the Bison parser skeleton and distribute that work
-   under terms of your choice, so long as that work isn't itself a
-   parser generator using the skeleton or a modified version thereof
-   as a parser skeleton.  Alternatively, if you modify or redistribute
-   the parser skeleton itself, you may (at your option) remove this
-   special exception, which will cause the skeleton and the resulting
-   Bison output files to be licensed under the GNU General Public
-   License without this special exception.
-
-   This special exception was added by the Free Software Foundation in
-   version 2.2 of Bison.  */
-
-/* Tokens.  */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
-   /* Put the tokens into the symbol table, so that GDB and other debuggers
-      know about them.  */
-   enum yytokentype {
-     FOR = 258,
-     IN = 259,
-     WHILE = 260,
-     IF = 261,
-     NOT = 262,
-     TWIDDLE = 263,
-     BANG = 264,
-     SUBSHELL = 265,
-     SWITCH = 266,
-     FN = 267,
-     WORD = 268,
-     REDIR = 269,
-     DUP = 270,
-     PIPE = 271,
-     SUB = 272,
-     SIMPLE = 273,
-     ARGLIST = 274,
-     WORDS = 275,
-     BRACE = 276,
-     PAREN = 277,
-     PCMD = 278,
-     PIPEFD = 279,
-     OROR = 280,
-     ANDAND = 281,
-     COUNT = 282
-   };
-#endif
-/* Tokens.  */
-#define FOR 258
-#define IN 259
-#define WHILE 260
-#define IF 261
-#define NOT 262
-#define TWIDDLE 263
-#define BANG 264
-#define SUBSHELL 265
-#define SWITCH 266
-#define FN 267
-#define WORD 268
-#define REDIR 269
-#define DUP 270
-#define PIPE 271
-#define SUB 272
-#define SIMPLE 273
-#define ARGLIST 274
-#define WORDS 275
-#define BRACE 276
-#define PAREN 277
-#define PCMD 278
-#define PIPEFD 279
-#define OROR 280
-#define ANDAND 281
-#define COUNT 282
-
-
-
-
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-typedef union YYSTYPE
-#line 16 "syn.y"
-{
-	struct tree *tree;
-}
-/* Line 1529 of yacc.c.  */
-#line 107 "y.tab.h"
-	YYSTYPE;
-# define yystype YYSTYPE /* obsolescent; will be withdrawn */
-# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
-#endif
-
-extern YYSTYPE yylval;
-
--- a/main.c
+++ b/main.c
@@ -45,8 +45,8 @@
 main(int argc, char **argv)
 {
 	int fd;
+	char s[1024];
 	extern ulong kerndate;
-
 	kerndate = seconds();
 	eve = getuser();
 	if(eve == nil)
@@ -73,11 +73,22 @@
 	bind("#N", "/dev", MAFTER);
 	bind("#C", "/", MAFTER);
 
-	/* TODO: flag for debug */
-	fd = open("/dev/cons", ORDWR);
-	dbg = open(dbgfile, OWRITE);
-	if(session(fd) < 0)
-		fprint(dbg, "session failed: %r\n");
+    if(bind("/root", "/", MAFTER) < 0)
+		panic("bind /root: %r");
+
+	if((fd = open("/dev/cons", OREAD)) < 0)
+		panic("open stdin: %r");
+	if(read(fd, s, sizeof s) <= 0)
+		panic("read: %r");
 	close(fd);
+
+	char *cmd[] = {
+		"/bin/rc",
+		"-c",
+		s
+	};
+
+	printf("%s %s %s\n", cmd[0], cmd[1], cmd[2]);
+	runcommand(3, cmd);
 	_exit(0);
 }
--- /dev/null
+++ b/rc/Makefile
@@ -1,0 +1,30 @@
+ROOT=..
+include ../Make.config
+LIB=librc.a
+
+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\
+	havefork.$O\
+	drawcpu.$O\
+	y.tab.$O\
+
+default: $(LIB)
+$(LIB): $(OFILES)
+	$(AR) r $(LIB) $(OFILES)
+	$(RANLIB) $(LIB)
+
+%.$O: %.c
+	$(CC) $(CFLAGS) $*.c
--- /dev/null
+++ b/rc/code.c
@@ -1,0 +1,547 @@
+#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]
+code *codebuf;
+static int codep, ncode, codeline;
+#define	emitf(x) ((codep!=ncode || morecode()), codebuf[codep].f = (x), codep++)
+#define	emiti(x) ((codep!=ncode || morecode()), codebuf[codep].i = (x), codep++)
+#define	emits(x) ((codep!=ncode || morecode()), codebuf[codep].s = (x), codep++)
+
+void stuffdot(int);
+void outcode(tree*, int);
+void codeswitch(tree*, int);
+int iscase(tree*);
+code *codecopy(code*);
+void codefree(code*);
+
+int
+morecode(void)
+{
+	ncode+=ncode;
+	codebuf = (code *)erealloc((char *)codebuf, 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 = emalloc(ncode*sizeof codebuf[0]);
+	codep = 0;
+	codeline = 0;			/* force source */
+	emiti(0);			/* reference count */
+	emits(estrdup(lex->file));	/* source file name */
+	outcode(t, !lex->qflag && flag['e']!=0);
+	if(nerror){
+		free(codebuf);
+		return 0;
+	}
+	emitf(Xreturn);
+	emitf(0);
+	return 1;
+}
+
+/*
+ * called on a tree where we expect eigther
+ * a pattern or a string instead of a glob to
+ * remove the GLOB chars from the strings
+ * or set glob to 2 for pattern so Xglob
+ * is not inserted when compiling the tree.
+ */
+void
+noglobs(tree *t, int pattern)
+{
+Again:
+	if(t==0)
+		return;
+	if(t->type==WORD && t->glob){
+		if(pattern)
+			t->glob=2;
+		else{
+			deglob(t->str);
+			t->glob=0;
+		}
+	}
+	if(t->type==PAREN || t->type==WORDS || t->type=='^'){
+		t->glob=0;
+		noglobs(c1, pattern);
+		t = c0;
+		goto Again;
+	}
+}
+
+void
+outcode(tree *t, int eflag)
+{
+	void (*f)(void);
+	int p, q;
+	tree *tt;
+	if(t==0)
+		return;
+	if(t->type!=NOT && t->type!=';')
+		lex->iflast = 0;
+	if(t->line != codeline){
+		codeline = t->line;
+		if(codebuf && codep >= 2 && codebuf[codep-2].f == Xsrcline)
+			codebuf[codep-1].i = codeline;
+		else {
+			emitf(Xsrcline);
+			emiti(codeline);
+		}
+	}
+	switch(t->type){
+	default:
+		pfmt(err, "bad type %d in outcode\n", t->type);
+		break;
+	case '$':
+		emitf(Xmark);
+		noglobs(c0, 0);
+		outcode(c0, eflag);
+		emitf(Xdol);
+		break;
+	case '"':
+		emitf(Xmark);
+		emitf(Xmark);
+		noglobs(c0, 0);
+		outcode(c0, eflag);
+		emitf(Xdol);
+		emitf(Xqw);
+		emitf(Xpush);
+		break;
+	case SUB:
+		emitf(Xmark);
+		noglobs(c0, 0);
+		outcode(c0, eflag);
+		emitf(Xmark);
+		noglobs(c1, 0);
+		outcode(c1, eflag);
+		emitf(Xsub);
+		break;
+	case '&':
+		emitf(Xasync);
+		p = emiti(0);
+
+		/* undocumented? */
+		emitf(Xmark);
+		emitf(Xword);
+		emits(estrdup("/dev/null"));
+		emitf(Xread);
+		emiti(0);
+
+		/* insert rfork s for plan9 */
+		f = builtinfunc("rfork");
+		if(f){
+			emitf(Xmark);
+			emitf(Xword);
+			emits(estrdup("s"));
+			emitf(Xword);
+			emits(estrdup("rfork"));
+			emitf(f);
+		}
+
+		codeline = 0;	/* force source */
+		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(Xmark);
+		if(c0){
+			noglobs(c0, 0);
+			outcode(c0, 0);
+		} else {
+			emitf(Xmark);
+			emitf(Xword);
+			emits(estrdup("ifs"));
+			emitf(Xdol);
+		}
+		emitf(Xqw);
+		emitf(Xbackq);
+		p = emiti(0);
+		codeline = 0;	/* force source */
+		outcode(c1, 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);
+		noglobs(c0, 0);
+		outcode(c0, eflag);
+		emitf(Xcount);
+		break;
+	case FN:
+		emitf(Xmark);
+		noglobs(c0, 0);
+		outcode(c0, eflag);
+		if(c1){
+			emitf(Xfn);
+			p = emiti(0);
+			emits(fnstr(c1));
+			codeline = 0;	/* force source */
+			outcode(c1, eflag);
+			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(!lex->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);
+		codeline = 0;	/* force source */
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+		if(eflag)
+			emitf(Xeflag);
+		break;
+	case SWITCH:
+		codeswitch(t, eflag);
+		break;
+	case TWIDDLE:
+		emitf(Xmark);
+		noglobs(c1, 1);
+		outcode(c1, eflag);
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xqw);
+		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);
+		}
+		else{
+			emitf(Xmark);
+			emitf(Xword);
+			emits(estrdup("*"));
+			emitf(Xdol);
+		}
+		emitf(Xmark);		/* dummy value for Xlocal */
+		emitf(Xmark);
+		noglobs(c0, 0);
+		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(t->str);
+		t->str=0;	/* passed ownership */
+		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);
+		codeline = 0;	/* force source */
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+		break;
+	case REDIR:
+		if(t->rtype!=HERE){
+			emitf(Xmark);
+			outcode(c0, eflag);
+		}
+		switch(t->rtype){
+		case APPEND:
+			emitf(Xappend);
+			break;
+		case WRITE:
+			emitf(Xwrite);
+			break;
+		case READ:
+			emitf(Xread);
+			break;
+		case RDWR:
+			emitf(Xrdwr);
+			break;
+		case HERE:
+			emitf(c0->quoted?Xhereq:Xhere);
+			emits(t->str);
+			t->str=0;	/* passed ownership */
+			break;
+		}
+		emiti(t->fd0);
+		outcode(c1, eflag);
+		emitf(Xpopredir);
+		break;
+	case '=':
+		tt = t;
+		for(;t && t->type=='=';t = c2);
+		if(t){					/* var=value cmd */
+			for(t = tt;t->type=='=';t = c2){
+				emitf(Xmark);
+				outcode(c1, eflag);
+				emitf(Xmark);
+				noglobs(c0, 0);
+				outcode(c0, eflag);
+				emitf(Xlocal);		/* push var for cmd */
+			}
+			outcode(t, eflag);		/* gen. code for cmd */
+			for(t = tt; t->type == '='; t = c2)
+				emitf(Xunlocal);	/* pop var */
+		}
+		else{					/* var=value */
+			for(t = tt;t;t = c2){
+				emitf(Xmark);
+				outcode(c1, eflag);
+				emitf(Xmark);
+				noglobs(c0, 0);
+				outcode(c0, eflag);
+				emitf(Xassign);	/* set var permanently */
+			}
+		}
+		t = tt;	/* so tests below will work */
+		break;
+	case PIPE:
+		emitf(Xpipe);
+		emiti(t->fd0);
+		emiti(t->fd1);
+		p = emiti(0);
+		q = emiti(0);
+		codeline = 0;	/* force source */
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+		codeline = 0;	/* force source */
+		outcode(c1, eflag);
+		emitf(Xreturn);
+		stuffdot(q);
+		emitf(Xpipewait);
+		break;
+	}
+	if(t->glob==1)
+		emitf(Xglob);
+	if(t->type!=NOT && t->type!=';')
+		lex->iflast = t->type==IF;
+	else if(c0)
+		lex->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]==0
+	|| c1->child[0]->type!=';'
+	|| !iscase(c1->child[0]->child[0])){
+		yyerror("case missing in switch");
+		return;
+	}
+	emitf(Xmark);
+	outcode(c0, eflag);
+	emitf(Xqw);
+	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) {
+			noglobs(c1, 1);
+			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+2;p->f;p++){
+		if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite
+		|| p->f==Xrdwr
+		|| p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse
+		|| p->f==Xfor || p->f==Xjump
+		|| p->f==Xsrcline
+		|| 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==Xhere || p->f==Xhereq) free(p[1].s), p+=2;
+		else if(p->f==Xword) free((++p)->s);
+		else if(p->f==Xfn){
+			free(p[2].s);
+			p+=2;
+		}
+	}
+	free(cp[1].s);
+	free(cp);
+}
--- /dev/null
+++ b/rc/drawcpu.c
@@ -1,0 +1,385 @@
+/*
+ * 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"
+
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/wait.h>
+
+static void execfinit(void);
+
+builtin Builtin[] = {
+	"cd",		execcd,
+	"whatis",	execwhatis,
+	"eval",		execeval,
+	"exec",		execexec,	/* but with popword first */
+	"exit",		execexit,
+	"shift",	execshift,
+	"wait",		execwait,
+	".",		execdot,
+	"flag",		execflag,
+	"finit",	execfinit,
+	0
+};
+
+// TODO: PREFIX "/lib/rcmain"
+char Rcmain[]="./rc/rcmain.unix";
+char Fdprefix[]="/dev/fd/";
+
+char *Signame[NSIG];
+
+/*
+ * finit could be removed but is kept for
+ * backwards compatibility, see: rcmain.plan9
+ */
+static void
+execfinit(void)
+{
+	char *cmds = estrdup("for(i in '/env/fn#'*){. -bq $i}\n");
+	int line = runq->line;
+	poplist();
+	execcmds(openiocore(cmds, strlen(cmds)), estrdup(srcfile(runq)), runq->local, runq->redir);
+	runq->lex->line = line;
+	runq->lex->qflag = 1;
+}
+
+char*
+Env(char *name, int fn)
+{
+	static char buf[128];
+
+	strcpy(buf, "/env/");
+	if(fn) strcat(buf, "fn#");
+	return strncat(buf, name, sizeof(buf)-1);
+}
+
+void
+Vinit(void)
+{
+	int fd;
+	DIR *dir;
+	struct dirent *ent;
+
+	dir = opendir("/env");
+	if(dir == nil){
+		pfmt(err, "%s: can't open: %s\n", argv0, Errstr());
+		return;
+	}
+	for(;;){
+		ent = readdir(dir);
+		if (ent == nil)
+			break;
+		if(ent->d_namlen<=0 || strncmp(ent->d_name, "fn#", 3)==0)
+			continue;
+		if((fd = Open(Env(ent->d_name, 0), 0))>=0){
+			io *f = openiofd(fd);
+			word *w = 0, **wp = &w;
+			char *s;
+			while((s = rstr(f, "")) != 0){
+				*wp = Newword(s, (word*)0);
+				wp = &(*wp)->next;
+			}
+			closeio(f);
+			setvar(ent->d_name, w);
+			vlook(ent->d_name)->changed = 0;
+		}
+		free(ent);
+	}
+	closedir(dir);
+}
+
+char*
+Errstr(void)
+{
+	return strerror(errno);
+}
+
+/* Can we do this inside the kernel? */
+int
+Waitfor(int pid)
+{
+	thread *p;
+	char num[12];
+	int wpid, status;
+
+	if(pid >= 0 && !havewaitpid(pid))
+		return 0;
+	while((wpid = wait(&status))!=-1){
+		delwaitpid(wpid);
+		inttoascii(num, WIFSIGNALED(status)?WTERMSIG(status)+1000:WEXITSTATUS(status));
+		if(wpid==pid){
+			setstatus(num);
+			return 0;
+		}
+		for(p = runq->ret;p;p = p->ret)
+			if(p->pid==wpid){
+				p->pid=-1;
+				p->status = estrdup(num);
+				break;
+			}
+	}
+	if(errno==EINTR) return -1;
+	return 0;
+}
+
+static void
+addenv(var *v)
+{
+	word *w;
+	int fd;
+	io *f;
+
+	if(v->changed){
+		v->changed = 0;
+		if((fd = Creat(Env(v->name, 0)))<0)
+			pfmt(err, "%s: can't open: %s\n", argv0, Errstr());
+		else{
+			f = openiofd(fd);
+			for(w = v->val;w;w = w->next){
+				pstr(f, w->word);
+				pchr(f, '\0');
+			}
+			flushio(f);
+			closeio(f);
+		}
+	}
+	if(v->fnchanged){
+		v->fnchanged = 0;
+		if((fd = Creat(Env(v->name, 1)))<0)
+			pfmt(err, "%s: can't open: %s\n", argv0, Errstr());
+		else{
+			f = openiofd(fd);
+			if(v->fn)
+				pfmt(f, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
+			flushio(f);
+			closeio(f);
+		}
+	}
+}
+
+static 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);
+	if(err)
+		flushio(err);
+}
+
+void
+Exec(char **argv)
+{
+	// TODO: execve after loading env to string
+	execv(argv[0], argv+1);
+}
+
+int
+Fork(void)
+{
+	Updenv();
+	return fork();
+}
+
+void*
+Opendir(char *name)
+{
+	return opendir(name);
+}
+
+char*
+Readdir(void *arg, int onlydirs)
+{
+	DIR *rd = arg;
+	struct dirent *ent = readdir(rd);
+	if(ent == NULL)
+		return 0;
+	return ent->d_name;
+}
+
+void
+Closedir(void *arg)
+{
+	DIR *rd = arg;
+	closedir(rd);
+}
+
+static void
+sighandler(int sig)
+{
+	trap[sig]++;
+	ntrap++;
+}
+
+// TODO: Use kernel sighandling 
+void
+Trapinit(void)
+{
+	int i;
+
+	Signame[0] = "sigexit";
+
+#ifdef SIGINT
+	Signame[SIGINT] = "sigint";
+#endif
+#ifdef SIGTERM
+	Signame[SIGTERM] = "sigterm";
+#endif
+#ifdef SIGHUP
+	Signame[SIGHUP] = "sighup";
+#endif
+#ifdef SIGQUIT
+	Signame[SIGQUIT] = "sigquit";
+#endif
+#ifdef SIGPIPE
+	Signame[SIGPIPE] = "sigpipe";
+#endif
+#ifdef SIGUSR1
+	Signame[SIGUSR1] = "sigusr1";
+#endif
+#ifdef SIGUSR2
+	Signame[SIGUSR2] = "sigusr2";
+#endif
+#ifdef SIGBUS
+	Signame[SIGBUS] = "sigbus";
+#endif
+#ifdef SIGWINCH
+	Signame[SIGWINCH] = "sigwinch";
+#endif
+
+	for(i=1; i<NSIG; i++) if(Signame[i]){
+#ifdef SA_RESTART
+		struct sigaction a;
+
+		sigaction(i, NULL, &a);
+		a.sa_flags &= ~SA_RESTART;
+		a.sa_handler = sighandler;
+		sigaction(i, &a, NULL);
+#else
+		signal(i, sighandler);
+#endif
+	}
+}
+
+long
+Write(int fd, void *buf, long cnt)
+{
+	return write(fd, buf, cnt);
+}
+
+long
+Read(int fd, void *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
+Open(char *file, int mode)
+{
+	static int tab[] = {OREAD,OWRITE,ORDWR,OREAD|ORCLOSE};
+	return open(file, tab[mode&3]);
+}
+
+void
+Close(int fd)
+{
+	close(fd);
+}
+
+int
+Creat(char *file)
+{
+	return create(file, OWRITE, 0666L);
+}
+
+int
+Dup(int a, int b)
+{
+	return dup(a, b);
+}
+
+int
+Dup1(int a)
+{
+	return dup(a, -1);
+}
+
+void
+Exit(void)
+{
+	Updenv();
+	exits(truestatus()?"":getstatus());
+}
+
+void
+Noerror(void)
+{
+	errno = 0;
+}
+
+int
+Isatty(int fd)
+{
+	return isatty(fd);
+}
+
+void
+Abort(void)
+{
+	abort();
+}
+
+int
+Chdir(char *dir)
+{
+	return chdir(dir);
+}
+
+void
+Prompt(char *s)
+{
+	pstr(err, s);
+	flushio(err);
+}
--- /dev/null
+++ b/rc/exec.c
@@ -1,0 +1,1172 @@
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+#include "drawcpu.h"
+
+#include <errno.h>
+
+char *argv0="rc";
+io *err;
+int mypid;
+thread *runq;
+
+/*
+ * Start executing the given code at the given pc with the given redirection
+ */
+void
+start(code *c, int pc, var *local, redir *redir)
+{
+	thread *p = new(thread);
+	p->code = codecopy(c);
+	p->line = 0;
+	p->pc = pc;
+	p->argv = 0;
+	p->redir = p->startredir = redir;
+	p->lex = 0;
+	p->local = local;
+	p->iflag = 0;
+	p->pid = 0;
+	p->status = 0;
+	p->ret = runq;
+	runq = p;
+}
+
+void
+startfunc(var *func, word *starval, var *local, redir *redir)
+{
+	start(func->fn, func->pc, local, redir);
+	runq->local = newvar("*", runq->local);
+	runq->local->val = starval;
+	runq->local->changed = 1;
+}
+
+static void
+popthread(void)
+{
+	thread *p = runq;
+	while(p->argv) poplist();
+	while(p->local && (p->ret==0 || p->local!=p->ret->local))
+		Xunlocal();
+	runq = p->ret;
+	if(p->lex) freelexer(p->lex);
+	codefree(p->code);
+	free(p->status);
+	free(p);
+}
+
+word*
+Newword(char *s, word *next)
+{
+	word *p=new(word);
+	p->word = s;
+	p->next = next;
+	return p;
+}
+word*
+newword(char *s, word *next)
+{
+	return Newword(estrdup(s), next);
+}
+word*
+Pushword(char *s)
+{
+	word *p;
+	if(s==0)
+		panic("null pushword", 0);
+	if(runq->argv==0)
+		panic("pushword but no argv!", 0);
+	p = Newword(s, runq->argv->words);
+	runq->argv->words = p;
+	return p;
+}
+word*
+pushword(char *s)
+{
+	return Pushword(estrdup(s));
+}
+char*
+Freeword(word *p)
+{
+	char *s = p->word;
+	free(p);
+	return s;
+}
+void
+freewords(word *w)
+{
+	word *p;
+	while((p = w)!=0){
+		w = w->next;
+		free(Freeword(p));
+	}
+}
+char*
+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;
+	return Freeword(p);
+}
+void
+popword(void)
+{
+	free(Popword());
+}
+
+void
+pushlist(void)
+{
+	list *p = new(list);
+	p->words = 0;
+	p->next = runq->argv;
+	runq->argv = p;
+}
+word*
+Poplist(void)
+{
+	word *w;
+	list *p = runq->argv;
+	if(p==0)
+		panic("poplist but no argv", 0);
+	w = p->words;
+	runq->argv = p->next;
+	free(p);
+	return w;
+}
+void
+poplist(void)
+{
+	freewords(Poplist());
+}
+
+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;
+}
+
+static void
+dontclose(int fd)
+{
+	redir *rp;
+
+	if(fd<0)
+		return;
+	for(rp = runq->redir; rp != runq->startredir; rp = rp->next){
+		if(rp->type == RCLOSE && rp->from == fd){
+			rp->type = 0;
+			break;
+		}
+	}
+}
+
+/*
+ * we are about to start a new thread that should exit on
+ * return, so the current stack is not needed anymore.
+ * free all the threads and lexers, but preserve the
+ * redirections and anything referenced by local.
+ */
+void
+turfstack(var *local)
+{
+	while(local){
+		thread *p;
+
+		for(p = runq; p && p->local == local; p = p->ret)
+			p->local = local->next;
+		local = local->next;
+	}
+	while(runq) {
+		if(runq->lex) dontclose(runq->lex->input->fd);
+		popthread();
+	}
+}
+
+void
+shuffleredir(void)
+{
+	redir **rr, *rp;
+
+	rp = runq->redir;
+	if(rp==0)
+		return;
+	runq->redir = rp->next;
+	rp->next = runq->startredir;
+	for(rr = &runq->redir; *rr != rp->next; rr = &((*rr)->next))
+		;
+	*rr = rp;
+}
+
+/*
+ * get command line flags, initialize keywords & traps.
+ * get values from environment.
+ * set $pid, $cflag, $*
+ * fabricate bootstrap code and start it (*=(argv);. -bq /usr/lib/rcmain $*)
+ * start interpreting code
+ */
+void
+runcommand(int argc, char **argv)
+{
+	code bootstrap[20];
+	char num[12];
+	char *rcmain=Rcmain;
+
+	int i, fd;
+	argv0 = argv[0];
+	argc = getflags(argc, argv, "srdiIlxebpvVc: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;
+	if(flag['m']) rcmain = flag['m'][0];
+
+	fd = open("/dev/cons", ORDWR);
+	err = open("/dev/cons", OWRITE);
+	close(fd);
+	kinit();
+	Trapinit();
+	Vinit();
+	inttoascii(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));
+	bootstrap[0].i = 1;
+	bootstrap[1].s="*bootstrap*";
+	bootstrap[2].f = Xmark;
+	bootstrap[3].f = Xword;
+	bootstrap[4].s="*";
+	bootstrap[5].f = Xassign;
+	bootstrap[6].f = Xmark;
+	bootstrap[7].f = Xmark;
+	bootstrap[8].f = Xword;
+	bootstrap[9].s="*";
+	bootstrap[10].f = Xdol;
+	bootstrap[11].f = Xword;
+	bootstrap[12].s = rcmain;
+	bootstrap[13].f = Xword;
+	bootstrap[14].s="-bq";
+	bootstrap[15].f = Xword;
+	bootstrap[16].s=".";
+	bootstrap[17].f = Xsimple;
+	bootstrap[18].f = Xexit;
+	bootstrap[19].f = 0;
+	start(bootstrap, 2, (var*)0, (redir*)0);
+
+	/* prime bootstrap argv */
+	pushlist();
+	for(i = argc-1;i!=0;--i) pushword(argv[i]);
+	for(;;){
+		if(flag['r'])
+			pfnc(err, runq);
+		(*runq->code[runq->pc++].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(split){... 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
+ * Xdol(name)				get variable value
+ * 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
+ * Xglob(list)				glob a list of words inplace
+ * 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
+ * Xpush(words)				push words down a list
+ * Xqw(words)				quote words inplace
+ * Xrdwr(file)[fd]			open file for reading and writing
+ * Xread(file)[fd]			open file to read
+ * Xreturn				kill thread
+ * Xsimple(args)			run command and wait
+ * Xsrcline[line]			set current source line number
+ * 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 fd;
+
+	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((fd = Open(file, 1))<0 && (fd = Creat(file))<0){
+		Xerror3(">> can't open", file, Errstr());
+		return;
+	}
+	Seek(fd, 0L, 2);
+	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
+	poplist();
+}
+
+void
+Xsettrue(void)
+{
+	setstatus("");
+}
+
+void
+Xbang(void)
+{
+	setstatus(truestatus()?"false":"");
+}
+
+void
+Xclose(void)
+{
+	pushredir(RCLOSE, runq->code[runq->pc++].i, 0);
+}
+
+void
+Xdup(void)
+{
+	pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
+	runq->pc+=2;
+}
+
+void
+Xeflag(void)
+{
+	if(!truestatus()) Xexit();
+}
+
+void
+Xexit(void)
+{
+	static int beenhere = 0;
+
+	if(getpid()==mypid && !beenhere){
+		var *trapreq = vlook("sigexit");
+		word *starval = vlook("*")->val;
+		if(trapreq->fn){
+			beenhere = 1;
+			--runq->pc;
+			startfunc(trapreq, copywords(starval, (word*)0), (var*)0, (redir*)0);
+			return;
+		}
+	}
+	Exit();
+}
+
+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
+Xpush(void)
+{
+	word *t, *h = Poplist();
+	for(t = h; t->next; t = t->next)
+		;
+	t->next = runq->argv->words;
+	runq->argv->words = h;
+}
+
+static int
+herefile(char *tmp)
+{
+	char *s = tmp+strlen(tmp)-1;
+	static int ser;
+	int fd, i;
+
+	i = ser++;
+	while(*s == 'Y'){
+		*s-- = (i%26) + 'A';
+		i = i/26;
+	}
+	i = getpid();
+	while(*s == 'X'){
+		*s-- = (i%10) + '0';
+		i = i/10;
+	}
+	s++;
+	for(i='a'; i<'z'; i++){
+		if(access(tmp, 0)!=0 && (fd = Creat(tmp))>=0)
+			return fd;
+		*s = i;
+	}
+	return -1;
+}
+
+void
+Xhere(void)
+{
+	char file[]="/tmp/hereXXXXXXXXXXYY";
+	int fd;
+	io *io;
+
+	if((fd = herefile(file))<0){
+		Xerror3("<< can't get temp file", file, Errstr());
+		return;
+	}
+	io = openiofd(fd);
+	psubst(io, (unsigned char*)runq->code[runq->pc++].s);
+	flushio(io);
+	closeio(io);
+
+	/* open for reading and unlink */
+	if((fd = Open(file, 3))<0){
+		Xerror3("<< can't open", file, Errstr());
+		return;
+	}
+	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
+}
+
+void
+Xhereq(void)
+{
+	char file[]="/tmp/hereXXXXXXXXXXYY", *body;
+	int fd;
+
+	if((fd = herefile(file))<0){
+		Xerror3("<< can't get temp file", file, Errstr());
+		return;
+	}
+	body = runq->code[runq->pc++].s;
+	Write(fd, body, strlen(body));
+	Close(fd);
+
+	/* open for reading and unlink */
+	if((fd = Open(file, 3))<0){
+		Xerror3("<< can't open", file, Errstr());
+		return;
+	}
+	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
+}
+
+void
+Xread(void)
+{
+	char *file;
+	int fd;
+
+	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((fd = Open(file, 0))<0){
+		Xerror3("< can't open", file, Errstr());
+		return;
+	}
+	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
+	poplist();
+}
+
+void
+Xrdwr(void)
+{
+	char *file;
+	int fd;
+
+	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((fd = Open(file, 2))<0){
+		Xerror3("<> can't open", file, Errstr());
+		return;
+	}
+	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
+	poplist();
+}
+
+void
+Xpopredir(void)
+{
+	redir *rp = runq->redir;
+
+	if(rp==0)
+		panic("Xpopredir null!", 0);
+	runq->redir = rp->next;
+	if(rp->type==ROPEN)
+		Close(rp->from);
+	free(rp);
+}
+
+void
+Xreturn(void)
+{
+	while(runq->redir!=runq->startredir)
+		Xpopredir();
+	popthread();
+	if(runq==0)
+		Exit();
+}
+
+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 fd;
+
+	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((fd = Creat(file))<0){
+		Xerror3("> can't create", file, Errstr());
+		return;
+	}
+	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
+	poplist();
+}
+
+void
+Xmatch(void)
+{
+	word *p;
+	char *s;
+
+	setstatus("no match");
+	s = runq->argv->words->word;
+	for(p = runq->argv->next->words;p;p = p->next)
+		if(match(s, p->word, '\0')){
+			setstatus("");
+			break;
+		}
+	poplist();
+	poplist();
+}
+
+void
+Xcase(void)
+{
+	word *p;
+	char *s;
+	int ok = 0;
+
+	s = runq->argv->next->words->word;
+	for(p = runq->argv->words;p;p = p->next){
+		if(match(s, p->word, '\0')){
+			ok = 1;
+			break;
+		}
+	}
+	if(ok)
+		runq->pc++;
+	else
+		runq->pc = runq->code[runq->pc].i;
+	poplist();
+}
+
+static word*
+conclist(word *lp, word *rp, word *tail)
+{
+	word *v, *p, **end;
+	int ln, rn;
+
+	for(end = &v;;){
+		ln = strlen(lp->word), rn = strlen(rp->word);
+		p = Newword(emalloc(ln+rn+1), (word *)0);
+		memmove(p->word, lp->word, ln);
+		memmove(p->word+ln, rp->word, rn+1);
+		*end = p, end = &p->next;
+		if(lp->next == 0 && rp->next == 0)
+			break;
+		if(lp->next) lp = lp->next;
+		if(rp->next) rp = rp->next;
+	}
+	*end = tail;
+	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;
+	}
+	v = vlook(runq->argv->words->word);
+	poplist();
+	freewords(v->val);
+	v->val = Poplist();
+	v->changed = 1;
+}
+
+/*
+ * 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;
+	}
+	n = 0;
+	s = runq->argv->words->word;
+	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
+Xqw(void)
+{
+	char *s, *d;
+	word *a, *p;
+	int n;
+
+	a = runq->argv->words;
+	if(a==0){
+		pushword("");
+		return;
+	}
+	if(a->next==0)
+		return;
+	n=0;
+	for(p=a;p;p=p->next)
+		n+=1+strlen(p->word);
+	s = emalloc(n+1);
+	d = s;
+	d += strlen(strcpy(d, a->word));
+	for(p=a->next;p;p=p->next){
+		*d++=' ';
+		d += strlen(strcpy(d, p->word));
+	}
+	free(a->word);
+	freewords(a->next);
+	a->word = s;
+	a->next = 0;
+}
+
+static word*
+copynwords(word *a, word *tail, int n)
+{
+	word *v, **end;
+	
+	v = 0;
+	end = &v;
+	while(n-- > 0){
+		*end = newword(a->word, 0);
+		end = &(*end)->next;
+		a = a->next;
+	}
+	*end = tail;
+	return v;
+}
+
+static word*
+subwords(word *val, int len, word *sub, word *a)
+{
+	int n, m;
+	char *s;
+
+	if(sub==0)
+		return a;
+	a = subwords(val, len, sub->next, a);
+	s = sub->word;
+	m = 0;
+	n = 0;
+	while('0'<=*s && *s<='9')
+		n = n*10+ *s++ -'0';
+	if(*s == '-'){
+		if(*++s == 0)
+			m = len - n;
+		else{
+			while('0'<=*s && *s<='9')
+				m = m*10+ *s++ -'0';
+			m -= n;
+		}
+	}
+	if(n<1 || n>len || m<0)
+		return a;
+	if(n+m>len)
+		m = len-n;
+	while(--n > 0)
+		val = val->next;
+	return copynwords(val, a, m+1);
+}
+
+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;
+	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, num[12];
+	int n;
+
+	if(count(runq->argv->words)!=1){
+		Xerror1("$# variable name not singleton!");
+		return;
+	}
+	n = 0;
+	s = runq->argv->words->word;
+	for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
+	if(n==0 || *t){
+		a = vlook(s)->val;
+		inttoascii(num, count(a));
+	}
+	else{
+		a = vlook("*")->val;
+		inttoascii(num, a && 1<=n && n<=count(a)?1:0);
+	}
+	poplist();
+	pushword(num);
+}
+
+void
+Xlocal(void)
+{
+	if(count(runq->argv->words)!=1){
+		Xerror1("local variable name must be singleton");
+		return;
+	}
+	runq->local = newvar(runq->argv->words->word, runq->local);
+	poplist();
+	runq->local->val = Poplist();
+	runq->local->changed = 1;
+}
+
+void
+Xunlocal(void)
+{
+	var *hid, *v = runq->local;
+	if(v==0)
+		panic("Xunlocal: no locals!", 0);
+	runq->local = v->next;
+	hid = vlook(v->name);
+	hid->changed = 1;
+	freevar(v);
+}
+
+void
+Xfn(void)
+{
+	var *v;
+	word *a;
+	int pc = runq->pc;
+	runq->pc = runq->code[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 = pc+2;
+		v->fnchanged = 1;
+	}
+	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();
+}
+
+static char*
+concstatus(char *s, char *t)
+{
+	int n, m;
+
+	if(t==0) return s;
+	if(s==0) return t;
+	n = strlen(s);
+	m = strlen(t);
+	s = erealloc(s, n+m+2);
+	if(n > 0) s[n++]='|';
+	memmove(s+n, t, m+1);
+	free(t);
+	return s;
+}
+
+void
+Xpipewait(void)
+{
+	char *old = Getstatus();
+	if(runq->pid==-1){
+		Setstatus(concstatus(runq->status, old));
+		runq->status=0;
+	}else{
+		while(Waitfor(runq->pid) < 0)
+			;
+		runq->pid=-1;
+		Setstatus(concstatus(Getstatus(), old));
+	}
+}
+
+static char *promptstr;
+
+void
+Xrdcmds(void)
+{
+	thread *p = runq;
+
+	if(flag['s'] && !truestatus())
+		pfmt(err, "status=%v\n", vlook("status")->val);
+	flushio(err);
+
+	lex = p->lex;
+	if(p->iflag){
+		word *prompt = vlook("prompt")->val;
+		if(prompt)
+			promptstr = prompt->word;
+		else
+			promptstr="% ";
+	}
+	Noerror();
+	nerror = 0;
+	if(yyparse()){
+		if(p->iflag && (!lex->eof || errno==EINTR)){
+			if(errno==EINTR){
+				pchr(err, '\n');
+				lex->eof = 0;
+			}
+			--p->pc;	/* go back for next command */
+		}
+	}
+	else{
+		if(lex->eof){
+			dontclose(lex->input->fd);
+			freelexer(lex);
+			p->lex = 0;
+		} else
+			--p->pc;	/* re-execute Xrdcmds after codebuf runs */
+		start(codebuf, 2, p->local, p->redir);
+	}
+	lex = 0;
+	freenodes();
+}
+
+void
+pprompt(void)
+{
+	word *prompt;
+
+	if(!runq->iflag)
+		return;
+
+	Prompt(promptstr);
+	doprompt = 0;
+
+	prompt = vlook("prompt")->val;
+	if(prompt && prompt->next)
+		promptstr = prompt->next->word;
+	else
+		promptstr = "\t";
+}
+
+char*
+srcfile(thread *p)
+{
+	return p->code[1].s;
+}
+
+void
+Xerror1(char *s)
+{
+	setstatus("error");
+	pfln(err, srcfile(runq), runq->line);
+	pfmt(err, ": %s\n", s);
+	flushio(err);
+	while(!runq->iflag) Xreturn();
+}
+void
+Xerror2(char *s, char *e)
+{
+	setstatus(e);
+	pfln(err, srcfile(runq), runq->line);
+	pfmt(err, ": %s: %s\n", s, e);
+	flushio(err);
+	while(!runq->iflag) Xreturn();
+}
+void
+Xerror3(char *s, char *m, char *e)
+{
+	setstatus(e);
+	pfln(err, srcfile(runq), runq->line);
+	pfmt(err, ": %s: %s: %s\n", s, m, e);
+	flushio(err);
+	while(!runq->iflag) Xreturn();
+}
+
+void
+Setstatus(char *s)
+{
+	setvar("status", Newword(s?s:estrdup(""), (word *)0));
+}
+void
+setstatus(char *s)
+{
+	Setstatus(estrdup(s));
+}
+char*
+Getstatus(void)
+{
+	var *status = vlook("status");
+	word *val = status->val;
+	if(val==0) return 0;
+	status->val=0;
+	status->changed=1;
+	freewords(val->next);
+	return Freeword(val);
+}
+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
+Xfor(void)
+{
+	word *a = runq->argv->words;
+	if(a==0){
+		poplist();
+		runq->pc = runq->code[runq->pc].i;
+	}
+	else{
+		runq->argv->words = a->next;
+		a->next = 0;
+		freewords(runq->local->val);
+		runq->local->val = a;
+		runq->local->changed = 1;
+		runq->pc++;
+	}
+}
+
+void
+Xglob(void)
+{
+	word *a, *x;
+
+	for(a = runq->argv->words; a; a = x){
+		x = a->next;
+		globword(a);
+	}
+}
+
+void
+Xsrcline(void)
+{
+	runq->line = runq->code[runq->pc++].i;
+}
--- /dev/null
+++ b/rc/exec.h
@@ -1,0 +1,85 @@
+/*
+ * 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), Xqw(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), Xhere(void), Xhereq(void);
+extern void Xrdwr(void), Xsrcline(void);
+extern 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), Xpush(void);
+extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void);
+extern void Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void);
+extern void Xerror1(char*);
+extern void Xerror2(char*,char*);
+extern void Xerror3(char*,char*,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{
+	int type;			/* what to do */
+	int from, to;			/* what to do it to */
+	redir *next;			/* what else to do (reverse order) */
+};
+
+/*
+ * redir types
+ */
+#define	ROPEN	1			/* dup2(from, to); close(from); */
+#define	RDUP	2			/* dup2(from, to); */
+#define	RCLOSE	3			/* close(from); */
+void	shuffleredir(void);
+
+struct thread{
+	code *code;			/* code for this thread */
+	int pc;				/* code[pc] is the next instruction */
+	int line;			/* source code line for Xsrcline */
+	list *argv;			/* argument stack */
+	redir *redir;			/* redirection stack */
+	redir *startredir;		/* redir inheritance point */
+	var *local;			/* list of local variables */
+	lexer *lex;			/* lexer for Xrdcmds */
+	int iflag;			/* interactive? */
+	int pid;			/* process for Xpipewait to wait for */
+	char *status;			/* status for Xpipewait */
+	thread *ret;			/* who continues when this finishes */
+};
+extern thread *runq;
+void turfstack(var*);
+
+extern int mypid;
+extern int ntrap;			/* number of outstanding traps */
+extern int trap[NSIG];			/* number of outstanding traps per type */
+
+code *codecopy(code*);
+extern code *codebuf;			/* compiler output */
+extern int ifnot;
+
+struct builtin{
+	char *name;
+	void (*fnc)(void);
+};
+extern void (*builtinfunc(char *name))(void);
+
+void execcd(void), execwhatis(void), execeval(void), execexec(void);
+int execforkexec(void);
+void execexit(void), execshift(void);
+void execwait(void), execumask(void), execdot(void), execflag(void);
+void execfunc(var*), execcmds(io*, char*, var*, redir*);
+void startfunc(var*, word*, var*, redir*);
+
+char *srcfile(thread*);
+char *getstatus(void);
--- /dev/null
+++ b/rc/fns.h
@@ -1,0 +1,69 @@
+void	Abort(void);
+int	Chdir(char*);
+void	Close(int);
+void	Closedir(void*);
+int	Creat(char*);
+int	Dup(int, int);
+int	Dup1(int);
+int	Executable(char*);
+void	Exec(char**);
+void	Exit(void);
+char*	Errstr(void);
+char*	Freeword(word*);
+int	Fork(void);
+char*	Getstatus(void);
+int	Isatty(int);
+word*	Newword(char*,word*);
+void	Noerror(void);
+int	Open(char*, int);
+void*	Opendir(char*);
+word*	Poplist(void);
+char*	Popword(void);
+word*	Pushword(char*);
+long	Read(int, void*, long);
+char*	Readdir(void*, int);
+long	Seek(int, long, long);
+void	Setstatus(char*);
+void	Trapinit(void);
+void	Updenv(void);
+void	Vinit(void);
+int	Waitfor(int);
+long	Write(int, void*, long);
+void	addwaitpid(int);
+void	clearwaitpids(void);
+void	codefree(code*);
+int	compile(tree*);
+int	count(word*);
+char*	deglob(char*);
+void	delwaitpid(int);
+void	dotrap(void);
+void	freenodes(void);
+void	freewords(word*);
+void	globword(word*);
+int	havewaitpid(int);
+int	idchr(int);
+void	inttoascii(char*, int);
+void	kinit(void);
+int	mapfd(int);
+int	match(char*, char*, int);
+char*	makercpath(char*, char*);
+void	pfln(io*, char*, int);
+void	poplist(void);
+void	popword(void);
+void	pprompt(void);
+void	Prompt(char*);
+void	psubst(io*, unsigned char*);
+void	pushlist(void);
+void	pushredir(int, int, int);
+word*	pushword(char*);
+void	readhere(io*);
+void	heredoc(tree*);
+void	setstatus(char*);
+void	skipnl(void);
+void	start(code*, int, var*, redir*);
+int	truestatus(void);
+void	usage(char*);
+int	wordchr(int);
+void	yyerror(char*);
+int	yylex(void);
+int	yyparse(void);
--- /dev/null
+++ b/rc/getflags.c
@@ -1,0 +1,234 @@
+#include "rc.h"
+#include "getflags.h"
+#include "fns.h"
+char *flagset[] = {"<flag>"};
+char **flag[NFLAG];
+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;
+	int i, j, c, count;
+	flagarg = flags;
+	if(cmdname==0)
+		cmdname = argv[0];
+
+	i = 1;
+	while(i!=argc){
+		if(argv[i][0] != '-' || argv[i][1] == '\0'){
+			if(stop)		/* always true in rc */
+				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");
+	setstatus("bad flags");
+	Exit();
+}
+
+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;
+	}
+}
--- /dev/null
+++ b/rc/getflags.h
@@ -1,0 +1,7 @@
+#define	NFLAG	128
+
+extern char **flag[NFLAG];
+extern char *cmdname;
+extern char *flagset[];
+
+int getflags(int, char*[], char*, int);
--- /dev/null
+++ b/rc/glob.c
@@ -1,0 +1,259 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+
+/*
+ * delete all the GLOB marks from s, in place
+ */
+char*
+deglob(char *s)
+{
+	char *r = strchr(s, GLOB);
+	if(r){
+		char *w = r++;
+		do{
+			if(*r==GLOB)
+				r++;
+			*w++=*r;
+		}while(*r++);
+	}
+	return s;
+}
+
+static int
+globcmp(const void *s, const void *t)
+{
+	return strcmp(*(char**)s, *(char**)t);
+}
+
+static 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((void *)list, n, sizeof(void *), globcmp);
+	for(a = left,n = 0;a!=right;a = a->next,n++) a->word = list[n];
+	free(list);
+}
+
+/*
+ * 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
+ */
+
+static 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, '/');
+}
+
+static void
+pappend(char **pdir, char *name)
+{
+	char *path = makercpath(*pdir, name);
+	free(*pdir);
+	*pdir = path;
+}
+
+static word*
+globdir(word *list, char *pattern, char *name)
+{
+	char *slash, *glob, *entry;
+	void *dir;
+
+#ifdef Plan9
+	/* append slashes, Readdir() already filtered directories */
+	while(*pattern=='/'){
+		pappend(&name, "/");
+		pattern++;
+	}
+#endif
+	if(*pattern=='\0')
+		return Newword(name, list);
+
+	/* scan the pattern looking for a component with a metacharacter in it */
+	glob=strchr(pattern, GLOB);
+
+	/* If we ran out of pattern, append the name if accessible */
+	if(glob==0){
+		pappend(&name, pattern);
+		if(access(name, 0)==0)
+			return Newword(name, list);
+		goto out;
+	}
+
+	*glob='\0';
+	slash=strrchr(pattern, '/');
+	if(slash){
+		*slash='\0';
+		pappend(&name, pattern);
+		*slash='/';
+		pattern=slash+1;
+	}
+	*glob=GLOB;
+
+	/* read the directory and recur for any entry that matches */
+	dir = Opendir(name[0]?name:".");
+	if(dir==0)
+		goto out;
+	slash=strchr(glob, '/');
+	while((entry=Readdir(dir, slash!=0)) != 0){
+		if(matchfn(entry, pattern))
+			list = globdir(list, slash?slash:"", makercpath(name, entry));
+	}
+	Closedir(dir);
+out:
+	free(name);
+	return list;
+}
+
+/*
+ * Subsitute a word with its glob in place.
+ */
+void
+globword(word *w)
+{
+	word *left, *right;
+
+	if(w==0 || strchr(w->word, GLOB)==0)
+		return;
+	right = w->next;
+	left = globdir(right, w->word, estrdup(""));
+	if(left == right) {
+		deglob(w->word);
+	} else {
+		free(w->word);
+		globsort(left, right);
+		w->next = left->next;
+		w->word = Freeword(left);
+	}
+}
+
+/*
+ * Return a pointer to the next utf code in the string,
+ * not jumping past nuls in broken utf codes!
+ */
+static char*
+nextutf(char *p)
+{
+	int i, n, c = *p;
+
+	if(onebyte(c))
+		return p+1;
+	if(twobyte(c))
+		n = 2;
+	else if(threebyte(c))
+		n = 3;
+	else
+		n = 4;
+	for(i = 1; i < n; i++)
+		if(!xbyte(p[i]))
+			break;
+	return p+i;
+}
+
+/*
+ * Convert the utf code at *p to a unicode value
+ */
+static int
+unicode(char *p)
+{
+	int c = *p;
+
+	if(onebyte(c))
+		return c&0xFF;
+	if(twobyte(c)){
+		if(xbyte(p[1]))
+			return ((c&0x1F)<<6) | (p[1]&0x3F);
+	} else if(threebyte(c)){
+		if(xbyte(p[1]) && xbyte(p[2]))
+			return ((c&0x0F)<<12) | ((p[1]&0x3F)<<6) | (p[2]&0x3F);
+	} else if(fourbyte(c)){
+		if(xbyte(p[1]) && xbyte(p[2]) && xbyte(p[3]))
+			return ((c&0x07)<<18) | ((p[1]&0x3F)<<12) | ((p[2]&0x3F)<<6) | (p[3]&0x3F);
+	}
+	return -1;
+}
+
+/*
+ * Do p and q point at equal utf codes
+ */
+static int
+equtf(char *p, char *q)
+{
+	if(*p!=*q)
+ 		return 0;
+	return unicode(p) == unicode(q);
+}
+
+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';
+}
--- /dev/null
+++ b/rc/havefork.c
@@ -1,0 +1,240 @@
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+static int *waitpids;
+static int nwaitpids;
+
+void
+addwaitpid(int pid)
+{
+	waitpids = erealloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]);
+	waitpids[nwaitpids++] = pid;
+}
+
+void
+delwaitpid(int pid)
+{
+	int r, w;
+	
+	for(r=w=0; r<nwaitpids; r++)
+		if(waitpids[r] != pid)
+			waitpids[w++] = waitpids[r];
+	nwaitpids = w;
+}
+
+void
+clearwaitpids(void)
+{
+	nwaitpids = 0;
+}
+
+int
+havewaitpid(int pid)
+{
+	int i;
+
+	for(i=0; i<nwaitpids; i++)
+		if(waitpids[i] == pid)
+			return 1;
+	return 0;
+}
+
+void
+Xasync(void)
+{
+	int pid;
+	char npid[10];
+
+	switch(pid = Fork()){
+	case -1:
+		Xerror2("try again", Errstr());
+		break;
+	case 0:
+		clearwaitpids();
+		start(runq->code, runq->pc+1, runq->local, runq->redir);
+		runq->ret = 0;
+		break;
+	default:
+		addwaitpid(pid);
+		runq->pc = runq->code[runq->pc].i;
+		inttoascii(npid, pid);
+		setvar("apid", newword(npid, (word *)0));
+		break;
+	}
+}
+
+void
+Xpipe(void)
+{
+	thread *p = runq;
+	int pid, pc = p->pc;
+	int lfd = p->code[pc++].i;
+	int rfd = p->code[pc++].i;
+	int pfd[2];
+
+	if(pipe(pfd)<0){
+		Xerror2("can't get pipe", Errstr());
+		return;
+	}
+	switch(pid = Fork()){
+	case -1:
+		Xerror2("try again", Errstr());
+		break;
+	case 0:
+		clearwaitpids();
+		Close(pfd[PRD]);
+		start(p->code, pc+2, runq->local, runq->redir);
+		runq->ret = 0;
+		pushredir(ROPEN, pfd[PWR], lfd);
+		break;
+	default:
+		addwaitpid(pid);
+		Close(pfd[PWR]);
+		start(p->code, p->code[pc].i, runq->local, runq->redir);
+		pushredir(ROPEN, pfd[PRD], rfd);
+		p->pc = p->code[pc+1].i;
+		p->pid = pid;
+		break;
+	}
+}
+
+/*
+ * Who should wait for the exit from the fork?
+ */
+
+void
+Xbackq(void)
+{
+	int pid, pfd[2];
+	char *s, *split;
+	word *end, **link;
+	io *f;
+
+	if(pipe(pfd)<0){
+		Xerror2("can't make pipe", Errstr());
+		return;
+	}
+	switch(pid = Fork()){
+	case -1:
+		Xerror2("try again", Errstr());
+		Close(pfd[PRD]);
+		Close(pfd[PWR]);
+		return;
+	case 0:
+		clearwaitpids();
+		Close(pfd[PRD]);
+		start(runq->code, runq->pc+1, runq->local, runq->redir);
+		pushredir(ROPEN, pfd[PWR], 1);
+		return;
+	default:
+		addwaitpid(pid);
+		Close(pfd[PWR]);
+
+		split = Popword();
+		poplist();
+		f = openiofd(pfd[PRD]);
+		end = runq->argv->words;
+		link = &runq->argv->words;
+		while((s = rstr(f, split)) != 0){
+			*link = Newword(s, (word*)0);
+			link = &(*link)->next;
+		}
+		*link = end;
+		closeio(f);
+		free(split);
+
+		Waitfor(pid);
+
+		runq->pc = runq->code[runq->pc].i;
+		return;
+	}
+}
+
+void
+Xpipefd(void)
+{
+	thread *p = runq;
+	int pid, pc = p->pc;
+	char name[40];
+	int pfd[2];
+	int sidefd, mainfd;
+
+	if(pipe(pfd)<0){
+		Xerror2("can't get pipe", Errstr());
+		return;
+	}
+	if(p->code[pc].i==READ){
+		sidefd = pfd[PWR];
+		mainfd = pfd[PRD];
+	}
+	else{
+		sidefd = pfd[PRD];
+		mainfd = pfd[PWR];
+	}
+	switch(pid = Fork()){
+	case -1:
+		Xerror2("try again", Errstr());
+		break;
+	case 0:
+		clearwaitpids();
+		Close(mainfd);
+		start(p->code, pc+2, runq->local, runq->redir);
+		pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0);
+		runq->ret = 0;
+		break;
+	default:
+		addwaitpid(pid);
+		Close(sidefd);
+		pushredir(ROPEN, mainfd, mainfd);
+		shuffleredir();	/* shuffle redir to bottom of stack for Xpopredir() */
+		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:
+		Xerror2("try again", Errstr());
+		break;
+	case 0:
+		clearwaitpids();
+		start(runq->code, runq->pc+1, runq->local, runq->redir);
+		runq->ret = 0;
+		break;
+	default:
+		addwaitpid(pid);
+		while(Waitfor(pid) < 0)
+			;
+		runq->pc = runq->code[runq->pc].i;
+		break;
+	}
+}
+
+int
+execforkexec(void)
+{
+	int pid;
+
+	switch(pid = Fork()){
+	case -1:
+		return -1;
+	case 0:
+		clearwaitpids();
+		pushword("exec");
+		execexec();
+		/* does not return */
+	}
+	addwaitpid(pid);
+	return pid;
+}
--- /dev/null
+++ b/rc/here.c
@@ -1,0 +1,137 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+void psubst(io*, unsigned char*);
+void pstrs(io*, word*);
+
+static char*
+readhere1(tree *tag, io *in)
+{
+	io *out;
+	char c, *m;
+
+	pprompt();
+	out = openiostr();
+	m = tag->str;
+	while((c = rchr(in)) != EOF){
+		if(c=='\0'){
+			yyerror("NUL bytes in here doc");
+			closeio(out);
+			return 0;
+		}
+		if(c=='\n'){
+			lex->line++;
+			if(m && *m=='\0'){
+				out->bufp -= m - tag->str;
+				*out->bufp='\0';
+				break;
+			}
+			pprompt();
+			m = tag->str;
+		} else if(m){
+			if(*m == c){
+				m++;
+			} else {
+				m = 0;
+			}
+		}
+		pchr(out, c);
+	}
+	doprompt = 1;
+	return closeiostr(out);
+}
+
+static tree *head, *tail;
+
+void
+heredoc(tree *redir)
+{
+	if(redir->child[0]->type!=WORD){
+		yyerror("Bad here tag");
+		return;
+	}
+	redir->child[2]=0;
+	if(head)
+		tail->child[2]=redir;
+	else
+		head=redir;
+	tail=redir;
+}
+
+void
+readhere(io *in)
+{
+	while(head){
+		tail=head->child[2];
+		head->child[2]=0;
+		head->str=readhere1(head->child[0], in);
+		head=tail;
+	}
+}
+
+void
+psubst(io *f, unsigned char *s)
+{
+	unsigned char *t, *u;
+	word *star;
+	int savec, n;
+
+	while(*s){
+		if(*s!='$'){
+			if(0xa0 <= *s && *s <= 0xf5){
+				pchr(f, *s++);
+				if(*s=='\0')
+					break;
+			}
+			else if(0xf6 <= *s && *s <= 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((char *)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);
+	}
+}
--- /dev/null
+++ b/rc/io.c
@@ -1,0 +1,302 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+enum {
+	NBUF = 8192,
+};
+
+void
+vpfmt(io *f, char *fmt, va_list ap)
+{
+	for(;*fmt;fmt++) {
+		if(*fmt!='%') {
+			pchr(f, *fmt);
+			continue;
+		}
+		if(*++fmt == '\0')		/* "blah%"? */
+			break;
+		switch(*fmt){
+		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':
+			pptr(f, va_arg(ap, void*));
+			break;
+		case 'Q':
+			pquo(f, va_arg(ap, char *));
+			break;
+		case 'q':
+			pwrd(f, va_arg(ap, char *));
+			break;
+		case 's':
+			pstr(f, va_arg(ap, char *));
+			break;
+		case 't':
+			pcmd(f, va_arg(ap, tree *));
+			break;
+		case 'v':
+			pval(f, va_arg(ap, word *));
+			break;
+		default:
+			pchr(f, *fmt);
+			break;
+		}
+	}
+}
+
+void
+pfmt(io *f, char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vpfmt(f, fmt, ap);
+	va_end(ap);
+}
+
+void
+pchr(io *b, int c)
+{
+	if(b->bufp>=b->ebuf)
+		flushio(b);
+	*b->bufp++=c;
+}
+
+int
+rchr(io *b)
+{
+	if(b->bufp>=b->ebuf)
+		return emptyiobuf(b);
+	return *b->bufp++;
+}
+
+char*
+rstr(io *b, char *stop)
+{
+	char *s, *p;
+	int l, m, n;
+
+	do {
+		l = rchr(b);
+		if(l == EOF)
+			return 0;
+	} while(l && strchr(stop, l));
+	b->bufp--;
+
+	s = 0;
+	l = 0;
+	for(;;){
+		p = (char*)b->bufp;
+		n = (char*)b->ebuf - p;
+		if(n > 0){
+			for(m = 0; m < n; m++){
+				if(strchr(stop, p[m])==0)
+					continue;
+
+				b->bufp += m+1;
+				if(m > 0 || s==0){
+					s = erealloc(s, l+m+1);
+					memmove(s+l, p, m);
+					l += m;
+				}
+				s[l]='\0';
+				return s;
+			}
+			s = erealloc(s, l+m+1);
+			memmove(s+l, p, m);
+			l += m;
+			b->bufp += m;
+		}
+		if(emptyiobuf(b) == EOF){
+			if(s) s[l]='\0';
+			return s;
+		}
+		b->bufp--;
+	}
+}
+
+void
+pquo(io *f, char *s)
+{
+	pchr(f, '\'');
+	for(;*s;s++){
+		if(*s=='\'')
+			pchr(f, *s);
+		pchr(f, *s);
+	}
+	pchr(f, '\'');
+}
+
+void
+pwrd(io *f, char *s)
+{
+	char *t;
+	for(t = s;*t;t++) if(*t >= 0 && (*t <= ' ' || strchr("`^#*[]=|\\?${}()'<>&;", *t))) break;
+	if(t==s || *t)
+		pquo(f, s);
+	else pstr(f, s);
+}
+
+void
+pptr(io *f, void *p)
+{
+	static char hex[] = "0123456789ABCDEF";
+	unsigned long v;
+	int n;
+
+	v = (unsigned long)p;
+	if(sizeof(v) == sizeof(p) && v>>32)
+		for(n = 60;n>=32;n-=4) pchr(f, hex[(v>>n)&0xF]);
+	for(n = 28;n>=0;n-=4) pchr(f, hex[(v>>n)&0xF]);
+}
+
+void
+pstr(io *f, char *s)
+{
+	if(s==0)
+		s="(null)";
+	while(*s) pchr(f, *s++);
+}
+
+void
+pdec(io *f, int 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, unsigned n)
+{
+	if(n>7)
+		poct(f, n>>3);
+	pchr(f, (n&7)+'0');
+}
+
+void
+pval(io *f, word *a)
+{
+	if(a==0)
+		return;
+	while(a->next && a->next->word){
+		pwrd(f, (char *)a->word);
+		pchr(f, ' ');
+		a = a->next;
+	}
+	pwrd(f, (char *)a->word);
+}
+
+io*
+newio(unsigned char *buf, int len, int fd)
+{
+	io *f = new(io);
+	f->buf = buf;
+	f->bufp = buf;
+	f->ebuf = buf+len;
+	f->fd = fd;
+	return f;
+}
+
+/*
+ * Open a string buffer for writing.
+ */
+io*
+openiostr(void)
+{
+	unsigned char *buf = emalloc(100+1);
+	memset(buf, '\0', 100+1);
+	return newio(buf, 100, -1);
+}
+
+/*
+ * Return the buf, free the io
+ */
+char*
+closeiostr(io *f)
+{
+	void *buf = f->buf;
+	free(f);
+	return buf;
+}
+
+/*
+ * Use a open file descriptor for reading.
+ */
+io*
+openiofd(int fd)
+{
+	return newio(emalloc(NBUF), 0, fd);
+}
+
+/*
+ * Open a corebuffer to read.  EOF occurs after reading len
+ * characters from buf.
+ */
+io*
+openiocore(void *buf, int len)
+{
+	return newio(buf, len, -1);
+}
+
+void
+flushio(io *f)
+{
+	int n;
+
+	if(f->fd<0){
+		n = f->ebuf - f->buf;
+		f->buf = erealloc(f->buf, n+n+1);
+		f->bufp = f->buf + n;
+		f->ebuf = f->bufp + n;
+		memset(f->bufp, '\0', n+1);
+	}
+	else{
+		n = f->bufp - f->buf;
+		if(n && Write(f->fd, f->buf, n) != n){
+			Write(2, "Write error\n", 12);
+			if(ntrap)
+				dotrap();
+		}
+		f->bufp = f->buf;
+		f->ebuf = f->buf+NBUF;
+	}
+}
+
+void
+closeio(io *f)
+{
+	if(f->fd>=0) Close(f->fd);
+	free(closeiostr(f));
+}
+
+int
+emptyiobuf(io *f)
+{
+	int n;
+	if(f->fd<0 || (n = Read(f->fd, f->buf, NBUF))<=0) return EOF;
+	f->bufp = f->buf;
+	f->ebuf = f->buf + n;
+	return *f->bufp++;
+}
--- /dev/null
+++ b/rc/io.h
@@ -1,0 +1,28 @@
+#define	EOF	(-1)
+
+struct io{
+	int	fd;
+	unsigned char *buf, *bufp, *ebuf;
+	io	*next;
+};
+
+io *openiofd(int), *openiostr(void), *openiocore(void*, int);
+void pchr(io*, int);
+int rchr(io*);
+char *rstr(io*, char*);
+char *closeiostr(io*);
+void closeio(io*);
+int emptyiobuf(io*);
+void flushio(io*);
+void pdec(io*, int);
+void poct(io*, unsigned);
+void pptr(io*, void*);
+void pquo(io*, char*);
+void pwrd(io*, char*);
+void pstr(io*, char*);
+void pcmd(io*, tree*);
+void pval(io*, word*);
+void pfun(io*, void(*)(void));
+void pfnc(io*, thread*);
+void pfmt(io*, char*, ...);
+void vpfmt(io*, char*, va_list);
--- /dev/null
+++ b/rc/lex.c
@@ -1,0 +1,435 @@
+#include "rc.h"
+#include "io.h"
+#include "getflags.h"
+#include "fns.h"
+
+lexer *lex;
+
+int doprompt = 1;
+int nerror;
+
+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);
+}
+
+lexer*
+newlexer(io *input, char *file)
+{
+	lexer *n = new(struct lexer);
+	n->input = input;
+	n->file = file;
+	n->line = 1;
+	n->eof = 0;
+	n->future = EOF;
+	n->peekc = '{';
+	n->epilog = "}\n";
+	n->lastc = 0;
+	n->inquote = 0;
+	n->incomm = 0;
+	n->lastword = 0;
+	n->lastdol = 0;
+	n->iflast = 0;
+	n->qflag = 0;
+	n->tok[0] = 0;
+	return n;
+}
+
+void
+freelexer(lexer *p)
+{
+	closeio(p->input);
+	free(p->file);
+	free(p);
+}
+
+/*
+ * read a character from the input stream
+ */	
+static int
+getnext(void)
+{
+	int c;
+
+	if(lex->peekc!=EOF){
+		c = lex->peekc;
+		lex->peekc = EOF;
+		return c;
+	}
+	if(lex->eof){
+epilog:
+		if(*lex->epilog)
+			return *lex->epilog++;
+		doprompt = 1;
+		return EOF;
+	}
+	if(doprompt)
+		pprompt();
+	c = rchr(lex->input);
+	if(c=='\\' && !lex->inquote){
+		c = rchr(lex->input);
+		if(c=='\n' && !lex->incomm){		/* don't continue a comment */
+			doprompt = 1;
+			c=' ';
+		}
+		else{
+			lex->peekc = c;
+			c='\\';
+		}
+	}
+	if(c==EOF){
+		lex->eof = 1;
+		goto epilog;
+	} else {
+		if(c=='\n')
+			doprompt = 1;
+		if((!lex->qflag && flag['v']!=0) || flag['V'])
+			pchr(err, c);
+	}
+	return c;
+}
+
+/*
+ * Look ahead in the input stream
+ */
+static int
+nextc(void)
+{
+	if(lex->future==EOF)
+		lex->future = getnext();
+	return lex->future;
+}
+
+/*
+ * Consume the lookahead character.
+ */
+static int
+advance(void)
+{
+	int c = nextc();
+	lex->lastc = lex->future;
+	lex->future = EOF;
+	if(c == '\n')
+		lex->line++;
+	return c;
+}
+
+static void
+skipwhite(void)
+{
+	int c;
+	for(;;){
+		c = nextc();
+		/* Why did this used to be  if(!inquote && c=='#') ?? */
+		if(c=='#'){
+			lex->incomm = 1;
+			for(;;){
+				c = nextc();
+				if(c=='\n' || c==EOF) {
+					lex->incomm = 0;
+					break;
+				}
+				advance();
+			}
+		}
+		if(c==' ' || c=='\t')
+			advance();
+		else return;
+	}
+}
+
+void
+skipnl(void)
+{
+	int c;
+	for(;;){
+		skipwhite();
+		c = nextc();
+		if(c!='\n')
+			return;
+		advance();
+	}
+}
+
+static int
+nextis(int c)
+{
+	if(nextc()==c){
+		advance();
+		return 1;
+	}
+	return 0;
+}
+
+static char*
+addtok(char *p, int val)
+{
+	if(p==0)
+		return 0;
+	if(p==&lex->tok[NTOK-1]){
+		*p = 0;
+		yyerror("token buffer too short");
+		return 0;
+	}
+	*p++=val;
+	return p;
+}
+
+static char*
+addutf(char *p, int c)
+{
+	int i, n;
+
+	p = addtok(p, c);	/* 1-byte UTF runes are special */
+	if(onebyte(c))
+		return p;
+	if(twobyte(c))
+		n = 2;
+	else if(threebyte(c))
+		n = 3;
+	else
+		n = 4;
+	for(i = 1; i < n; i++) {
+		c = nextc();
+		if(c == EOF || !xbyte(c))
+			break;
+		p = addtok(p, advance());
+	}
+	return p;
+}
+
+int
+yylex(void)
+{
+	int glob, c, d = nextc();
+	char *tok = lex->tok;
+	char *w = tok;
+	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(lex->lastword){
+		lex->lastword = 0;
+		if(d=='('){
+			advance();
+			strcpy(tok, "( [SUB]");
+			return SUB;
+		}
+		if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
+			strcpy(tok, "^");
+			return '^';
+		}
+	}
+	lex->inquote = 0;
+	skipwhite();
+	switch(c = advance()){
+	case EOF:
+		lex->lastdol = 0;
+		strcpy(tok, "EOF");
+		return EOF;
+	case '$':
+		lex->lastdol = 1;
+		if(nextis('#')){
+			strcpy(tok, "$#");
+			return COUNT;
+		}
+		if(nextis('"')){
+			strcpy(tok, "$\"");
+			return '"';
+		}
+		strcpy(tok, "$");
+		return '$';
+	case '&':
+		lex->lastdol = 0;
+		if(nextis('&')){
+			skipnl();
+			strcpy(tok, "&&");
+			return ANDAND;
+		}
+		strcpy(tok, "&");
+		return '&';
+	case '|':
+		lex->lastdol = 0;
+		if(nextis(c)){
+			skipnl();
+			strcpy(tok, "||");
+			return OROR;
+		}
+	case '<':
+	case '>':
+		lex->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 if (nextis('>')){
+				t->rtype = RDWR;
+				*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 '\'':
+		lex->lastdol = 0;
+		lex->lastword = 1;
+		lex->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)){
+		lex->lastdol = 0;
+		tok[0] = c;
+		tok[1]='\0';
+		return c;
+	}
+	glob = 0;
+	for(;;){
+		if(c=='*' || c=='[' || c=='?' || c==GLOB){
+			glob = 1;
+			w = addtok(w, GLOB);
+		}
+		w = addutf(w, c);
+		c = nextc();
+		if(lex->lastdol?!idchr(c):!wordchr(c)) break;
+		advance();
+	}
+
+	lex->lastword = 1;
+	lex->lastdol = 0;
+	if(w!=0)
+		*w='\0';
+	t = klook(tok);
+	if(t->type!=WORD)
+		lex->lastword = 0;
+	else
+		t->glob = glob;
+	t->quoted = 0;
+	yylval.tree = t;
+	return t->type;
+}
+
+void
+yyerror(char *m)
+{
+	pfln(err, lex->file, lex->line);
+	pstr(err, ": ");
+	if(lex->tok[0] && lex->tok[0]!='\n')
+		pfmt(err, "token %q: ", lex->tok);
+	pfmt(err, "%s\n", m);
+	flushio(err);
+
+	lex->lastword = 0;
+	lex->lastdol = 0;
+	while(lex->lastc!='\n' && lex->lastc!=EOF) advance();
+	nerror++;
+
+	setstatus(m);
+}
--- /dev/null
+++ b/rc/pcmd.c
@@ -1,0 +1,169 @@
+#include "rc.h"
+#include "io.h"
+#include "fns.h"
+
+#define	c0	t->child[0]
+#define	c1	t->child[1]
+#define	c2	t->child[2]
+
+static void
+pdeglob(io *f, char *s)
+{
+	while(*s){
+		if(*s==GLOB)
+			s++;
+		pchr(f, *s++);
+	}
+}
+
+static int ntab = 0;
+
+static char*
+tabs(void)
+{
+	return "\t\t\t\t\t\t\t\t"+8-(ntab%8);
+}
+
+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%t", c0, c1);
+	break;
+	case ANDAND:	pfmt(f, "%t && %t", c0, c1);
+	break;
+	case BANG:	pfmt(f, "! %t", c0);
+	break;
+	case BRACE:
+			ntab++;
+			pfmt(f, "{\n%s%t", tabs(), c0);
+			ntab--;
+			pfmt(f, "\n%s}", tabs());
+	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){
+			pfmt(f, "%t", c0);
+			if(c1){
+				if(c0->line==c1->line)
+					pstr(f, "; ");
+				else
+					pfmt(f, "\n%s", tabs());
+				pfmt(f, "%t", c1);
+			}
+		}
+		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:
+		pchr(f, ' ');
+		switch(t->rtype){
+		case HERE:
+			if(c1)
+				pfmt(f, "%t ", c1);
+			pchr(f, '<');
+		case READ:
+		case RDWR:
+			pchr(f, '<');
+			if(t->rtype==RDWR)
+				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(t->rtype == HERE)
+			pfmt(f, "\n%s%s\n", t->str, c0->str);
+		else 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;
+	}
+}
--- /dev/null
+++ b/rc/pfnc.c
@@ -1,0 +1,78 @@
+#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",
+	Xpush, "Xpush",
+	Xrdwr, "Xrdwr",
+	Xread, "Xread",
+	Xhere, "Xhere",
+	Xhereq, "Xhereq",
+	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",
+	Xpopredir, "Xpopredir",
+	Xrdcmds, "Xrdcmds",
+	Xbackq, "Xbackq",
+	Xpipefd, "Xpipefd",
+	Xsubshell, "Xsubshell",
+	Xfor, "Xfor",
+	Xglob, "Xglob",
+	Xsimple, "Xsimple",
+	Xqw, "Xqw",
+	Xsrcline, "Xsrcline",
+0};
+
+void
+pfun(io *f, void (*fn)(void))
+{
+	int i;
+	for(i = 0;fname[i].f;i++) if(fname[i].f==fn){
+		pstr(f, fname[i].name);
+		return;
+	}
+	pfmt(f, "%p", fn);
+}
+
+void
+pfnc(io *f, thread *t)
+{
+	list *a;
+
+	pfln(f, srcfile(t), t->line);
+	pfmt(f, " pid %d cycle %p %d ", getpid(), t->code, t->pc);
+	pfun(f, t->code[t->pc].f);
+	for(a = t->argv;a;a = a->next) pfmt(f, " (%v)", a->words);
+	pchr(f, '\n');
+	flushio(f);
+}
--- /dev/null
+++ b/rc/rc.h
@@ -1,0 +1,152 @@
+/*
+ * Plan9 is defined for plan 9
+ * otherwise its UNIX.
+ * Please don't litter the code with ifdefs.  The three below (and one in
+ * getflags) should be enough.
+ */
+
+#include <u.h>
+#include <libc.h>
+
+#define	SIGINT	2
+#define	SIGQUIT	3
+
+#define	YYMAXDEPTH	500
+#ifndef PAREN
+#include "y.tab.h"
+#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 lexer lexer;
+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 */
+	int	line;
+	char	glob;			/* 0=string, 1=glob, 2=pattern see globprop() and noglobs() */
+	char	quoted;
+	char	iskw;
+	char	*str;
+	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*);
+tree *globprop(tree*);
+char *fnstr(tree*);
+
+/*
+ * The first word of any code vector is a reference count
+ * and the second word is a string for srcfile().
+ * Code starts at pc 2. The last code word must be a zero
+ * terminator for codefree().
+ * 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;
+};
+
+#define	NTOK	8192
+
+struct lexer{
+	io	*input;
+	char	*file;
+	int	line;
+
+	char	*prolog;
+	char	*epilog;
+
+	int	peekc;
+	int	future;
+	int	lastc;
+
+	char	eof;
+	char	inquote;
+	char	incomm;
+	char	lastword;	/* was the last token read a word or compound word terminator? */
+	char	lastdol;	/* was the last token read '$' or '$#' or '"'? */
+	char	iflast;		/* static `if not' checking */
+
+	char	qflag;
+
+	char	tok[NTOK];
+};
+extern lexer *lex;		/* current lexer */
+lexer *newlexer(io*, char*);
+void freelexer(lexer*);
+
+#define	APPEND	1
+#define	WRITE	2
+#define	READ	3
+#define	HERE	4
+#define	DUPFD	5
+#define	CLOSE	6
+#define RDWR	7
+
+struct var{
+	var	*next;		/* next on hash or local list */
+	word	*val;		/* value */
+	code	*fn;		/* pointer to function's code vector */
+	int	pc;		/* pc of start of function */
+	char	fnchanged;
+	char	changed;
+	char	name[];
+};
+var *vlook(char*), *gvlook(char*), *newvar(char*, var*);
+void setvar(char*, word*), freevar(var*);
+
+#define	NVAR	521
+extern var *gvar[NVAR];		/* hash for globals */
+
+#define	new(type)	((type *)emalloc(sizeof(type)))
+
+void *emalloc(long);
+void *erealloc(void *, long);
+
+/*
+ * 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)
+/*
+ * Is c the first character of a utf sequence?
+ */
+#define	onebyte(c)	(((c)&0x80)==0x00)
+#define twobyte(c)	(((c)&0xe0)==0xc0)
+#define threebyte(c)	(((c)&0xf0)==0xe0)
+#define fourbyte(c)	(((c)&0xf8)==0xf0)
+#define xbyte(c)	(((c)&0xc0)==0x80)
+
+extern char *argv0;
+extern int nerror;		/* number of errors encountered during compilation */
+extern int doprompt;		/* is it time for a prompt? */
+extern io *err;
+
+/*
+ * 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[];
+extern char *Signame[];
--- /dev/null
+++ b/rc/rcmain
@@ -1,0 +1,38 @@
+# rcmain: unix version
+if(~ $#home 0) home=$HOME
+if(~ $#ifs 0) ifs=' 	
+'
+profile=$home/.rcrc
+switch($#prompt){
+case 0
+	prompt=('% ' '	')
+case 1
+	prompt=($prompt '	')
+}
+if(~ $rcname ?.out) prompt=('broken! ' '	')
+if(flag p) path=/bin
+if not {
+	finit
+	if(~ $#path 0) path=(. /bin /usr/bin /usr/local/bin)
+}
+fn sigexit
+if(! ~ $#cflag 0){
+	if(flag l) {
+		. -q $profile
+	}
+	status=''
+	eval $cflag
+}
+if not if(flag i){
+	if(flag l) {
+		. -q $profile
+	}
+	status=''
+	if(! ~ $#* 0) . $*
+	. -i /dev/fd/0
+}
+if not if(~ $#* 0) . /dev/fd/0
+if not{
+	status=''
+	. $*
+}
--- /dev/null
+++ b/rc/simple.c
@@ -1,0 +1,536 @@
+/*
+ * 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){
+	int i=ifnot;
+	thread *p=runq;
+	code *c;
+loop:
+	c=&p->code[p->pc];
+	while(1){
+		if(c->f==Xpopredir || c->f==Xunlocal)
+			c++;
+		else if(c->f==Xsrcline)
+			c += 2;
+		else if(c->f==Xwastrue){
+			c++;
+			i=0;
+		}
+		else if(c->f==Xifnot){
+			if(i)
+				c += 2;
+			else
+				c = &p->code[c[1].i];
+		}
+		else if(c->f==Xreturn){
+			p = p->ret;
+			if(p==0)
+				return 1;
+			goto loop;
+		}else
+			break;
+	}
+	return c->f==Xexit;
+}
+
+void (*builtinfunc(char *name))(void)
+{
+	extern builtin Builtin[];
+	builtin *bp;
+
+	for(bp = Builtin;bp->name;bp++)
+		if(strcmp(name, bp->name)==0)
+			return bp->fnc;
+	return 0;
+}
+
+void
+Xsimple(void)
+{
+	void (*f)(void);
+	word *a;
+	var *v;
+	int pid;
+
+	a = runq->argv->words;
+	if(a==0){
+		Xerror1("empty argument list");
+		return;
+	}
+	if(flag['x'])
+		pfmt(err, "%v\n", a); /* wrong, should do redirs */
+	v = gvlook(a->word);
+	if(v->fn)
+		execfunc(v);
+	else{
+		if(strcmp(a->word, "builtin")==0){
+			a = a->next;
+			if(a==0){
+				Xerror1("builtin: empty argument list");
+				return;
+			}
+			popword();	/* "builtin" */
+		}
+		f = builtinfunc(a->word);
+		if(f){
+			(*f)();
+			return;
+		}
+		if(exitnext()){
+			/* fork and wait is redundant */
+			pushword("exec");
+			execexec();
+			/* does not return */
+		}
+		else{
+			if((pid = execforkexec()) < 0){
+				Xerror2("try again", Errstr());
+				return;
+			}
+			poplist();
+
+			/* interrupts don't get us out */
+			while(Waitfor(pid) < 0)
+				;
+		}
+	}
+}
+
+static 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;
+		}
+	}
+}
+
+char*
+makercpath(char *dir, char *file)
+{
+	char *path;
+	int m, n = strlen(dir);
+	if(n==0) return estrdup(file);
+	while (n > 0 && dir[n-1]=='/') n--;
+	while (file[0]=='/') file++;
+	m = strlen(file);
+	path = emalloc(n + m + 2);
+	if(n>0) memmove(path, dir, n);
+	path[n++]='/';
+	memmove(path+n, file, m+1);
+	return path;
+}
+
+word*
+searchpath(char *w, char *v)
+{
+	static struct word nullpath = { "", 0 };
+	word *path;
+
+	if(w[0] && w[0] != '/' && w[0] != '#' &&
+	  (w[0] != '.' || (w[1] && w[1] != '/' && (w[1] != '.' || w[2] && w[2] != '/')))){
+		path = vlook(v)->val;
+		if(path)
+			return path;
+	}
+	return &nullpath;
+}
+
+static char**
+mkargv(word *a)
+{
+	char **argv = (char **)emalloc((count(a)+2)*sizeof(char *));
+	char **argp = argv+1;
+	for(;a;a = a->next) *argp++=a->word;
+	*argp = 0;
+	return argv;
+}
+
+void
+execexec(void)
+{
+	char **argv;
+	word *path;
+
+	popword();	/* "exec" */
+	if(runq->argv->words==0){
+		Xerror1("exec: empty argument list");
+		return;
+	}
+	argv = mkargv(runq->argv->words);
+	Updenv();
+	doredir(runq->redir);
+	for(path = searchpath(argv[1], "path"); path; path = path->next){
+		argv[0] = makercpath(path->word, argv[1]);
+		Exec(argv);
+	}
+	setstatus(Errstr());
+	pfln(err, srcfile(runq), runq->line);
+	pfmt(err, ": %s: %s\n", argv[1], getstatus());
+	Xexit();
+}
+
+void
+execfunc(var *func)
+{
+	popword();	/* name */
+	startfunc(func, Poplist(), runq->local, runq->redir);
+}
+
+void
+execcd(void)
+{
+	word *a = runq->argv->words;
+	word *cdpath;
+	char *dir;
+
+	setstatus("can't cd");
+	switch(count(a)){
+	default:
+		pfmt(err, "Usage: cd [directory]\n");
+		break;
+	case 2:
+		a = a->next;
+		for(cdpath = searchpath(a->word, "cdpath"); cdpath; cdpath = cdpath->next){
+			dir = makercpath(cdpath->word, a->word);
+			if(Chdir(dir)>=0){
+				if(cdpath->word[0] != '\0' && strcmp(cdpath->word, ".") != 0)
+					pfmt(err, "%s\n", dir);
+				free(dir);
+				setstatus("");
+				break;
+			}
+			free(dir);
+		}
+		if(cdpath==0)
+			pfmt(err, "Can't cd %s: %s\n", a->word, Errstr());
+		break;
+	case 1:
+		a = vlook("home")->val;
+		if(a){
+			if(Chdir(a->word)>=0)
+				setstatus("");
+			else
+				pfmt(err, "Can't cd %s: %s\n", a->word, Errstr());
+		}
+		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>0 && star->val;--n){
+		a = star->val->next;
+		free(Freeword(star->val));
+		star->val = a;
+		star->changed = 1;
+	}
+	setstatus("");
+	poplist();
+}
+
+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;
+}
+
+void
+execcmds(io *input, char *file, var *local, redir *redir)
+{
+	static union code rdcmds[5];
+
+	if(rdcmds[0].i==0){
+		rdcmds[0].i = 1;
+		rdcmds[1].s="*rdcmds*";
+		rdcmds[2].f = Xrdcmds;
+		rdcmds[3].f = Xreturn;
+		rdcmds[4].f = 0;
+	}
+
+	if(exitnext()) turfstack(local);
+
+	start(rdcmds, 2, local, redir);
+	runq->lex = newlexer(input, file);
+}
+
+void
+execeval(void)
+{
+	char *cmds;
+	int len;
+	io *f;
+
+	popword();	/* "eval" */
+
+	if(runq->argv->words==0){
+		Xerror1("Usage: eval cmd ...");
+		return;
+	}
+	Xqw();		/* make into single word */
+	cmds = Popword();
+	len = strlen(cmds);
+	cmds[len++] = '\n';
+	poplist();
+
+	f = openiostr();
+	pfln(f, srcfile(runq), runq->line);
+	pstr(f, " *eval*");
+
+	execcmds(openiocore(cmds, len), closeiostr(f), runq->local, runq->redir);
+}
+
+void
+execdot(void)
+{
+	int fd, bflag, iflag, qflag;
+	word *path, *argv;
+	char *file;
+
+	popword();	/* "." */
+
+	bflag = iflag = qflag = 0;
+	while(runq->argv->words && runq->argv->words->word[0]=='-'){
+		char *f = runq->argv->words->word+1;
+		if(*f == '-'){
+			popword();
+			break;
+		}
+		for(; *f; f++){
+			switch(*f){
+			case 'b':
+				bflag = 1;
+				continue;
+			case 'i':
+				iflag = 1;
+				continue;
+			case 'q':
+				qflag = 1;
+				continue;
+			}
+			goto Usage;
+		}
+		popword();
+	}
+
+	/* get input file */
+	if(runq->argv->words==0){
+Usage:
+		Xerror1("Usage: . [-biq] file [arg ...]");
+		return;
+	}
+	argv = Poplist();
+		
+	file = 0;
+	fd = -1;
+	for(path = searchpath(argv->word, "path"); path; path = path->next){
+		file = makercpath(path->word, argv->word);
+		fd = Open(file, 0);
+		if(fd >= 0)
+			break;
+		if(strcmp(file, "/dev/stdin")==0){	/* for sun & ucb */
+			fd = open("/dev/cons", OREAD);
+		}
+		free(file);
+	}
+	if(fd<0){
+		if(!qflag)
+			Xerror3(". can't open", argv->word, Errstr());
+		freewords(argv);
+		return;
+	}
+	execcmds(openiofd(fd), file, (var*)0, runq->redir);
+	pushredir(RCLOSE, fd, 0);
+	runq->lex->qflag = qflag;
+	runq->iflag = iflag;
+	if(iflag || !bflag && flag['b']==0){
+		runq->lex->peekc=EOF;
+		runq->lex->epilog="";
+	}
+
+	runq->local = newvar("*", runq->local);
+	runq->local->val = argv->next;
+	argv->next=0;
+	runq->local->changed = 1;
+
+	runq->local = newvar("0", runq->local);
+	runq->local->val = argv;
+	runq->local->changed = 1;
+}
+
+void
+execflag(void)
+{
+	char *letter, *val;
+	switch(count(runq->argv->words)){
+	case 2:
+		setstatus(flag[(unsigned char)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[(unsigned char)letter[0]] = flagset;
+				setstatus("");
+				break;
+			}
+			if(strcmp(val, "-")==0){
+				flag[(unsigned char)letter[0]] = 0;
+				setstatus("");
+				break;
+			}
+		}
+	default:
+		Xerror1("Usage: flag [letter] [+-]");
+		return;
+	}
+	poplist();
+}
+
+void
+execwhatis(void){	/* mildly wrong -- should fork before writing */
+	word *a, *b, *path;
+	var *v;
+	char *file;
+	io *out;
+	int found, sep;
+	a = runq->argv->words->next;
+	if(a==0){
+		Xerror1("Usage: whatis name ...");
+		return;
+	}
+	setstatus("");
+	out = openiofd(mapfd(1));
+	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=' ';
+				}
+				pstr(out, ")\n");
+			}
+			found = 1;
+		}
+		else
+			found = 0;
+		v = gvlook(a->word);
+		if(v->fn)
+			pfmt(out, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
+		else{
+			if(builtinfunc(a->word))
+				pfmt(out, "builtin %s\n", a->word);
+			else {
+				for(path = searchpath(a->word, "path"); path; path = path->next){
+					file = makercpath(path->word, a->word);
+					if(Executable(file)){
+						pfmt(out, "%s\n", file);
+						free(file);
+						break;
+					}
+					free(file);
+				}
+				if(!path && !found){
+					pfmt(err, "%s: not found\n", a->word);
+					setstatus("not found");
+				}
+			}
+		}
+		flushio(out);
+	}
+	poplist();
+	free(closeiostr(out));	/* don't close fd */
+}
+
+void
+execwait(void)
+{
+	switch(count(runq->argv->words)){
+	default:
+		Xerror1("Usage: wait [pid]");
+		return;
+	case 2:
+		Waitfor(atoi(runq->argv->words->next->word));
+		break;
+	case 1:
+		Waitfor(-1);
+		break;
+	}
+	poplist();
+}
--- /dev/null
+++ b/rc/subr.c
@@ -1,0 +1,54 @@
+#include "rc.h"
+#include "io.h"
+#include "fns.h"
+
+void *
+emalloc(long n)
+{
+	void *p = malloc(n);
+	if(p==0)
+		panic("Can't malloc %d bytes", n);
+	return p;
+}
+
+void*
+erealloc(void *p, long n)
+{
+	p = realloc(p, n);
+	if(p==0 && n!=0)
+		panic("Can't realloc %d bytes\n", n);
+	return p;
+}
+
+void
+pfln(io *fd, char *file, int line)
+{
+	if(file && line)
+		pfmt(fd, "%s:%d", file, line);
+	else if(file)
+		pstr(fd, file);
+	else
+		pstr(fd, argv0);
+}
+
+static char *bp;
+
+static 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
+inttoascii(char *s, int n)
+{
+	bp = s;
+	iacvt(n);
+	*bp='\0';
+}
--- /dev/null
+++ b/rc/trap.c
@@ -1,0 +1,34 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+#include "io.h"
+
+int ntrap;
+int trap[NSIG];
+
+void
+dotrap(void)
+{
+	int i;
+	var *trapreq;
+	word *starval;
+	starval = vlook("*")->val;
+	while(ntrap) for(i = 0;i<NSIG;i++) while(trap[i]){
+		--trap[i];
+		--ntrap;
+		if(getpid()!=mypid) Exit();
+		trapreq = vlook(Signame[i]);
+		if(trapreq->fn)
+			startfunc(trapreq, copywords(starval, (word*)0), (var*)0, (redir*)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();
+	}
+}
--- /dev/null
+++ b/rc/tree.c
@@ -1,0 +1,190 @@
+#include "rc.h"
+#include "io.h"
+#include "fns.h"
+
+/*
+ * create and clear a new tree node, and add it
+ * to the node list.
+ */
+static tree *treefree, *treenodes;
+
+tree*
+newtree(void)
+{
+	tree *t;
+
+	t = treefree;
+	if(t==0)
+		t = new(tree);
+	else
+		treefree = t->next;
+	t->quoted = 0;
+	t->glob = 0;
+	t->iskw = 0;
+	t->str = 0;
+	t->child[0] = t->child[1] = t->child[2] = 0;
+	t->line = lex->line;
+	t->next = treenodes;
+	treenodes = t;
+	return t;
+}
+
+void
+freenodes(void)
+{
+	tree *t;
+
+	t = treenodes;
+	while(t){
+		if(t->str){
+			free(t->str);
+			t->str = 0;
+		}
+		t->child[0] = t->child[1] = t->child[2] = 0;
+		if(t->next==0){
+			t->next = treefree;
+			treefree = treenodes;
+			break;
+		}
+		t = t->next;
+	}
+	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;
+
+	if(c0)
+		t->line = c0->line;
+	else if(c1)
+		t->line = c1->line;
+	else if(c2)
+		t->line = c2->line;
+	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;
+
+	t = tree1(SIMPLE, t);
+	t->str = fnstr(t);
+	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;
+}
+
+char*
+fnstr(tree *t)
+{
+	io *f = openiostr();
+	pfmt(f, "%t", t);
+	return closeiostr(f);
+}
+
+tree*
+globprop(tree *t)
+{
+	tree *c0 = t->child[0];
+	tree *c1 = t->child[1];
+	if(c1==0){
+		while(c0 && c0->type==WORDS){
+			c1 = c0->child[1];
+			if(c1 && c1->glob){
+				c1->glob=2;
+				t->glob=1;
+			}
+			c0 = c0->child[0];
+		}
+	} else {
+		if(c0->glob){
+			c0->glob=2;
+			t->glob=1;
+		}
+		if(c1->glob){
+			c1->glob=2;
+			t->glob=1;
+		}
+	}
+	return t;
+}
+
+tree*
+token(char *str, int type)
+{
+	tree *t = newtree();
+	t->str = estrdup(str);
+	t->type = type;
+	return t;
+}
--- /dev/null
+++ b/rc/var.c
@@ -1,0 +1,109 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+
+var *gvar[NVAR];
+
+int
+hash(char *s, int n)
+{
+	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)
+{
+	int h = hash(name, NKW);
+	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*
+newvar(char *name, var *next)
+{
+	int n = strlen(name)+1;
+	var *v = emalloc(sizeof(var)+n);
+	memmove(v->name, name, n);
+	v->next = next;
+	v->val = 0;
+	v->fn = 0;
+	v->changed = 0;
+	v->fnchanged = 0;
+	return v;
+}
+
+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(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)
+{
+	var *v = vlook(name);
+	freewords(v->val);
+	v->val = val;
+	v->changed = 1;
+}
+
+void
+freevar(var *v)
+{
+	freewords(v->val);
+	free(v);
+}
--- /dev/null
+++ b/rc/y.tab.c
@@ -1,0 +1,1978 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     FOR = 258,
+     IN = 259,
+     WHILE = 260,
+     IF = 261,
+     NOT = 262,
+     TWIDDLE = 263,
+     BANG = 264,
+     SUBSHELL = 265,
+     SWITCH = 266,
+     FN = 267,
+     WORD = 268,
+     REDIR = 269,
+     DUP = 270,
+     PIPE = 271,
+     SUB = 272,
+     SIMPLE = 273,
+     ARGLIST = 274,
+     WORDS = 275,
+     BRACE = 276,
+     PAREN = 277,
+     PCMD = 278,
+     PIPEFD = 279,
+     OROR = 280,
+     ANDAND = 281,
+     COUNT = 282
+   };
+#endif
+/* Tokens.  */
+#define FOR 258
+#define IN 259
+#define WHILE 260
+#define IF 261
+#define NOT 262
+#define TWIDDLE 263
+#define BANG 264
+#define SUBSHELL 265
+#define SWITCH 266
+#define FN 267
+#define WORD 268
+#define REDIR 269
+#define DUP 270
+#define PIPE 271
+#define SUB 272
+#define SIMPLE 273
+#define ARGLIST 274
+#define WORDS 275
+#define BRACE 276
+#define PAREN 277
+#define PCMD 278
+#define PIPEFD 279
+#define OROR 280
+#define ANDAND 281
+#define COUNT 282
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 12 "syn.y"
+
+#include "rc.h"
+#include "fns.h"
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 16 "syn.y"
+{
+	struct tree *tree;
+}
+/* Line 193 of yacc.c.  */
+#line 159 "y.tab.c"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 216 of yacc.c.  */
+#line 172 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+/* Hand changed */
+#include <stddef.h>
+#define YYSIZE_T size_t
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+    int i;
+#endif
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  63
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   347
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  40
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  24
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  72
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  118
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   282
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+      32,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,    30,     2,    29,     2,    34,     2,
+      37,    25,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,    33,
+       2,    38,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,    28,     2,    39,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,    35,     2,    36,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      26,    27,    31
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint8 yyprhs[] =
+{
+       0,     0,     3,     4,     7,     9,    12,    14,    17,    20,
+      23,    25,    28,    32,    36,    40,    41,    44,    47,    49,
+      50,    53,    54,    59,    60,    65,    66,    75,    76,    83,
+      84,    89,    90,    95,    97,   101,   105,   109,   113,   116,
+     119,   122,   125,   129,   132,   134,   137,   140,   142,   146,
+     148,   150,   154,   157,   163,   166,   169,   171,   174,   178,
+     182,   185,   187,   189,   191,   193,   195,   197,   199,   201,
+     203,   205,   206
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+      41,     0,    -1,    -1,    42,    32,    -1,    51,    -1,    44,
+      42,    -1,    51,    -1,    45,    43,    -1,    51,    33,    -1,
+      51,    34,    -1,    44,    -1,    51,    32,    -1,    35,    43,
+      36,    -1,    37,    43,    25,    -1,    59,    38,    60,    -1,
+      -1,    50,    49,    -1,    14,    60,    -1,    15,    -1,    -1,
+      46,    49,    -1,    -1,     6,    47,    52,    51,    -1,    -1,
+       6,     7,    53,    51,    -1,    -1,     3,    37,    60,     4,
+      63,    25,    54,    51,    -1,    -1,     3,    37,    60,    25,
+      55,    51,    -1,    -1,     5,    47,    56,    51,    -1,    -1,
+      11,    60,    57,    46,    -1,    58,    -1,     8,    60,    63,
+      -1,    51,    27,    51,    -1,    51,    26,    51,    -1,    51,
+      16,    51,    -1,    50,    51,    -1,    48,    51,    -1,     9,
+      51,    -1,    10,    51,    -1,    12,    63,    46,    -1,    12,
+      63,    -1,    59,    -1,    58,    60,    -1,    58,    50,    -1,
+      61,    -1,    59,    28,    60,    -1,    62,    -1,    61,    -1,
+      60,    28,    60,    -1,    29,    60,    -1,    29,    60,    17,
+      63,    25,    -1,    30,    60,    -1,    31,    60,    -1,    13,
+      -1,    39,    46,    -1,    39,    60,    46,    -1,    37,    63,
+      25,    -1,    14,    46,    -1,     3,    -1,     4,    -1,     5,
+      -1,     6,    -1,     7,    -1,     8,    -1,     9,    -1,    10,
+      -1,    11,    -1,    12,    -1,    -1,    63,    60,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,    24,    24,    25,    26,    27,    28,    29,    30,    31,
+      32,    33,    34,    35,    36,    37,    38,    39,    40,    41,
+      42,    43,    43,    45,    45,    46,    46,    56,    56,    58,
+      58,    60,    60,    62,    63,    64,    65,    66,    67,    68,
+      69,    70,    71,    72,    73,    74,    75,    76,    77,    78,
+      79,    80,    81,    82,    83,    84,    85,    86,    87,    88,
+      89,    90,    90,    90,    90,    90,    90,    90,    90,    90,
+      90,    91,    92
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "FOR", "IN", "WHILE", "IF", "NOT",
+  "TWIDDLE", "BANG", "SUBSHELL", "SWITCH", "FN", "WORD", "REDIR", "DUP",
+  "PIPE", "SUB", "SIMPLE", "ARGLIST", "WORDS", "BRACE", "PAREN", "PCMD",
+  "PIPEFD", "')'", "OROR", "ANDAND", "'^'", "'$'", "'\"'", "COUNT",
+  "'\\n'", "';'", "'&'", "'{'", "'}'", "'('", "'='", "'`'", "$accept",
+  "rc", "line", "body", "cmdsa", "cmdsan", "brace", "paren", "assign",
+  "epilog", "redir", "cmd", "@1", "@2", "@3", "@4", "@5", "@6", "simple",
+  "first", "word", "comword", "keyword", "words", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,    41,   280,   281,    94,    36,
+      34,   282,    10,    59,    38,   123,   125,    40,    61,    96
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    40,    41,    41,    42,    42,    43,    43,    44,    44,
+      45,    45,    46,    47,    48,    49,    49,    50,    50,    51,
+      51,    52,    51,    53,    51,    54,    51,    55,    51,    56,
+      51,    57,    51,    51,    51,    51,    51,    51,    51,    51,
+      51,    51,    51,    51,    58,    58,    58,    59,    59,    60,
+      60,    60,    61,    61,    61,    61,    61,    61,    61,    61,
+      61,    62,    62,    62,    62,    62,    62,    62,    62,    62,
+      62,    63,    63
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     0,     2,     1,     2,     1,     2,     2,     2,
+       1,     2,     3,     3,     3,     0,     2,     2,     1,     0,
+       2,     0,     4,     0,     4,     0,     8,     0,     6,     0,
+       4,     0,     4,     1,     3,     3,     3,     3,     2,     2,
+       2,     2,     3,     2,     1,     2,     2,     1,     3,     1,
+       1,     3,     2,     5,     2,     2,     1,     2,     3,     3,
+       2,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     0,     2
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+      19,     0,     0,     0,     0,    19,    19,     0,    71,    56,
+       0,    18,     0,     0,     0,    19,    71,     0,     0,     0,
+      19,    15,    19,    19,     4,    33,    44,    47,     0,    19,
+      29,    23,    21,    61,    62,    63,    64,    65,    66,    67,
+      68,    69,    70,     0,    71,    50,    49,    40,    41,    31,
+      43,    60,    17,    52,    54,    55,     0,    10,    19,     6,
+       0,    57,     0,     1,     3,     5,     0,    20,    15,    39,
+      38,    19,    19,    19,     8,     9,    46,    45,     0,     0,
+       0,     0,    19,    19,    19,     0,    34,     0,    42,    72,
+      71,    12,     7,    11,    59,    58,    16,    37,    36,    35,
+      48,    14,    71,    27,    13,    30,    24,    22,    51,    32,
+       0,     0,    19,    53,    25,    28,    19,    26
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,    18,    19,    56,    57,    58,    21,    30,    22,    67,
+      23,    59,    84,    83,   116,   112,    82,    87,    25,    26,
+      89,    45,    46,    50
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -28
+static const yytype_int16 yypact[] =
+{
+     122,   -27,   -13,    -3,   296,   308,   308,   296,   -28,   -28,
+     135,   -28,   296,   296,   296,   308,   -28,   135,    25,     5,
+     308,     4,   308,   308,    84,   172,   -26,   -28,   296,   308,
+     -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,
+     -28,   -28,   -28,     9,    15,   -28,   -28,    20,    20,    15,
+     135,   -28,    15,    30,   -28,   -28,    13,   -28,   308,    36,
+     185,   -28,   -19,   -28,   -28,   -28,   296,   -28,     4,    20,
+      20,   308,   308,   308,   -28,   -28,   -28,    15,   296,   296,
+      23,    32,   308,   308,   308,   296,   296,     9,   -28,    15,
+     -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,    20,    20,
+     -28,    15,   -28,   -28,   -28,    38,    38,    38,   -28,   -28,
+     222,   259,   308,   -28,   -28,    38,   308,    38
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+     -28,   -28,    35,   -12,     1,   -28,    16,    57,   -28,    -7,
+     -18,     8,   -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,
+      28,     0,   -28,    -5
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -3
+static const yytype_int8 yytable[] =
+{
+      27,    20,    78,    68,    31,    27,    27,    76,    24,    85,
+      28,    60,    79,    47,    48,    27,    15,    81,    66,    11,
+      27,    20,    27,    27,    29,    63,    51,   102,    24,    27,
+      69,    70,    44,    61,    29,    49,    71,    64,    52,    86,
+      53,    54,    55,    85,    15,    62,    92,    90,   103,    91,
+      68,    85,    71,    77,    71,    65,    80,   104,    27,    51,
+      32,    96,    72,    73,    72,    73,    88,     0,    93,    74,
+      75,    27,    27,    27,     0,     0,     0,     0,    95,    97,
+      98,    99,    27,    27,    27,   110,     0,     0,     0,     0,
+     105,   106,   107,     0,    52,     0,     0,   111,     0,     0,
+      71,     0,     0,   109,     0,     0,   100,   101,     0,     0,
+      72,    73,    27,   108,     0,     0,    27,    74,    75,     0,
+     115,     0,    -2,     0,   117,     1,     0,     2,     3,     0,
+       4,     5,     6,     7,     8,     9,    10,    11,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,     9,    43,
+       0,    12,    13,    14,     0,     0,     0,    15,     0,    16,
+       0,    17,     0,     0,    12,    13,    14,     0,     0,     0,
+      15,     0,    16,     0,    17,    33,    34,    35,    36,    37,
+      38,    39,    40,    41,    42,     9,    10,    11,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,     9,    43,
+       0,    12,    13,    14,     0,     0,     0,     0,     0,    16,
+      94,    17,     0,     0,    12,    13,    14,     0,     0,     0,
+       0,     0,    16,     0,    17,    33,    34,    35,    36,    37,
+      38,    39,    40,    41,    42,     9,    43,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,   113,     0,     0,
+       0,    12,    13,    14,     0,     0,     0,     0,     0,    16,
+       0,    17,    33,    34,    35,    36,    37,    38,    39,    40,
+      41,    42,     9,    43,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,   114,     0,     0,     0,    12,    13,
+      14,     0,     0,     0,     0,     0,    16,     0,    17,    33,
+      34,    35,    36,    37,    38,    39,    40,    41,    42,     9,
+      43,     1,     0,     2,     3,     0,     4,     5,     6,     7,
+       8,     9,    10,    11,     0,    12,    13,    14,     0,     0,
+       0,     0,     0,    16,     0,    17,     0,    12,    13,    14,
+       0,     0,     0,    15,     0,    16,     0,    17
+};
+
+static const yytype_int8 yycheck[] =
+{
+       0,     0,    28,    21,     7,     5,     6,    25,     0,    28,
+      37,    16,    38,     5,     6,    15,    35,    29,    14,    15,
+      20,    20,    22,    23,    37,     0,    10,     4,    20,    29,
+      22,    23,     4,    17,    37,     7,    16,    32,    10,    44,
+      12,    13,    14,    28,    35,    17,    58,    17,    25,    36,
+      68,    28,    16,    25,    16,    20,    28,    25,    58,    43,
+       3,    68,    26,    27,    26,    27,    50,    -1,    32,    33,
+      34,    71,    72,    73,    -1,    -1,    -1,    -1,    62,    71,
+      72,    73,    82,    83,    84,    90,    -1,    -1,    -1,    -1,
+      82,    83,    84,    -1,    66,    -1,    -1,   102,    -1,    -1,
+      16,    -1,    -1,    87,    -1,    -1,    78,    79,    -1,    -1,
+      26,    27,   112,    85,    -1,    -1,   116,    33,    34,    -1,
+     112,    -1,     0,    -1,   116,     3,    -1,     5,     6,    -1,
+       8,     9,    10,    11,    12,    13,    14,    15,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      -1,    29,    30,    31,    -1,    -1,    -1,    35,    -1,    37,
+      -1,    39,    -1,    -1,    29,    30,    31,    -1,    -1,    -1,
+      35,    -1,    37,    -1,    39,     3,     4,     5,     6,     7,
+       8,     9,    10,    11,    12,    13,    14,    15,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      -1,    29,    30,    31,    -1,    -1,    -1,    -1,    -1,    37,
+      25,    39,    -1,    -1,    29,    30,    31,    -1,    -1,    -1,
+      -1,    -1,    37,    -1,    39,     3,     4,     5,     6,     7,
+       8,     9,    10,    11,    12,    13,    14,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    25,    -1,    -1,
+      -1,    29,    30,    31,    -1,    -1,    -1,    -1,    -1,    37,
+      -1,    39,     3,     4,     5,     6,     7,     8,     9,    10,
+      11,    12,    13,    14,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    25,    -1,    -1,    -1,    29,    30,
+      31,    -1,    -1,    -1,    -1,    -1,    37,    -1,    39,     3,
+       4,     5,     6,     7,     8,     9,    10,    11,    12,    13,
+      14,     3,    -1,     5,     6,    -1,     8,     9,    10,    11,
+      12,    13,    14,    15,    -1,    29,    30,    31,    -1,    -1,
+      -1,    -1,    -1,    37,    -1,    39,    -1,    29,    30,    31,
+      -1,    -1,    -1,    35,    -1,    37,    -1,    39
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     3,     5,     6,     8,     9,    10,    11,    12,    13,
+      14,    15,    29,    30,    31,    35,    37,    39,    41,    42,
+      44,    46,    48,    50,    51,    58,    59,    61,    37,    37,
+      47,     7,    47,     3,     4,     5,     6,     7,     8,     9,
+      10,    11,    12,    14,    60,    61,    62,    51,    51,    60,
+      63,    46,    60,    60,    60,    60,    43,    44,    45,    51,
+      63,    46,    60,     0,    32,    42,    14,    49,    50,    51,
+      51,    16,    26,    27,    33,    34,    50,    60,    28,    38,
+      60,    43,    56,    53,    52,    28,    63,    57,    46,    60,
+      17,    36,    43,    32,    25,    46,    49,    51,    51,    51,
+      60,    60,     4,    25,    25,    51,    51,    51,    60,    46,
+      63,    63,    55,    25,    25,    51,    54,    51
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+	      (Loc).first_line, (Loc).first_column,	\
+	      (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    yytype_int16 *bottom;
+    yytype_int16 *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+    YYSTYPE *yyvsp;
+    int yyrule;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       		       );
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+	 constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+		    + sizeof yyexpecting - 1
+		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+		       * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+	 YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	  {
+	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+	      {
+		yycount = 1;
+		yysize = yysize0;
+		yyformat[sizeof yyunexpected - 1] = '\0';
+		break;
+	      }
+	    yyarg[yycount++] = yytname[yyx];
+	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+	    yysize_overflow |= (yysize1 < yysize);
+	    yysize = yysize1;
+	    yyfmt = yystpcpy (yyfmt, yyprefix);
+	    yyprefix = yyor;
+	  }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+	return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+	{
+	  /* Avoid sprintf, as that infringes on the user's name space.
+	     Don't have undefined behavior even if the translation
+	     produced a string with the wrong number of "%s"s.  */
+	  char *yyp = yyresult;
+	  int yyi = 0;
+	  while ((*yyp = *yyf) != '\0')
+	    {
+	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+		{
+		  yyp += yytnamerr (yyp, yyarg[yyi++]);
+		  yyf += 2;
+		}
+	      else
+		{
+		  yyp++;
+		  yyf++;
+		}
+	    }
+	}
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  YYUSE (yyvaluep);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+  
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+
+		    &yystacksize);
+
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 24 "syn.y"
+    { return 1;}
+    break;
+
+  case 3:
+#line 25 "syn.y"
+    {readhere(lex->input); return !compile((yyvsp[(1) - (2)].tree));}
+    break;
+
+  case 5:
+#line 27 "syn.y"
+    {(yyval.tree)=tree2(';', (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 7:
+#line 29 "syn.y"
+    {(yyval.tree)=tree2(';', (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 9:
+#line 31 "syn.y"
+    {(yyval.tree)=tree1('&', (yyvsp[(1) - (2)].tree));}
+    break;
+
+  case 11:
+#line 33 "syn.y"
+    {readhere(lex->input);}
+    break;
+
+  case 12:
+#line 34 "syn.y"
+    {(yyval.tree)=tree1(BRACE, (yyvsp[(2) - (3)].tree));}
+    break;
+
+  case 13:
+#line 35 "syn.y"
+    {(yyval.tree)=tree1(PCMD, (yyvsp[(2) - (3)].tree));}
+    break;
+
+  case 14:
+#line 36 "syn.y"
+    {(yyval.tree)=tree2('=', (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
+    break;
+
+  case 15:
+#line 37 "syn.y"
+    {(yyval.tree)=0;}
+    break;
+
+  case 16:
+#line 38 "syn.y"
+    {(yyval.tree)=mung2((yyvsp[(1) - (2)].tree), (yyvsp[(1) - (2)].tree)->child[0], (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 17:
+#line 39 "syn.y"
+    {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)); if((yyval.tree)->rtype==HERE) heredoc((yyval.tree));}
+    break;
+
+  case 19:
+#line 41 "syn.y"
+    {(yyval.tree)=0;}
+    break;
+
+  case 20:
+#line 42 "syn.y"
+    {(yyval.tree)=epimung((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 21:
+#line 43 "syn.y"
+    {skipnl();}
+    break;
+
+  case 22:
+#line 44 "syn.y"
+    {(yyval.tree)=mung2((yyvsp[(1) - (4)].tree), (yyvsp[(2) - (4)].tree), (yyvsp[(4) - (4)].tree));}
+    break;
+
+  case 23:
+#line 45 "syn.y"
+    {skipnl();}
+    break;
+
+  case 24:
+#line 45 "syn.y"
+    {(yyval.tree)=mung1((yyvsp[(2) - (4)].tree), (yyvsp[(4) - (4)].tree));}
+    break;
+
+  case 25:
+#line 46 "syn.y"
+    {skipnl();}
+    break;
+
+  case 26:
+#line 55 "syn.y"
+    {(yyval.tree)=mung3((yyvsp[(1) - (8)].tree), (yyvsp[(3) - (8)].tree), (yyvsp[(5) - (8)].tree) ? (yyvsp[(5) - (8)].tree) : tree1(PAREN, (yyvsp[(5) - (8)].tree)), (yyvsp[(8) - (8)].tree));}
+    break;
+
+  case 27:
+#line 56 "syn.y"
+    {skipnl();}
+    break;
+
+  case 28:
+#line 57 "syn.y"
+    {(yyval.tree)=mung3((yyvsp[(1) - (6)].tree), (yyvsp[(3) - (6)].tree), (tree*)0, (yyvsp[(6) - (6)].tree));}
+    break;
+
+  case 29:
+#line 58 "syn.y"
+    {skipnl();}
+    break;
+
+  case 30:
+#line 59 "syn.y"
+    {(yyval.tree)=mung2((yyvsp[(1) - (4)].tree), (yyvsp[(2) - (4)].tree), (yyvsp[(4) - (4)].tree));}
+    break;
+
+  case 31:
+#line 60 "syn.y"
+    {skipnl();}
+    break;
+
+  case 32:
+#line 61 "syn.y"
+    {(yyval.tree)=tree2(SWITCH, (yyvsp[(2) - (4)].tree), (yyvsp[(4) - (4)].tree));}
+    break;
+
+  case 33:
+#line 62 "syn.y"
+    {(yyval.tree)=simplemung((yyvsp[(1) - (1)].tree));}
+    break;
+
+  case 34:
+#line 63 "syn.y"
+    {(yyval.tree)=mung2((yyvsp[(1) - (3)].tree), (yyvsp[(2) - (3)].tree), (yyvsp[(3) - (3)].tree));}
+    break;
+
+  case 35:
+#line 64 "syn.y"
+    {(yyval.tree)=tree2(ANDAND, (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
+    break;
+
+  case 36:
+#line 65 "syn.y"
+    {(yyval.tree)=tree2(OROR, (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
+    break;
+
+  case 37:
+#line 66 "syn.y"
+    {(yyval.tree)=mung2((yyvsp[(2) - (3)].tree), (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
+    break;
+
+  case 38:
+#line 67 "syn.y"
+    {(yyval.tree)=mung2((yyvsp[(1) - (2)].tree), (yyvsp[(1) - (2)].tree)->child[0], (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 39:
+#line 68 "syn.y"
+    {(yyval.tree)=mung3((yyvsp[(1) - (2)].tree), (yyvsp[(1) - (2)].tree)->child[0], (yyvsp[(1) - (2)].tree)->child[1], (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 40:
+#line 69 "syn.y"
+    {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 41:
+#line 70 "syn.y"
+    {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 42:
+#line 71 "syn.y"
+    {(yyval.tree)=tree2(FN, (yyvsp[(2) - (3)].tree), (yyvsp[(3) - (3)].tree));}
+    break;
+
+  case 43:
+#line 72 "syn.y"
+    {(yyval.tree)=tree1(FN, (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 45:
+#line 74 "syn.y"
+    {(yyval.tree)=globprop(tree2(ARGLIST, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)));}
+    break;
+
+  case 46:
+#line 75 "syn.y"
+    {(yyval.tree)=globprop(tree2(ARGLIST, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)));}
+    break;
+
+  case 48:
+#line 77 "syn.y"
+    {(yyval.tree)=globprop(tree2('^', (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree)));}
+    break;
+
+  case 49:
+#line 78 "syn.y"
+    {lex->lastword=1; (yyvsp[(1) - (1)].tree)->type=WORD;}
+    break;
+
+  case 51:
+#line 80 "syn.y"
+    {(yyval.tree)=globprop(tree2('^', (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree)));}
+    break;
+
+  case 52:
+#line 81 "syn.y"
+    {(yyval.tree)=tree1('$', (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 53:
+#line 82 "syn.y"
+    {(yyval.tree)=tree2(SUB, (yyvsp[(2) - (5)].tree), (yyvsp[(4) - (5)].tree));}
+    break;
+
+  case 54:
+#line 83 "syn.y"
+    {(yyval.tree)=tree1('"', (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 55:
+#line 84 "syn.y"
+    {(yyval.tree)=tree1(COUNT, (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 57:
+#line 86 "syn.y"
+    {(yyval.tree)=tree2('`', (tree*)0, (yyvsp[(2) - (2)].tree));}
+    break;
+
+  case 58:
+#line 87 "syn.y"
+    {(yyval.tree)=tree2('`', (yyvsp[(2) - (3)].tree), (yyvsp[(3) - (3)].tree));}
+    break;
+
+  case 59:
+#line 88 "syn.y"
+    {(yyval.tree)=globprop(tree1(PAREN, (yyvsp[(2) - (3)].tree)));}
+    break;
+
+  case 60:
+#line 89 "syn.y"
+    {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)); (yyval.tree)->type=PIPEFD;}
+    break;
+
+  case 71:
+#line 91 "syn.y"
+    {(yyval.tree)=(tree*)0;}
+    break;
+
+  case 72:
+#line 92 "syn.y"
+    {(yyval.tree)=tree2(WORDS, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+    break;
+
+
+/* Line 1267 of yacc.c.  */
+#line 1776 "y.tab.c"
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+      {
+	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+	  {
+	    YYSIZE_T yyalloc = 2 * yysize;
+	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
+	    if (yymsg != yymsgbuf)
+	      YYSTACK_FREE (yymsg);
+	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+	    if (yymsg)
+	      yymsg_alloc = yyalloc;
+	    else
+	      {
+		yymsg = yymsgbuf;
+		yymsg_alloc = sizeof yymsgbuf;
+	      }
+	  }
+
+	if (0 < yysize && yysize <= yymsg_alloc)
+	  {
+	    (void) yysyntax_error (yymsg, yystate, yychar);
+	    yyerror (yymsg);
+	  }
+	else
+	  {
+	    yyerror (YY_("syntax error"));
+	    if (yysize != 0)
+	      goto yyexhaustedlab;
+	  }
+      }
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+		 yytoken, &yylval);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+
--- /dev/null
+++ b/rc/y.tab.h
@@ -1,0 +1,114 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     FOR = 258,
+     IN = 259,
+     WHILE = 260,
+     IF = 261,
+     NOT = 262,
+     TWIDDLE = 263,
+     BANG = 264,
+     SUBSHELL = 265,
+     SWITCH = 266,
+     FN = 267,
+     WORD = 268,
+     REDIR = 269,
+     DUP = 270,
+     PIPE = 271,
+     SUB = 272,
+     SIMPLE = 273,
+     ARGLIST = 274,
+     WORDS = 275,
+     BRACE = 276,
+     PAREN = 277,
+     PCMD = 278,
+     PIPEFD = 279,
+     OROR = 280,
+     ANDAND = 281,
+     COUNT = 282
+   };
+#endif
+/* Tokens.  */
+#define FOR 258
+#define IN 259
+#define WHILE 260
+#define IF 261
+#define NOT 262
+#define TWIDDLE 263
+#define BANG 264
+#define SUBSHELL 265
+#define SWITCH 266
+#define FN 267
+#define WORD 268
+#define REDIR 269
+#define DUP 270
+#define PIPE 271
+#define SUB 272
+#define SIMPLE 273
+#define ARGLIST 274
+#define WORDS 275
+#define BRACE 276
+#define PAREN 277
+#define PCMD 278
+#define PIPEFD 279
+#define OROR 280
+#define ANDAND 281
+#define COUNT 282
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 16 "syn.y"
+{
+	struct tree *tree;
+}
+/* Line 1529 of yacc.c.  */
+#line 107 "y.tab.h"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
--- a/session.c
+++ /dev/null
@@ -1,27 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <rc.h>
-
-// TODO: aanserver
-static void
-sessionexit(void)
-{
-    char *s = getenv("rstatus");
-    if(s != nil)
-        exit(*s);
-}
-
-int
-session(int fd)
-{
-    char buf[1024];
-    read(fd, buf, sizeof buf);
-    char *cmd[] = {
-        "rc",
-        "-c",
-        buf
-    };
-
-    runscript(fd, 1, cmd);
-    return -1;
-}