shithub: riscv

Download patch

ref: 189731aad01e09db1807c78af421c615ed3a3242
parent: c51a5cfa06dd982da839c4cfcba1610259d27e38
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Jan 3 13:41:48 EST 2022

rc: make it portable (for UNIX)

Fixup remaining Plan9 dependencies (chartorune()).
Add Makefile for UNIX-like systems (tested with Linux and APE).
Make error printing consistent, use Errstr() explicitely.
Get rid of NSTATUS buffer limit, just malloc it.

--- /dev/null
+++ b/sys/src/cmd/rc/Makefile
@@ -1,0 +1,51 @@
+TARG=rc
+
+OFILES=\
+	code.o\
+	exec.o\
+	getflags.o\
+	glob.o\
+	here.o\
+	io.o\
+	lex.o\
+	pcmd.o\
+	pfnc.o\
+	simple.o\
+	subr.o\
+	trap.o\
+	tree.o\
+	var.o\
+	havefork.o\
+	unix.o\
+	y.tab.o\
+
+HFILES=rc.h\
+	y.tab.h\
+	io.h\
+	exec.h\
+	fns.h\
+	getflags.h\
+
+YFILES=syn.y
+
+PREFIX=/usr/local
+
+all: $(TARG)
+
+install: $(TARG) rcmain.unix
+	cp $(TARG) $(PREFIX)/bin/
+	cp rcmain.unix $(PREFIX)/lib/rcmain
+
+$(TARG): $(OFILES)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $(TARG) $(OFILES)
+
+y.tab.h y.tab.c: $(YFILES)
+	$(YACC) -d $(YFILES)
+
+unix.o:	unix.c
+	$(CC) $(CFLAGS) '-DPREFIX="$(PREFIX)"' -c unix.c
+
+$(OFILES): $(HFILES)
+
+clean:
+	rm -f $(OFILES) $(TARG) y.tab.? y.debug
--- a/sys/src/cmd/rc/code.c
+++ b/sys/src/cmd/rc/code.c
@@ -460,7 +460,7 @@
 	int out;		/* jump here to leave switch */
 	int nextcase;	/* patch jump address to next case */
 	tree *tt;
-	if(c1->child[0]==nil
+	if(c1->child[0]==0
 	|| c1->child[0]->type!=';'
 	|| !iscase(c1->child[0]->child[0])){
 		yyerror("case missing in switch");
--- a/sys/src/cmd/rc/exec.c
+++ b/sys/src/cmd/rc/exec.c
@@ -19,6 +19,8 @@
 	p->lex = 0;
 	p->local = local;
 	p->iflag = 0;
+	p->pid = 0;
+	p->status = 0;
 	p->ret = runq;
 	runq = p;
 }
@@ -42,6 +44,7 @@
 	runq = p->ret;
 	if(p->lex) freelexer(p->lex);
 	codefree(p->code);
+	free(p->status);
 	free(p);
 }
 
@@ -341,7 +344,7 @@
 	}
 	file = runq->argv->words->word;
 	if((fd = Open(file, 1))<0 && (fd = Creat(file))<0){
-		Xerror("can't open");
+		Xerror3(">> can't open", file, Errstr());
 		return;
 	}
 	Seek(fd, 0L, 2);
@@ -395,7 +398,7 @@
 			return;
 		}
 	}
-	Exit(getstatus());
+	Exit();
 }
 
 void
@@ -443,25 +446,51 @@
 	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/here.XXXXXXXXXXX";
+	char file[]="/tmp/hereXXXXXXXXXXYY";
 	int fd;
 	io *io;
 
-	if((fd = Creat(mktemp(file)))<0){
-		Xerror("can't open");
+	if((fd = herefile(file))<0){
+		Xerror3("<< can't get temp file", file, Errstr());
 		return;
 	}
 	io = openiofd(fd);
-	psubst(io, (uchar*)runq->code[runq->pc++].s);
+	psubst(io, (unsigned char*)runq->code[runq->pc++].s);
 	flushio(io);
 	closeio(io);
 
 	/* open for reading and unlink */
 	if((fd = Open(file, 3))<0){
-		Xerror("can't open");
+		Xerror3("<< can't open", file, Errstr());
 		return;
 	}
 	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
@@ -470,11 +499,11 @@
 void
 Xhereq(void)
 {
-	char file[] = "/tmp/here.XXXXXXXXXXX", *body;
+	char file[]="/tmp/hereXXXXXXXXXXYY", *body;
 	int fd;
 
-	if((fd = Creat(mktemp(file)))<0){
-		Xerror("can't open");
+	if((fd = herefile(file))<0){
+		Xerror3("<< can't get temp file", file, Errstr());
 		return;
 	}
 	body = runq->code[runq->pc++].s;
@@ -483,7 +512,7 @@
 
 	/* open for reading and unlink */
 	if((fd = Open(file, 3))<0){
-		Xerror("can't open");
+		Xerror3("<< can't open", file, Errstr());
 		return;
 	}
 	pushredir(ROPEN, fd, runq->code[runq->pc++].i);
@@ -492,6 +521,7 @@
 void
 Xread(void)
 {
+	char *file;
 	int fd;
 
 	switch(count(runq->argv->words)){
@@ -504,8 +534,9 @@
 	case 1:
 		break;
 	}
-	if((fd = Open(runq->argv->words->word, 0))<0){
-		Xerror("can't open");
+	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);
@@ -515,6 +546,7 @@
 void
 Xrdwr(void)
 {
+	char *file;
 	int fd;
 
 	switch(count(runq->argv->words)){
@@ -527,8 +559,9 @@
 	case 1:
 		break;
 	}
-	if((fd = Open(runq->argv->words->word, 2))<0){
-		Xerror("can't open");
+	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);
@@ -555,7 +588,7 @@
 		Xpopredir();
 	popthread();
 	if(runq==0)
-		Exit(getstatus());
+		Exit();
 }
 
 void
@@ -588,6 +621,7 @@
 void
 Xwrite(void)
 {
+	char *file;
 	int fd;
 
 	switch(count(runq->argv->words)){
@@ -600,8 +634,9 @@
 	case 1:
 		break;
 	}
-	if((fd = Creat(runq->argv->words->word))<0){
-		Xerror("can't open");
+	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);
@@ -696,7 +731,7 @@
 	var *v;
 
 	if(count(runq->argv->words)!=1){
-		Xerror1("variable name not singleton!");
+		Xerror1("= variable name not singleton!");
 		return;
 	}
 	v = vlook(runq->argv->words->word);
@@ -728,7 +763,7 @@
 	int n;
 
 	if(count(runq->argv->words)!=1){
-		Xerror1("variable name not singleton!");
+		Xerror1("$ variable name not singleton!");
 		return;
 	}
 	n = 0;
@@ -833,7 +868,7 @@
 	char *s;
 
 	if(count(runq->argv->next->words)!=1){
-		Xerror1("variable name not singleton!");
+		Xerror1("$() variable name not singleton!");
 		return;
 	}
 	s = runq->argv->next->words->word;
@@ -853,7 +888,7 @@
 	int n;
 
 	if(count(runq->argv->words)!=1){
-		Xerror1("variable name not singleton!");
+		Xerror1("$# variable name not singleton!");
 		return;
 	}
 	n = 0;
@@ -875,7 +910,7 @@
 Xlocal(void)
 {
 	if(count(runq->argv->words)!=1){
-		Xerror1("variable name must be singleton");
+		Xerror1("local variable name must be singleton");
 		return;
 	}
 	runq->local = newvar(runq->argv->words->word, runq->local);
@@ -932,29 +967,31 @@
 static char*
 concstatus(char *s, char *t)
 {
-	static char v[NSTATUS+1];
-	int n = strlen(s);
-	strncpy(v, s, NSTATUS);
-	if(n<NSTATUS){
-		v[n]='|';
-		strncpy(v+n+1, t, NSTATUS-n-1);
-	}
-	v[NSTATUS]='\0';
-	return v;
+	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 status[NSTATUS+1];
-	if(runq->pid==-1)
-		setstatus(concstatus(runq->status, getstatus()));
-	else{
-		strncpy(status, getstatus(), NSTATUS);
-		status[NSTATUS]='\0';
-		Waitfor(runq->pid, 1);
+	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(), status));
+		Setstatus(concstatus(Getstatus(), old));
 	}
 }
 
@@ -995,7 +1032,6 @@
 			p->lex = 0;
 		} else
 			--p->pc;	/* re-execute Xrdcmds after codebuf runs */
-		ntrap = 0;	/* avoid double-interrupts during blocked writes */
 		start(codebuf, 2, p->local, p->redir);
 	}
 	lex = 0;
@@ -1027,31 +1063,54 @@
 }
 
 void
-Xerror(char *s)
+Xerror1(char *s)
 {
+	setstatus("error");
 	pfln(err, srcfile(runq), runq->line);
-	pfmt(err, ": %s: %r\n", s);
+	pfmt(err, ": %s\n", s);
 	flushio(err);
-	setstatus("error");
 	while(!runq->iflag) Xreturn();
 }
-
 void
-Xerror1(char *s)
+Xerror2(char *s, char *e)
 {
+	setstatus(e);
 	pfln(err, srcfile(runq), runq->line);
-	pfmt(err, ": %s\n", s);
+	pfmt(err, ": %s: %s\n", s, e);
 	flushio(err);
-	setstatus("error");
 	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)
 {
-	setvar("status", newword(s, (word *)0));
+	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)
 {
--- a/sys/src/cmd/rc/exec.h
+++ b/sys/src/cmd/rc/exec.h
@@ -11,8 +11,9 @@
 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 Xerror(char*);
 extern void Xerror1(char*);
+extern void Xerror2(char*,char*);
+extern void Xerror3(char*,char*,char*);
 
 /*
  * word lists are in correct order,
@@ -33,7 +34,6 @@
 	int from, to;			/* what to do it to */
 	redir *next;			/* what else to do (reverse order) */
 };
-#define	NSTATUS	128			/* length of status */
 
 /*
  * redir types
@@ -54,7 +54,7 @@
 	lexer *lex;			/* lexer for Xrdcmds */
 	int iflag;			/* interactive? */
 	int pid;			/* process for Xpipewait to wait for */
-	char status[NSTATUS];		/* status for Xpipewait */
+	char *status;			/* status for Xpipewait */
 	thread *ret;			/* who continues when this finishes */
 };
 
@@ -82,3 +82,5 @@
 
 char *srcfile(thread*);
 char *getstatus(void);
+
+extern char *argv0;
--- a/sys/src/cmd/rc/fns.h
+++ b/sys/src/cmd/rc/fns.h
@@ -8,10 +8,11 @@
 int	Eintr(void);
 int	Executable(char*);
 void	Exec(char**);
-void	Exit(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);
@@ -23,10 +24,11 @@
 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, int);
+int	Waitfor(int);
 long	Write(int, void*, long);
 void	addwaitpid(int);
 void	clearwaitpids(void);
@@ -52,7 +54,7 @@
 void	popword(void);
 void	pprompt(void);
 void	Prompt(char*);
-void	psubst(io *f, uchar *s);
+void	psubst(io*, unsigned char*);
 void	pushlist(void);
 void	pushredir(int, int, int);
 word*	pushword(char*);
--- a/sys/src/cmd/rc/getflags.c
+++ b/sys/src/cmd/rc/getflags.c
@@ -205,7 +205,8 @@
 		errs(tail);
 	}
 	errs("\n");
-	Exit("bad flags");
+	setstatus("bad flags");
+	Exit();
 }
 
 static void
--- a/sys/src/cmd/rc/glob.c
+++ b/sys/src/cmd/rc/glob.c
@@ -21,7 +21,7 @@
 }
 
 static int
-globcmp(void *s, void *t)
+globcmp(const void *s, const void *t)
 {
 	return strcmp(*(char**)s, *(char**)t);
 }
@@ -70,11 +70,13 @@
 	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);
 
@@ -140,32 +142,51 @@
  * Return a pointer to the next utf code in the string,
  * not jumping past nuls in broken utf codes!
  */
-
 static char*
 nextutf(char *p)
 {
-	Rune dummy;
+	int i, n, c = *p;
 
-	return p + chartorune(&dummy, 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)
 {
-	Rune r;
+	int c = *p;
 
-	chartorune(&r, p);
-	return r;
+	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)
 {
--- a/sys/src/cmd/rc/havefork.c
+++ b/sys/src/cmd/rc/havefork.c
@@ -50,7 +50,7 @@
 
 	switch(pid = Fork()){
 	case -1:
-		Xerror("try again");
+		Xerror2("try again", Errstr());
 		break;
 	case 0:
 		clearwaitpids();
@@ -76,12 +76,12 @@
 	int pfd[2];
 
 	if(pipe(pfd)<0){
-		Xerror("can't get pipe");
+		Xerror2("can't get pipe", Errstr());
 		return;
 	}
 	switch(pid = Fork()){
 	case -1:
-		Xerror("try again");
+		Xerror2("try again", Errstr());
 		break;
 	case 0:
 		clearwaitpids();
@@ -114,12 +114,12 @@
 	io *f;
 
 	if(pipe(pfd)<0){
-		Xerror("can't make pipe");
+		Xerror2("can't make pipe", Errstr());
 		return;
 	}
 	switch(pid = Fork()){
 	case -1:
-		Xerror("try again");
+		Xerror2("try again", Errstr());
 		Close(pfd[PRD]);
 		Close(pfd[PWR]);
 		return;
@@ -146,7 +146,7 @@
 		closeio(f);
 		free(split);
 
-		Waitfor(pid, 0);
+		Waitfor(pid);
 
 		runq->pc = runq->code[runq->pc].i;
 		return;
@@ -163,7 +163,7 @@
 	int sidefd, mainfd;
 
 	if(pipe(pfd)<0){
-		Xerror("can't get pipe");
+		Xerror2("can't get pipe", Errstr());
 		return;
 	}
 	if(p->code[pc].i==READ){
@@ -176,7 +176,7 @@
 	}
 	switch(pid = Fork()){
 	case -1:
-		Xerror("try again");
+		Xerror2("try again", Errstr());
 		break;
 	case 0:
 		clearwaitpids();
@@ -205,7 +205,7 @@
 
 	switch(pid = Fork()){
 	case -1:
-		Xerror("try again");
+		Xerror2("try again", Errstr());
 		break;
 	case 0:
 		clearwaitpids();
@@ -214,7 +214,8 @@
 		break;
 	default:
 		addwaitpid(pid);
-		Waitfor(pid, 1);
+		while(Waitfor(pid) < 0)
+			;
 		runq->pc = runq->code[runq->pc].i;
 		break;
 	}
--- a/sys/src/cmd/rc/here.c
+++ b/sys/src/cmd/rc/here.c
@@ -3,7 +3,7 @@
 #include "io.h"
 #include "fns.h"
 
-void psubst(io*, uchar*);
+void psubst(io*, unsigned char*);
 void pstrs(io*, word*);
 
 char*
@@ -48,11 +48,12 @@
 }
 
 void
-psubst(io *f, uchar *s)
+psubst(io *f, unsigned char *s)
 {
-	int savec, n;
-	uchar *t, *u;
+	unsigned char *t, *u;
 	word *star;
+	int savec, n;
+
 	while(*s){
 		if(*s!='$'){
 			if(0xa0 <= *s && *s <= 0xf5){
--- a/sys/src/cmd/rc/io.c
+++ b/sys/src/cmd/rc/io.c
@@ -36,9 +36,6 @@
 		case 'q':
 			pwrd(f, va_arg(ap, char *));
 			break;
-		case 'r':
-			pstr(f, Errstr());
-			break;
 		case 's':
 			pstr(f, va_arg(ap, char *));
 			break;
@@ -141,7 +138,7 @@
 pwrd(io *f, char *s)
 {
 	char *t;
-	for(t = s;*t;t++) if(*t >= 0 && needsrcquote(*t)) break;
+	for(t = s;*t;t++) if(*t >= 0 && strchr("`^#*[]=|\\?${}()'<>&;", *t)) break;
 	if(t==s || *t)
 		pquo(f, s);
 	else pstr(f, s);
@@ -148,16 +145,16 @@
 }
 
 void
-pptr(io *f, void *v)
+pptr(io *f, void *p)
 {
+	static char hex[] = "0123456789ABCDEF";
+	unsigned long long v;
 	int n;
-	uintptr p;
 
-	p = (uintptr)v;
-	if(sizeof(uintptr) == sizeof(uvlong) && p>>32)
-		for(n = 60;n>=32;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]);
-
-	for(n = 28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]);
+	v = (unsigned long 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
@@ -212,7 +209,7 @@
 }
 
 io*
-newio(uchar *buf, int len, int fd)
+newio(unsigned char *buf, int len, int fd)
 {
 	io *f = new(io);
 	f->buf = buf;
@@ -228,7 +225,7 @@
 io*
 openiostr(void)
 {
-	uchar *buf = emalloc(100+1);
+	unsigned char *buf = emalloc(100+1);
 	memset(buf, '\0', 100+1);
 	return newio(buf, 100, -1);
 }
@@ -258,7 +255,7 @@
  * characters from buf.
  */
 io*
-openiocore(uchar *buf, int len)
+openiocore(void *buf, int len)
 {
 	return newio(buf, len, -1);
 }
--- a/sys/src/cmd/rc/io.h
+++ b/sys/src/cmd/rc/io.h
@@ -2,12 +2,12 @@
 
 struct io{
 	int	fd;
-	uchar	*buf, *bufp, *ebuf;
+	unsigned char *buf, *bufp, *ebuf;
 	io	*next;
 };
 io *err;
 
-io *openiofd(int), *openiostr(void), *openiocore(uchar*, int);
+io *openiofd(int), *openiostr(void), *openiocore(void*, int);
 void pchr(io*, int);
 int rchr(io*);
 char *rstr(io*, char*);
--- a/sys/src/cmd/rc/lex.c
+++ b/sys/src/cmd/rc/lex.c
@@ -190,21 +190,22 @@
 static char*
 addutf(char *p, int c)
 {
-	uchar b, m;
-	int i;
+	int i, n;
 
 	p = addtok(p, c);	/* 1-byte UTF runes are special */
 	if(onebyte(c))
 		return p;
-
-	m = 0xc0;
-	b = 0x80;
-	for(i=1; i < UTFmax; i++){
-		if((c&m) == b)
+	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());
-		b = m;
-		m = (m >> 1)|0x80;
 	}
 	return p;
 }
--- a/sys/src/cmd/rc/mkfile
+++ b/sys/src/cmd/rc/mkfile
@@ -21,7 +21,7 @@
 	y.tab.$O\
 
 HFILES=rc.h\
-	x.tab.h\
+	y.tab.h\
 	io.h\
 	exec.h\
 	fns.h\
@@ -38,10 +38,9 @@
 	$YFILES\
 	${TARG:%=/386/bin/%}\
 
+CFLAGS=$CFLAGS -DPlan9
+
 </sys/src/cmd/mkone
 
-x.tab.h: y.tab.h
-	cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
-
 clean:V:
-	rm -f [$OS].out *.[$OS] [xy].tab.? y.debug $TARG
+	rm -f [$OS].out *.[$OS] y.tab.? y.debug $TARG
--- a/sys/src/cmd/rc/plan9.c
+++ b/sys/src/cmd/rc/plan9.c
@@ -47,6 +47,10 @@
 	0
 };
 
+/*
+ * finit could be removed but is kept for
+ * backwards compatibility, see: rcmain.plan9
+ */
 static void
 execfinit(void)
 {
@@ -53,7 +57,7 @@
 	char *cmds = estrdup("for(i in '/env/fn#'*){. -bq $i}\n");
 	int line = runq->line;
 	poplist();
-	execcmds(openiocore((uchar*)cmds, strlen(cmds)), estrdup(srcfile(runq)), runq->local, runq->redir);
+	execcmds(openiocore(cmds, strlen(cmds)), estrdup(srcfile(runq)), runq->local, runq->redir);
 	runq->lex->line = line;
 	runq->lex->qflag = 1;
 }
@@ -131,7 +135,7 @@
 
 	dir = Open(Env("", 0), 0);
 	if(dir<0){
-		pfmt(err, "%s: can't open: %r\n", argv0);
+		pfmt(err, "%s: can't open: %s\n", argv0, Errstr());
 		return;
 	}
 	for(;;){
@@ -169,7 +173,7 @@
 }
 
 int
-Waitfor(int pid, int)
+Waitfor(int pid)
 {
 	thread *p;
 	Waitmsg *w;
@@ -187,7 +191,8 @@
 		for(p = runq->ret;p;p = p->ret)
 			if(p->pid==w->pid){
 				p->pid=-1;
-				strcpy(p->status, w->msg);
+				p->status = estrdup(w->msg);
+				break;
 			}
 		free(w);
 	}
@@ -206,7 +211,7 @@
 	if(v->changed){
 		v->changed = 0;
 		if((fd = Creat(Env(v->name, 0)))<0)
-			pfmt(err, "%s: can't open: %r\n", argv0);
+			pfmt(err, "%s: can't open: %s\n", argv0, Errstr());
 		else{
 			f = openiofd(fd);
 			for(w = v->val;w;w = w->next){
@@ -220,7 +225,7 @@
 	if(v->fnchanged){
 		v->fnchanged = 0;
 		if((fd = Creat(Env(v->name, 1)))<0)
-			pfmt(err, "%s: can't open: %r\n", argv0);
+			pfmt(err, "%s: can't open: %s\n", argv0, Errstr());
 		else{
 			f = openiofd(fd);
 			if(v->fn)
@@ -420,16 +425,15 @@
 }
 
 int
-Dup1(int)
+Dup1(int a)
 {
-	return -1;
+	return dup(a, -1);
 }
 
 void
-Exit(char *stat)
+Exit(void)
 {
 	Updenv();
-	setstatus(stat);
 	exits(truestatus()?"":getstatus());
 }
 
@@ -459,8 +463,7 @@
 void
 Abort(void)
 {
-	pfmt(err, "aborting\n");
-	Exit("aborting");
+	abort();
 }
 
 static int newwdir;
--- a/sys/src/cmd/rc/rc.h
+++ b/sys/src/cmd/rc/rc.h
@@ -1,28 +1,29 @@
 /*
  * Plan9 is defined for plan 9
- * V9 is defined for 9th edition
- * Sun is defined for sun-os
+ * otherwise its UNIX.
  * Please don't litter the code with ifdefs.  The three below (and one in
  * getflags) should be enough.
  */
-#define	Plan9
-#ifdef	Plan9
+#ifdef Plan9
 #include <u.h>
 #include <libc.h>
-#define	NSIG	32
+#define NSIG	32
 #define	SIGINT	2
 #define	SIGQUIT	3
-#endif
-#ifdef	V9
+#else
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
 #include <signal.h>
-#include <libc.h>
+#ifndef NSIG
+#define NSIG 32
 #endif
-#ifdef Sun
-#include <signal.h>
 #endif
+
 #define	YYMAXDEPTH	500
 #ifndef PAREN
-#include "x.tab.h"
+#include "y.tab.h"
 #endif
 typedef struct tree tree;
 typedef struct word word;
@@ -143,10 +144,13 @@
  */
 #define	GLOB	((char)0x01)
 /*
- * onebyte(c)
- * Is c the first character of a one-byte utf sequence?
+ * Is c the first character of a utf sequence?
  */
-#define	onebyte(c)	((c&0x80)==0x00)
+#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 **argp;
 extern char **args;
--- /dev/null
+++ b/sys/src/cmd/rc/rcmain.plan9
@@ -1,0 +1,41 @@
+# rcmain: Plan 9 version
+if(~ $#home 0) home=/
+if(~ $#ifs 0) ifs=' 	
+'
+switch($#prompt){
+case 0
+	prompt=('% ' '	')
+case 1
+	prompt=($prompt '	')
+}
+if(~ $rcname ?.out) prompt=('broken! ' '	')
+if(flag p) path=/bin
+if not{
+	for(i in '/env/fn#'*){
+		. -bq $i
+	}
+	if(~ $#path 0) path=(/bin .)
+}
+fn sigexit
+if(! ~ $#cflag 0){
+	if(flag l){
+		. -q /rc/lib/rcmain.local
+		. -q $home/lib/profile
+	}
+	status=''
+	eval $cflag
+}
+if not if(flag i){
+	if(flag l){
+		. -q /rc/lib/rcmain.local
+		. -q $home/lib/profile
+	}
+	status=''
+	if(! ~ $#* 0) . $*
+	. -i '#d/0'
+}
+if not if(~ $#* 0) . '#d/0'
+if not{
+	status=''
+	. $*
+}
--- /dev/null
+++ b/sys/src/cmd/rc/rcmain.unix
@@ -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=''
+	. $*
+}
--- a/sys/src/cmd/rc/simple.c
+++ b/sys/src/cmd/rc/simple.c
@@ -94,13 +94,13 @@
 		}
 		else{
 			if((pid = execforkexec()) < 0){
-				Xerror("try again");
+				Xerror2("try again", Errstr());
 				return;
 			}
+			poplist();
 
 			/* interrupts don't get us out */
-			poplist();
-			while(Waitfor(pid, 1) < 0)
+			while(Waitfor(pid) < 0)
 				;
 		}
 	}
@@ -177,7 +177,7 @@
 
 	popword();	/* "exec" */
 	if(runq->argv->words==0){
-		Xerror1("empty argument list");
+		Xerror1("exec: empty argument list");
 		return;
 	}
 	argv = mkargv(runq->argv->words);
@@ -189,7 +189,7 @@
 	}
 	setstatus(Errstr());
 	pfln(err, srcfile(runq), runq->line);
-	pfmt(err, ": %s: %r\n", argv[1]);
+	pfmt(err, ": %s: %s\n", argv[1], getstatus());
 	Xexit();
 }
 
@@ -226,7 +226,7 @@
 			free(dir);
 		}
 		if(cdpath==0)
-			pfmt(err, "Can't cd %s: %r\n", a->word);
+			pfmt(err, "Can't cd %s: %s\n", a->word, Errstr());
 		break;
 	case 1:
 		a = vlook("home")->val;
@@ -234,7 +234,7 @@
 			if(Chdir(a->word)>=0)
 				setstatus("");
 			else
-				pfmt(err, "Can't cd %s: %r\n", a->word);
+				pfmt(err, "Can't cd %s: %s\n", a->word, Errstr());
 		}
 		else
 			pfmt(err, "Can't cd -- $home empty\n");
@@ -347,7 +347,7 @@
 	pfln(f, srcfile(runq), runq->line);
 	pstr(f, " *eval*");
 
-	execcmds(openiocore((uchar*)cmds, len), closeiostr(f), runq->local, runq->redir);
+	execcmds(openiocore(cmds, len), closeiostr(f), runq->local, runq->redir);
 }
 
 void
@@ -406,7 +406,8 @@
 		free(file);
 	}
 	if(fd<0){
-		if(!qflag) Xerror(".: can't open");
+		if(!qflag)
+			Xerror3(". can't open", argv->word, Errstr());
 		freewords(argv);
 		return;
 	}
@@ -436,7 +437,7 @@
 	char *letter, *val;
 	switch(count(runq->argv->words)){
 	case 2:
-		setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");
+		setstatus(flag[(unsigned char)runq->argv->words->next->word[0]]?"":"flag not set");
 		break;
 	case 3:
 		letter = runq->argv->words->next->word;
@@ -443,11 +444,11 @@
 		val = runq->argv->words->next->next->word;
 		if(strlen(letter)==1){
 			if(strcmp(val, "+")==0){
-				flag[(uchar)letter[0]] = flagset;
+				flag[(unsigned char)letter[0]] = flagset;
 				break;
 			}
 			if(strcmp(val, "-")==0){
-				flag[(uchar)letter[0]] = 0;
+				flag[(unsigned char)letter[0]] = 0;
 				break;
 			}
 		}
@@ -526,10 +527,10 @@
 		Xerror1("Usage: wait [pid]");
 		return;
 	case 2:
-		Waitfor(atoi(runq->argv->words->next->word), 0);
+		Waitfor(atoi(runq->argv->words->next->word));
 		break;
 	case 1:
-		Waitfor(-1, 0);
+		Waitfor(-1);
 		break;
 	}
 	poplist();
--- a/sys/src/cmd/rc/subr.c
+++ b/sys/src/cmd/rc/subr.c
@@ -41,7 +41,7 @@
 		pstr(fd, argv0);
 }
 
-char *bp;
+static char *bp;
 
 static void
 iacvt(int n)
@@ -69,5 +69,7 @@
 	pfmt(err, "%s: ", argv0);
 	pfmt(err, s, n);
 	pchr(err, '\n');
+	flushio(err);
+
 	Abort();
 }
--- a/sys/src/cmd/rc/trap.c
+++ b/sys/src/cmd/rc/trap.c
@@ -11,10 +11,10 @@
 	var *trapreq;
 	word *starval;
 	starval = vlook("*")->val;
-	while(ntrap) for(i = 0;i!=NSIG;i++) while(trap[i]){
+	while(ntrap) for(i = 0;i<NSIG;i++) while(trap[i]){
 		--trap[i];
 		--ntrap;
-		if(getpid()!=mypid) Exit(getstatus());
+		if(getpid()!=mypid) Exit();
 		trapreq = vlook(Signame[i]);
 		if(trapreq->fn)
 			startfunc(trapreq, copywords(starval, (word*)0), (var*)0, (redir*)0);
@@ -27,6 +27,6 @@
 			 */
 			while(!runq->iflag) Xreturn();
 		}
-		else Exit(getstatus());
+		else Exit();
 	}
 }
--- /dev/null
+++ b/sys/src/cmd/rc/unix.c
@@ -1,0 +1,420 @@
+/*
+ * Unix 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 <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
+};
+
+char Rcmain[] = PREFIX "/lib/rcmain";
+char Fdprefix[] = "/dev/fd/";
+
+char *Signame[NSIG];
+
+#define SEP '\1'
+extern char **environ;
+static char **envp;
+
+static void
+Xrdfn(void)
+{
+	char *s;
+	int len;
+
+	for(;*envp;envp++){
+		for(s=*envp;*s && *s!='(' && *s!='=';s++);
+		switch(*s){
+		case '(':		/* Bourne again */
+			if(strncmp(s, "()fn ", 5)!=0)
+				continue;
+			s=estrdup(s+2);
+			len=strlen(s);
+			s[len++]='\n';
+			envp++;
+			runq->pc--;	/* re-execute */
+			execcmds(openiocore(s, len), estrdup("*environ*"), runq->local, runq->redir);
+			runq->lex->qflag = 1;
+			return;
+		default:
+			continue;
+		}
+	}
+}
+
+static void
+execfinit(void)
+{
+	static union code rdfns[5];
+	if(rdfns[0].i==0){
+		rdfns[0].i = 1;
+		rdfns[1].s = "*rdfns*";
+		rdfns[2].f = Xrdfn;
+		rdfns[3].f = Xreturn;
+		rdfns[4].f = 0;
+	}
+	poplist();
+	envp=environ;
+	start(rdfns, 2, runq->local, runq->redir);
+}
+
+static int
+cmpenv(const void *aa, const void *ab)
+{
+	return strcmp(*(char**)aa, *(char**)ab);
+}
+
+static char**
+mkenv(void)
+{
+	char **env, **ep, *p, *q;
+	struct var **h, *v;
+	struct word *a;
+	int nvar = 0, nchr = 0, sep;
+
+	/*
+	 * Slightly kludgy loops look at locals then globals.
+	 * locals no longer exist - geoff
+	 */
+	for(h = gvar-1; h != &gvar[NVAR]; h++)
+	for(v = h >= gvar? *h: runq->local; v ;v = v->next){
+		if((v==vlook(v->name)) && v->val){
+			nvar++;
+			nchr+=strlen(v->name)+1;
+			for(a = v->val;a;a = a->next)
+				nchr+=strlen(a->word)+1;
+		}
+		if(v->fn){
+			nvar++;
+			nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8;
+		}
+	}
+	env = (char **)emalloc((nvar+1)*sizeof(char *)+nchr);
+	ep = env;
+	p = (char *)&env[nvar+1];
+	for(h = gvar-1; h != &gvar[NVAR]; h++)
+	for(v = h >= gvar? *h: runq->local;v;v = v->next){
+		if((v==vlook(v->name)) && v->val){
+			*ep++=p;
+			q = v->name;
+			while(*q) *p++=*q++;
+			sep='=';
+			for(a = v->val;a;a = a->next){
+				*p++=sep;
+				sep = SEP;
+				q = a->word;
+				while(*q) *p++=*q++;
+			}
+			*p++='\0';
+		}
+		if(v->fn){
+			*ep++=p;
+			*p++='#'; *p++='('; *p++=')';	/* to fool Bourne */
+			*p++='f'; *p++='n'; *p++=' ';
+			q = v->name;
+			while(*q) *p++=*q++;
+			*p++=' ';
+			q = v->fn[v->pc-1].s;
+			while(*q) *p++=*q++;
+			*p++='\0';
+		}
+	}
+	*ep = 0;
+	qsort((void *)env, nvar, sizeof ep[0], cmpenv);
+	return env;	
+}
+
+static word*
+envval(char *s)
+{
+	char *t, c;
+	word *v;
+	for(t=s;*t&&*t!=SEP;t++);
+	c=*t;
+	*t='\0';
+	v=newword(s, c=='\0'?(word*)0:envval(t+1));
+	*t=c;
+	return v;
+}
+
+void
+Vinit(void)
+{
+	char *s;
+
+	for(envp=environ;*envp;envp++){
+		for(s=*envp;*s && *s!='(' && *s!='=';s++);
+		switch(*s){
+		case '=':
+			*s='\0';
+			setvar(*envp, envval(s+1));
+			*s='=';
+			break;
+		default: continue;
+		}
+	}
+}
+
+static void
+sighandler(int sig)
+{
+	trap[sig]++;
+	ntrap++;
+}
+
+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
+	}
+}
+
+char*
+Errstr(void)
+{
+	return strerror(errno);
+}
+
+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(Eintr()) return -1;
+	return 0;
+}
+
+static char **nextenv;
+
+void
+Updenv(void)
+{
+	if(nextenv){
+		free(nextenv);
+		nextenv = NULL;
+	}
+	if(err)
+		flushio(err);
+}
+
+void
+Exec(char **argv)
+{
+	if(nextenv==NULL) nextenv=mkenv();
+	execve(argv[0], argv+1, nextenv);
+}
+
+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);
+}
+
+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 lseek(fd, cnt, whence);
+}
+
+int
+Executable(char *file)
+{
+	return access(file, 01)==0;
+}
+
+int
+Open(char *file, int mode)
+{
+	static int tab[] = {O_RDONLY,O_WRONLY,O_RDWR,O_RDONLY};
+	int fd = open(file, tab[mode&3]);
+	if(fd >= 0 && mode == 3)
+		unlink(file);
+	return fd;
+}
+
+void
+Close(int fd)
+{
+	close(fd);
+}
+
+int
+Creat(char *file)
+{
+	return creat(file, 0666L);
+}
+
+int
+Dup(int a, int b)
+{
+	return dup2(a, b);
+}
+
+int
+Dup1(int a)
+{
+	return dup(a);
+}
+
+void
+Exit(void)
+{
+	Updenv();
+	exit(truestatus()?0:1);
+}
+
+int
+Eintr(void)
+{
+	return errno==EINTR;
+}
+
+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);
+}