aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2021-10-13 09:08:59 -0700
committerNicholas Noll <nbnoll@eml.cc>2021-10-13 09:08:59 -0700
commit2ade60747db41771498ab2b85ce6e3c3389f2c26 (patch)
tree302927100158a1b50954e5ca4cb5c564bf19beec
parentd1a19f0d477a6249d8af9322317b8434b86260ea (diff)
feat(rc): added unix port of rc with linenoise
-rw-r--r--sys/cmd/rc/Makefile56
-rw-r--r--sys/cmd/rc/code.c1
-rw-r--r--sys/cmd/rc/exec.h81
-rw-r--r--sys/cmd/rc/fns.h68
-rw-r--r--sys/cmd/rc/getflags.c244
-rw-r--r--sys/cmd/rc/getflags.h7
-rw-r--r--sys/cmd/rc/havefork.c300
-rw-r--r--sys/cmd/rc/here.c150
-rw-r--r--sys/cmd/rc/history.c4
-rw-r--r--sys/cmd/rc/io.h26
-rw-r--r--sys/cmd/rc/linenoise.c1236
-rw-r--r--sys/cmd/rc/linenoise.h77
-rw-r--r--sys/cmd/rc/pcmd.c147
-rw-r--r--sys/cmd/rc/pfnc.c71
-rw-r--r--sys/cmd/rc/prompt.c101
-rw-r--r--sys/cmd/rc/rcmain.unix35
-rw-r--r--sys/cmd/rc/rules.mk3
-rw-r--r--sys/cmd/rc/subr.c77
-rw-r--r--sys/cmd/rc/syn.y91
-rw-r--r--sys/cmd/rc/trap.c37
-rw-r--r--sys/cmd/rc/unix.c627
-rw-r--r--sys/cmd/rc/unix.h56
-rw-r--r--sys/cmd/rc/x.tab.h114
-rw-r--r--sys/cmd/rc/y.tab.c1979
-rw-r--r--sys/cmd/rc/y.tab.h114
25 files changed, 5701 insertions, 1 deletions
diff --git a/sys/cmd/rc/Makefile b/sys/cmd/rc/Makefile
new file mode 100644
index 0000000..d4b9864
--- /dev/null
+++ b/sys/cmd/rc/Makefile
@@ -0,0 +1,56 @@
+YACC=yacc -d
+PREFIX=/usr/local
+WARNINGS=-Wall -Wextra -Wno-parentheses -Wno-missing-braces -Wno-missing-field-initializers -Wno-comment -Wno-sign-compare -Wno-unused-parameter -Wno-implicit-fallthrough #-Werror
+CFLAGS=-g $(WARNINGS) -DPREFIX=\"$(PREFIX)\" -D_XOPEN_SOURCE=500
+LFLAGS=/home/nolln/root/lib/libreadline.a /home/nolln/root/lib/libhistory.a /home/nolln/root/lib/libncursesw.a
+
+TARG=rc
+
+OFILES=\
+ code.o\
+ exec.o\
+ getflags.o\
+ glob.o\
+ here.o\
+ io.o\
+ lex.o\
+ pcmd.o\
+ pfnc.o\
+ simple.o\
+ subr.o\
+ trap.o\
+ tree.o\
+ var.o\
+ y.tab.o\
+ unix.o\
+ havefork.o\
+ prompt.o\
+
+HFILES=\
+ rc.h\
+ x.tab.h\
+ io.h\
+ exec.h\
+ fns.h\
+
+YFILES=syn.y
+
+all: $(TARG)
+
+$(TARG): $(OFILES)
+ $(CC) $(ARCHS) -o $(TARG) $(OFILES) $(LFLAGS)
+
+%.o: %.c $(HFILES)
+ $(CC) $(ARCHS) $(CFLAGS) -c $*.c
+
+y.tab.h y.tab.c: $(YFILES)
+ $(YACC) $(YFLAGS) $(YFILES)
+
+x.tab.h: y.tab.h
+ cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
+
+%: %.c $(HFILES)
+ $(CC) $(ARCHS) -o $@ $< $(LFLAGS)
+
+clean:
+ rm -f $(TARG) *.o $(CLEANFILES)
diff --git a/sys/cmd/rc/code.c b/sys/cmd/rc/code.c
index fbb5435..81deac2 100644
--- a/sys/cmd/rc/code.c
+++ b/sys/cmd/rc/code.c
@@ -3,6 +3,7 @@
#include "exec.h"
#include "fns.h"
#include "getflags.h"
+
#define c0 t->child[0]
#define c1 t->child[1]
#define c2 t->child[2]
diff --git a/sys/cmd/rc/exec.h b/sys/cmd/rc/exec.h
new file mode 100644
index 0000000..009684e
--- /dev/null
+++ b/sys/cmd/rc/exec.h
@@ -0,0 +1,81 @@
+/*
+ * Definitions used in the interpreter
+ */
+extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void);
+extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void);
+extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void);
+extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void);
+extern void Xrdwr(void);
+extern void Xrdfn(void), Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void);
+extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void);
+extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void);
+extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void);
+extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void);
+extern void Xerror(char*);
+extern void Xerror1(char*);
+/*
+ * word lists are in correct order,
+ * i.e. word0->word1->word2->word3->0
+ */
+struct word{
+ char *word;
+ word *next;
+};
+struct list{
+ word *words;
+ list *next;
+};
+word *newword(char *, word *), *copywords(word *, word *);
+struct redir{
+ char type; /* what to do */
+ short from, to; /* what to do it to */
+ struct redir *next; /* what else to do (reverse order) */
+};
+#define NSTATUS ERRMAX /* length of status (from plan 9) */
+/*
+ * redir types
+ */
+#define ROPEN 1 /* dup2(from, to); close(from); */
+#define RDUP 2 /* dup2(from, to); */
+#define RCLOSE 3 /* close(from); */
+struct thread{
+ union code *code; /* code for this thread */
+ int pc; /* code[pc] is the next instruction */
+ struct list *argv; /* argument stack */
+ struct redir *redir; /* redirection stack */
+ struct redir *startredir; /* redir inheritance point */
+ struct var *local; /* list of local variables */
+ char *cmdfile; /* file name in Xrdcmd */
+ struct io *cmdfd; /* file descriptor for Xrdcmd */
+ int iflast; /* static `if not' checking */
+ int eof; /* is cmdfd at eof? */
+ int iflag; /* interactive? */
+ int lineno; /* linenumber */
+ int pid; /* process for Xpipewait to wait for */
+ char status[NSTATUS]; /* status for Xpipewait */
+ tree *treenodes; /* tree nodes created by this process */
+ thread *ret; /* who continues when this finishes */
+};
+
+code *codecopy(code*);
+
+extern thread *runq;
+extern code *codebuf; /* compiler output */
+extern int ntrap; /* number of outstanding traps */
+extern int trap[NSIG]; /* number of outstanding traps per type */
+
+struct builtin{
+ char *name;
+ void (*fnc)(void);
+};
+
+extern struct builtin Builtin[];
+
+extern int eflagok; /* kludge flag so that -e doesn't exit in startup */
+extern int havefork;
+
+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 *);
diff --git a/sys/cmd/rc/fns.h b/sys/cmd/rc/fns.h
new file mode 100644
index 0000000..6ed2ca9
--- /dev/null
+++ b/sys/cmd/rc/fns.h
@@ -0,0 +1,68 @@
+void Abort(void);
+void Closedir(int);
+int Creat(char*);
+int Dup(int, int);
+int Dup1(int);
+int Eintr(void);
+int Executable(char*);
+void Execute(word*, word*);
+void Exit(char*);
+int ForkExecute(char*, char**, int, int, int);
+int Globsize(char*);
+int Isatty(int);
+void Memcpy(char*, char*, long);
+void Noerror(void);
+int Opendir(char*);
+long Read(int, char*, long);
+int Readdir(int, char*, int);
+long Seek(int, long, long);
+void Trapinit(void);
+void Unlink(char*);
+void Updenv(void);
+void Vinit(void);
+int Waitfor(int, int);
+long Write(int, char*, long);
+void addwaitpid(int);
+int advance(void);
+int back(int);
+void cleanhere(char*);
+void codefree(code*);
+int compile(tree*);
+char * list2str(word*);
+int count(word*);
+void deglob(char*);
+void delwaitpid(int);
+void dotrap(void);
+void freenodes(void);
+void freewords(word*);
+void globlist(void);
+int havewaitpid(int);
+int idchr(int);
+void inttoascii(char*, long);
+void kinit(void);
+int mapfd(int);
+int match(char*, char*, int);
+int matchfn(char*, char*);
+char** mkargv(word*);
+void clearwaitpids(void);
+void panic(char*, int);
+void pathinit(void);
+void poplist(void);
+void popword(void);
+void pprompt(void);
+void pushlist(void);
+void pushredir(int, int, int);
+void pushword(char*);
+void readhere(void);
+word* searchpath(char*);
+void setstatus(char*);
+void setvar(char*, word*);
+void skipnl(void);
+void start(code*, int, var*);
+int truestatus(void);
+void usage(char*);
+int wordchr(int);
+void yyerror(char*);
+int yylex(void);
+int yyparse(void);
+int octal(char*);
diff --git a/sys/cmd/rc/getflags.c b/sys/cmd/rc/getflags.c
new file mode 100644
index 0000000..09f6e3a
--- /dev/null
+++ b/sys/cmd/rc/getflags.c
@@ -0,0 +1,244 @@
+/*% cyntax -DTEST % && cc -DTEST -go # %
+ */
+#include "rc.h"
+#include "getflags.h"
+#include "fns.h"
+char *flagset[] = {"<flag>"};
+char **flag[NFLAG];
+char cmdline[NCMDLINE+1];
+char *cmdname;
+static char *flagarg="";
+static void reverse(char**, char**);
+static int scanflag(int, char*);
+static void errn(char*, int);
+static void errs(char*);
+static void errc(int);
+static int reason;
+#define RESET 1
+#define FEWARGS 2
+#define FLAGSYN 3
+#define BADFLAG 4
+static int badflag;
+
+int
+getflags(int argc, char *argv[], char *flags, int stop)
+{
+ char *s, *t;
+ int i, j, c, count;
+ flagarg = flags;
+ if(cmdname==0)
+ cmdname = argv[0];
+ s = cmdline;
+ for(i = 0;i!=argc;i++){
+ for(t = argv[i];*t;t++)
+ if(s!=&cmdline[NCMDLINE])
+ *s++=*t;
+ if(i!=argc-1 && s!=&cmdline[NCMDLINE])
+ *s++=' ';
+ }
+ *s='\0';
+ i = 1;
+ while(i!=argc){
+ if(argv[i][0]!='-' || argv[i][1]=='\0'){
+ if(stop)
+ return argc;
+ i++;
+ continue;
+ }
+ s = argv[i]+1;
+ while(*s){
+ c=*s++;
+ count = scanflag(c, flags);
+ if(count==-1)
+ return -1;
+ if(flag[c]){ reason = RESET; badflag = c; return -1; }
+ if(count==0){
+ flag[c] = flagset;
+ if(*s=='\0'){
+ for(j = i+1;j<=argc;j++)
+ argv[j-1] = argv[j];
+ --argc;
+ }
+ }
+ else{
+ if(*s=='\0'){
+ for(j = i+1;j<=argc;j++)
+ argv[j-1] = argv[j];
+ --argc;
+ s = argv[i];
+ }
+ if(argc-i<count){
+ reason = FEWARGS;
+ badflag = c;
+ return -1;
+ }
+ reverse(argv+i, argv+argc);
+ reverse(argv+i, argv+argc-count);
+ reverse(argv+argc-count+1, argv+argc);
+ argc-=count;
+ flag[c] = argv+argc+1;
+ flag[c][0] = s;
+ s="";
+ }
+ }
+ }
+ return argc;
+}
+
+static void
+reverse(char **p, char **q)
+{
+ char *t;
+ for(;p<q;p++,--q){ t=*p; *p=*q; *q = t; }
+}
+
+static int
+scanflag(int c, char *f)
+{
+ int fc, count;
+ if(0<=c && c<NFLAG)
+ while(*f){
+ if(*f==' '){
+ f++;
+ continue;
+ }
+ fc=*f++;
+ if(*f==':'){
+ f++;
+ if(*f<'0' || '9'<*f){ reason = FLAGSYN; return -1; }
+ count = 0;
+ while('0'<=*f && *f<='9') count = count*10+*f++-'0';
+ }
+ else
+ count = 0;
+ if(*f=='['){
+ do{
+ f++;
+ if(*f=='\0'){ reason = FLAGSYN; return -1; }
+ }while(*f!=']');
+ f++;
+ }
+ if(c==fc)
+ return count;
+ }
+ reason = BADFLAG;
+ badflag = c;
+ return -1;
+}
+
+void
+usage(char *tail)
+{
+ char *s, *t, c;
+ int count, nflag = 0;
+ switch(reason){
+ case RESET:
+ errs("Flag -");
+ errc(badflag);
+ errs(": set twice\n");
+ break;
+ case FEWARGS:
+ errs("Flag -");
+ errc(badflag);
+ errs(": too few arguments\n");
+ break;
+ case FLAGSYN:
+ errs("Bad argument to getflags!\n");
+ break;
+ case BADFLAG:
+ errs("Illegal flag -");
+ errc(badflag);
+ errc('\n');
+ break;
+ }
+ errs("Usage: ");
+ errs(cmdname);
+ for(s = flagarg;*s;){
+ c=*s;
+ if(*s++==' ')
+ continue;
+ if(*s==':'){
+ s++;
+ count = 0;
+ while('0'<=*s && *s<='9') count = count*10+*s++-'0';
+ }
+ else count = 0;
+ if(count==0){
+ if(nflag==0)
+ errs(" [-");
+ nflag++;
+ errc(c);
+ }
+ if(*s=='['){
+ s++;
+ while(*s!=']' && *s!='\0') s++;
+ if(*s==']')
+ s++;
+ }
+ }
+ if(nflag)
+ errs("]");
+ for(s = flagarg;*s;){
+ c=*s;
+ if(*s++==' ')
+ continue;
+ if(*s==':'){
+ s++;
+ count = 0;
+ while('0'<=*s && *s<='9') count = count*10+*s++-'0';
+ }
+ else count = 0;
+ if(count!=0){
+ errs(" [-");
+ errc(c);
+ if(*s=='['){
+ s++;
+ t = s;
+ while(*s!=']' && *s!='\0') s++;
+ errs(" ");
+ errn(t, s-t);
+ if(*s==']')
+ s++;
+ }
+ else
+ while(count--) errs(" arg");
+ errs("]");
+ }
+ else if(*s=='['){
+ s++;
+ while(*s!=']' && *s!='\0') s++;
+ if(*s==']')
+ s++;
+ }
+ }
+ if(tail){
+ errs(" ");
+ errs(tail);
+ }
+ errs("\n");
+ Exit("bad flags");
+}
+
+static void
+errn(char *s, int count)
+{
+ while(count){ errc(*s++); --count; }
+}
+
+static void
+errs(char *s)
+{
+ while(*s) errc(*s++);
+}
+#define NBUF 80
+static char buf[NBUF], *bufp = buf;
+
+static void
+errc(int c)
+{
+ *bufp++=c;
+ if(bufp==&buf[NBUF] || c=='\n'){
+ Write(2, buf, bufp-buf);
+ bufp = buf;
+ }
+}
diff --git a/sys/cmd/rc/getflags.h b/sys/cmd/rc/getflags.h
new file mode 100644
index 0000000..9afb32e
--- /dev/null
+++ b/sys/cmd/rc/getflags.h
@@ -0,0 +1,7 @@
+#define NFLAG 128
+#define NCMDLINE 512
+extern char **flag[NFLAG];
+extern char cmdline[NCMDLINE+1];
+extern char *cmdname;
+extern char *flagset[];
+int getflags(int, char*[], char*, int);
diff --git a/sys/cmd/rc/havefork.c b/sys/cmd/rc/havefork.c
new file mode 100644
index 0000000..02120e8
--- /dev/null
+++ b/sys/cmd/rc/havefork.c
@@ -0,0 +1,300 @@
+#ifdef Plan9
+#include <u.h>
+#endif
+#include <signal.h>
+#if defined(PLAN9PORT) && defined(__sun__)
+# define BSD_COMP /* sigh. for TIOCNOTTY */
+#endif
+#include <sys/ioctl.h>
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+int havefork = 1;
+
+void
+Xasync(void)
+{
+ int null = open("/dev/null", 0);
+ int tty;
+ int pid;
+ char npid[10];
+ if(null<0){
+ Xerror("Can't open /dev/null\n");
+ return;
+ }
+ switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){
+ case -1:
+ close(null);
+ Xerror("try again");
+ break;
+ case 0:
+ clearwaitpids();
+ /*
+ * I don't know what the right thing to do here is,
+ * so this is all experimentally determined.
+ * If we just dup /dev/null onto 0, then running
+ * ssh foo & will reopen /dev/tty, try to read a password,
+ * get a signal, and repeat, in a tight loop, forever.
+ * Arguably this is a bug in ssh (it behaves the same
+ * way under bash as under rc) but I'm fixing it here
+ * anyway. If we dissociate the process from the tty,
+ * then it won't be able to open /dev/tty ever again.
+ * The SIG_IGN on SIGTTOU makes writing the tty
+ * (via fd 1 or 2, for example) succeed even though
+ * our pgrp is not the terminal's controlling pgrp.
+ */
+ if((tty = open("/dev/tty", OREAD)) >= 0){
+ /*
+ * Should make reads of tty fail, writes succeed.
+ */
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ ioctl(tty, TIOCNOTTY);
+ close(tty);
+ }
+ if(isatty(0))
+ pushredir(ROPEN, null, 0);
+ else
+ close(null);
+ start(runq->code, runq->pc+1, runq->local);
+ runq->ret = 0;
+ break;
+ default:
+ addwaitpid(pid);
+ close(null);
+ runq->pc = runq->code[runq->pc].i;
+ inttoascii(npid, pid);
+ setvar("apid", newword(npid, (word *)0));
+ break;
+ }
+}
+
+void
+Xpipe(void)
+{
+ struct thread *p = runq;
+ int pc = p->pc, forkid;
+ int lfd = p->code[pc++].i;
+ int rfd = p->code[pc++].i;
+ int pfd[2];
+ if(pipe(pfd)<0){
+ Xerror("can't get pipe");
+ return;
+ }
+ switch(forkid = fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ clearwaitpids();
+ start(p->code, pc+2, runq->local);
+ runq->ret = 0;
+ close(pfd[PRD]);
+ pushredir(ROPEN, pfd[PWR], lfd);
+ break;
+ default:
+ addwaitpid(forkid);
+ start(p->code, p->code[pc].i, runq->local);
+ close(pfd[PWR]);
+ pushredir(ROPEN, pfd[PRD], rfd);
+ p->pc = p->code[pc+1].i;
+ p->pid = forkid;
+ break;
+ }
+}
+
+/*
+ * Who should wait for the exit from the fork?
+ */
+void
+Xbackq(void)
+{
+ struct thread *p = runq;
+ char wd[8193];
+ int c, n;
+ char *s, *ewd=&wd[8192], *stop, *q;
+ struct io *f;
+ var *ifs = vlook("ifs");
+ word *v, *nextv;
+ int pfd[2];
+ int pid;
+ Rune r;
+ stop = ifs->val?ifs->val->word:"";
+ if(pipe(pfd)<0){
+ Xerror("can't make pipe");
+ return;
+ }
+ switch(pid = fork()){
+ case -1:
+ Xerror("try again");
+ close(pfd[PRD]);
+ close(pfd[PWR]);
+ return;
+ case 0:
+ clearwaitpids();
+ close(pfd[PRD]);
+ start(runq->code, runq->pc+1, runq->local);
+ pushredir(ROPEN, pfd[PWR], 1);
+ return;
+ default:
+ addwaitpid(pid);
+ close(pfd[PWR]);
+ f = openfd(pfd[PRD]);
+ s = wd;
+ v = 0;
+ while((c = rchr(f))!=EOF){
+ if(s != ewd) {
+ *s++ = c;
+ for(q=stop; *q; q+=n) {
+ n = chartorune(&r, q);
+ if(s-wd >= n && memcmp(s-n, q, n) == 0) {
+ s -= n;
+ goto stop;
+ }
+ }
+ continue;
+ }
+ stop:
+ if(s != wd) {
+ *s = '\0';
+ v = newword(wd, v);
+ }
+ s = wd;
+ }
+ if(s!=wd){
+ *s='\0';
+ v = newword(wd, v);
+ }
+ closeio(f);
+ Waitfor(pid, 0);
+ /* v points to reversed arglist -- reverse it onto argv */
+ while(v){
+ nextv = v->next;
+ v->next = runq->argv->words;
+ runq->argv->words = v;
+ v = nextv;
+ }
+ p->pc = p->code[p->pc].i;
+ return;
+ }
+}
+
+void
+Xpipefd(void)
+{
+ struct thread *p = runq;
+ int pc = p->pc, pid;
+ char name[40];
+ int pfd[2];
+ struct { int sidefd, mainfd; } fd[2], *r, *w;
+
+ r = &fd[0];
+ w = &fd[1];
+ switch(p->code[pc].i){
+ case READ:
+ w = nil;
+ break;
+ case WRITE:
+ r = nil;
+ }
+
+ if(r){
+ if(pipe(pfd)<0){
+ Xerror("can't get pipe");
+ return;
+ }
+ r->sidefd = pfd[PWR];
+ r->mainfd = pfd[PRD];
+ }
+ if(w){
+ if(pipe(pfd)<0){
+ Xerror("can't get pipe");
+ return;
+ }
+ w->sidefd = pfd[PRD];
+ w->mainfd = pfd[PWR];
+ }
+ switch(pid = fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ clearwaitpids();
+ start(p->code, pc+2, runq->local);
+ if(r){
+ close(r->mainfd);
+ pushredir(ROPEN, r->sidefd, 1);
+ }
+ if(w){
+ close(w->mainfd);
+ pushredir(ROPEN, w->sidefd, 0);
+ }
+ runq->ret = 0;
+ break;
+ default:
+ addwaitpid(pid);
+ if(w){
+ close(w->sidefd);
+ pushredir(ROPEN, w->mainfd, w->mainfd); /* so that Xpopredir can close it later */
+ strcpy(name, Fdprefix);
+ inttoascii(name+strlen(name), w->mainfd);
+ pushword(name);
+ }
+ if(r){
+ close(r->sidefd);
+ pushredir(ROPEN, r->mainfd, r->mainfd);
+ strcpy(name, Fdprefix);
+ inttoascii(name+strlen(name), r->mainfd);
+ pushword(name);
+ }
+ p->pc = p->code[pc+1].i;
+ break;
+ }
+}
+
+void
+Xsubshell(void)
+{
+ int pid;
+ switch(pid = fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ clearwaitpids();
+ start(runq->code, runq->pc+1, runq->local);
+ runq->ret = 0;
+ break;
+ default:
+ addwaitpid(pid);
+ Waitfor(pid, 1);
+ runq->pc = runq->code[runq->pc].i;
+ break;
+ }
+}
+
+int
+execforkexec(void)
+{
+ int pid;
+ int n;
+ char buf[ERRMAX];
+
+ switch(pid = fork()){
+ case -1:
+ return -1;
+ case 0:
+ clearwaitpids();
+ pushword("exec");
+ execexec();
+ strcpy(buf, "can't exec: ");
+ n = strlen(buf);
+ errstr(buf+n, ERRMAX-n);
+ Exit(buf);
+ }
+ addwaitpid(pid);
+ return pid;
+}
diff --git a/sys/cmd/rc/here.c b/sys/cmd/rc/here.c
new file mode 100644
index 0000000..17c6245
--- /dev/null
+++ b/sys/cmd/rc/here.c
@@ -0,0 +1,150 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+struct here *here, **ehere;
+int ser = 0;
+char tmp[]="/tmp/here0000.0000";
+char hex[]="0123456789abcdef";
+void psubst(io*, char*);
+void pstrs(io*, word*);
+
+void
+hexnum(char *p, int n)
+{
+ *p++=hex[(n>>12)&0xF];
+ *p++=hex[(n>>8)&0xF];
+ *p++=hex[(n>>4)&0xF];
+ *p = hex[n&0xF];
+}
+
+tree*
+heredoc(tree *tag)
+{
+ struct here *h = new(struct here);
+ if(tag->type!=WORD)
+ yyerror("Bad here tag");
+ h->next = 0;
+ if(here)
+ *ehere = h;
+ else
+ here = h;
+ ehere=&h->next;
+ h->tag = tag;
+ hexnum(&tmp[9], getpid());
+ hexnum(&tmp[14], ser++);
+ h->name = strdup(tmp);
+ return token(tmp, WORD);
+}
+/*
+ * bug: lines longer than NLINE get split -- this can cause spurious
+ * missubstitution, or a misrecognized EOF marker.
+ */
+#define NLINE 4096
+
+void
+readhere(void)
+{
+ struct here *h, *nexth;
+ io *f;
+ char *s, *tag;
+ int c, subst;
+ char line[NLINE+1];
+ for(h = here;h;h = nexth){
+ subst=!h->tag->quoted;
+ tag = h->tag->str;
+ c = Creat(h->name);
+ if(c<0)
+ yyerror("can't create here document");
+ f = openfd(c);
+ s = line;
+ pprompt();
+ while((c = rchr(runq->cmdfd))!=EOF){
+ if(c=='\n' || s==&line[NLINE]){
+ *s='\0';
+ if(tag && strcmp(line, tag)==0) break;
+ if(subst)
+ psubst(f, line);
+ else pstr(f, line);
+ s = line;
+ if(c=='\n'){
+ pprompt();
+ pchr(f, c);
+ }
+ else *s++=c;
+ }
+ else *s++=c;
+ }
+ flush(f);
+ closeio(f);
+ cleanhere(h->name);
+ nexth = h->next;
+ efree((char *)h);
+ }
+ here = 0;
+ doprompt = 1;
+}
+
+void
+psubst(io *f, char *s)
+{
+ char *t, *u;
+ int savec, n;
+ word *star;
+ while(*s){
+ if(*s!='$'){
+ if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){
+ pchr(f, *s++);
+ if(*s=='\0')
+ break;
+ }
+ else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){
+ pchr(f, *s++);
+ if(*s=='\0')
+ break;
+ pchr(f, *s++);
+ if(*s=='\0')
+ break;
+ }
+ pchr(f, *s++);
+ }
+ else{
+ t=++s;
+ if(*t=='$')
+ pchr(f, *t++);
+ else{
+ while(*t && idchr(*t)) t++;
+ savec=*t;
+ *t='\0';
+ n = 0;
+ for(u = s;*u && '0'<=*u && *u<='9';u++) n = n*10+*u-'0';
+ if(n && *u=='\0'){
+ star = vlook("*")->val;
+ if(star && 1<=n && n<=count(star)){
+ while(--n) star = star->next;
+ pstr(f, star->word);
+ }
+ }
+ else
+ pstrs(f, vlook(s)->val);
+ *t = savec;
+ if(savec=='^')
+ t++;
+ }
+ s = t;
+ }
+ }
+}
+
+void
+pstrs(io *f, word *a)
+{
+ if(a){
+ while(a->next && a->next->word){
+ pstr(f, a->word);
+ pchr(f, ' ');
+ a = a->next;
+ }
+ pstr(f, a->word);
+ }
+}
diff --git a/sys/cmd/rc/history.c b/sys/cmd/rc/history.c
new file mode 100644
index 0000000..393c183
--- /dev/null
+++ b/sys/cmd/rc/history.c
@@ -0,0 +1,4 @@
+#include <stdio.h>
+#include <readline/history.h>
+
+
diff --git a/sys/cmd/rc/io.h b/sys/cmd/rc/io.h
new file mode 100644
index 0000000..dac8b48
--- /dev/null
+++ b/sys/cmd/rc/io.h
@@ -0,0 +1,26 @@
+#define EOF (-1)
+#define NBUF 512
+struct io{
+ int fd;
+ char *bufp, *ebuf, *strp, buf[NBUF];
+};
+
+extern struct io *err;
+
+io *openfd(int), *openstr(void), *opencore(char *, int);
+int emptybuf(io*);
+void pchr(io*, int);
+int rchr(io*);
+void closeio(io*);
+void flush(io*);
+int fullbuf(io*, int);
+void pdec(io*, 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 pfnc(io*, thread*);
+void pfmt(io*, char*, ...);
diff --git a/sys/cmd/rc/linenoise.c b/sys/cmd/rc/linenoise.c
new file mode 100644
index 0000000..751b4e9
--- /dev/null
+++ b/sys/cmd/rc/linenoise.c
@@ -0,0 +1,1236 @@
+/* linenoise.c -- guerrilla line editing library against the idea that a
+ * line editing lib needs to be 20,000 lines of C code.
+ *
+ * You can find the latest source code at:
+ *
+ * http://github.com/antirez/linenoise
+ *
+ * Does a number of crazy assumptions that happen to be true in 99.9999% of
+ * the 2010 UNIX computers around.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * References:
+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+ *
+ * Todo list:
+ * - Filter bogus Ctrl+<char> combinations.
+ * - Win32 support
+ *
+ * Bloat:
+ * - History search like Ctrl+r in readline?
+ *
+ * List of escape sequences used by this program, we do everything just
+ * with three sequences. In order to be so cheap we may have some
+ * flickering effect with some slow terminal, but the lesser sequences
+ * the more compatible.
+ *
+ * EL (Erase Line)
+ * Sequence: ESC [ n K
+ * Effect: if n is 0 or missing, clear from cursor to end of line
+ * Effect: if n is 1, clear from beginning of line to cursor
+ * Effect: if n is 2, clear entire line
+ *
+ * CUF (CUrsor Forward)
+ * Sequence: ESC [ n C
+ * Effect: moves cursor forward n chars
+ *
+ * CUB (CUrsor Backward)
+ * Sequence: ESC [ n D
+ * Effect: moves cursor backward n chars
+ *
+ * The following is used to get the terminal width if getting
+ * the width with the TIOCGWINSZ ioctl fails
+ *
+ * DSR (Device Status Report)
+ * Sequence: ESC [ 6 n
+ * Effect: reports the current cusor position as ESC [ n ; m R
+ * where n is the row and m is the column
+ *
+ * When multi line mode is enabled, we also use an additional escape
+ * sequence. However multi line editing is disabled by default.
+ *
+ * CUU (Cursor Up)
+ * Sequence: ESC [ n A
+ * Effect: moves cursor up of n chars.
+ *
+ * CUD (Cursor Down)
+ * Sequence: ESC [ n B
+ * Effect: moves cursor down of n chars.
+ *
+ * When linenoiseClearScreen() is called, two additional escape sequences
+ * are used in order to clear the screen and position the cursor at home
+ * position.
+ *
+ * CUP (Cursor position)
+ * Sequence: ESC [ H
+ * Effect: moves the cursor to upper left corner
+ *
+ * ED (Erase display)
+ * Sequence: ESC [ 2 J
+ * Effect: clear the whole screen
+ *
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "linenoise.h"
+
+#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
+#define LINENOISE_MAX_LINE 4096
+
+static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
+static linenoiseCompletionCallback *completionCallback = NULL;
+static linenoiseHintsCallback *hintsCallback = NULL;
+static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
+
+static struct termios orig_termios; /* In order to restore at exit.*/
+static int maskmode = 0; /* Show "***" instead of input. For passwords. */
+static int rawmode = 0; /* For atexit() function to check if restore is needed*/
+static int mlmode = 0; /* Multi line mode. Default is single line. */
+static int atexit_registered = 0; /* Register atexit just 1 time. */
+static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
+static int history_len = 0;
+static char **history = NULL;
+
+/* The linenoiseState structure represents the state during line editing.
+ * We pass this state to functions implementing specific editing
+ * functionalities. */
+struct linenoiseState {
+ int ifd; /* Terminal stdin file descriptor. */
+ int ofd; /* Terminal stdout file descriptor. */
+ char *buf; /* Edited line buffer. */
+ size_t buflen; /* Edited line buffer size. */
+ const char *prompt; /* Prompt to display. */
+ size_t plen; /* Prompt length. */
+ size_t pos; /* Current cursor position. */
+ size_t oldpos; /* Previous refresh cursor position. */
+ size_t len; /* Current edited line length. */
+ size_t cols; /* Number of columns in terminal. */
+ size_t maxrows; /* Maximum num of rows used so far (multiline mode) */
+ int history_index; /* The history index we are currently editing. */
+};
+
+enum KEY_ACTION{
+ KEY_NULL = 0, /* NULL */
+ CTRL_A = 1, /* Ctrl+a */
+ CTRL_B = 2, /* Ctrl-b */
+ CTRL_C = 3, /* Ctrl-c */
+ CTRL_D = 4, /* Ctrl-d */
+ CTRL_E = 5, /* Ctrl-e */
+ CTRL_F = 6, /* Ctrl-f */
+ CTRL_H = 8, /* Ctrl-h */
+ TAB = 9, /* Tab */
+ CTRL_K = 11, /* Ctrl+k */
+ CTRL_L = 12, /* Ctrl+l */
+ ENTER = 13, /* Enter */
+ CTRL_N = 14, /* Ctrl-n */
+ CTRL_P = 16, /* Ctrl-p */
+ CTRL_T = 20, /* Ctrl-t */
+ CTRL_U = 21, /* Ctrl+u */
+ CTRL_W = 23, /* Ctrl+w */
+ ESC = 27, /* Escape */
+ BACKSPACE = 127 /* Backspace */
+};
+
+static void linenoiseAtExit(void);
+int linenoiseHistoryAdd(const char *line);
+static void refreshLine(struct linenoiseState *l);
+
+/* Debugging macro. */
+#if 0
+FILE *lndebug_fp = NULL;
+#define lndebug(...) \
+ do { \
+ if (lndebug_fp == NULL) { \
+ lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
+ fprintf(lndebug_fp, \
+ "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
+ (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
+ (int)l->maxrows,old_rows); \
+ } \
+ fprintf(lndebug_fp, ", " __VA_ARGS__); \
+ fflush(lndebug_fp); \
+ } while (0)
+#else
+#define lndebug(fmt, ...)
+#endif
+
+int
+strcasecmp(const char *_l, const char *_r)
+{
+ const unsigned char *l=(void *)_l, *r=(void *)_r;
+ for (; *l && *r && (*l == *r || tolower(*l) == tolower(*r)); l++, r++);
+ return tolower(*l) - tolower(*r);
+}
+
+
+/* ======================= Low level terminal handling ====================== */
+
+/* Enable "mask mode". When it is enabled, instead of the input that
+ * the user is typing, the terminal will just display a corresponding
+ * number of asterisks, like "****". This is useful for passwords and other
+ * secrets that should not be displayed. */
+void linenoiseMaskModeEnable(void) {
+ maskmode = 1;
+}
+
+/* Disable mask mode. */
+void linenoiseMaskModeDisable(void) {
+ maskmode = 0;
+}
+
+/* Set if to use or not the multi line mode. */
+void linenoiseSetMultiLine(int ml) {
+ mlmode = ml;
+}
+
+/* Return true if the terminal name is in the list of terminals we know are
+ * not able to understand basic escape sequences. */
+static int isUnsupportedTerm(void) {
+ char *term = getenv("TERM");
+ int j;
+
+ if(term == NULL) return 0;
+ for(j = 0; unsupported_term[j]; j++)
+ if(!strcasecmp(term,unsupported_term[j])) return 1;
+ return 0;
+}
+
+/* Raw mode: 1960 magic shit. */
+static int enableRawMode(int fd) {
+ struct termios raw;
+
+ if (!isatty(STDIN_FILENO)) goto fatal;
+ if (!atexit_registered) {
+ atexit(linenoiseAtExit);
+ atexit_registered = 1;
+ }
+ if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
+
+ raw = orig_termios; /* modify the original mode */
+ /* input modes: no break, no CR to NL, no parity check, no strip char,
+ * no start/stop output control. */
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ /* output modes - disable post processing */
+ raw.c_oflag &= ~(OPOST);
+ /* control modes - set 8 bit chars */
+ raw.c_cflag |= (CS8);
+ /* local modes - choing off, canonical off, no extended functions,
+ * no signal chars (^Z,^C) */
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ /* control chars - set return condition: min number of bytes and timer.
+ * We want read to return every single byte, without timeout. */
+ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+
+ /* put terminal in raw mode after flushing */
+ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
+ rawmode = 1;
+ return 0;
+
+fatal:
+ errno = ENOTTY;
+ return -1;
+}
+
+static void disableRawMode(int fd) {
+ /* Don't even check the return value as it's too late. */
+ if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
+ rawmode = 0;
+}
+
+/* Use the ESC [6n escape sequence to query the horizontal cursor position
+ * and return it. On error -1 is returned, on success the position of the
+ * cursor. */
+static int getCursorPosition(int ifd, int ofd) {
+ char buf[32];
+ int cols, rows;
+ unsigned int i = 0;
+
+ /* Report cursor location */
+ if (write(ofd, "\x1b[6n", 4) != 4) return -1;
+
+ /* Read the response: ESC [ rows ; cols R */
+ while (i < sizeof(buf)-1) {
+ if (read(ifd,buf+i,1) != 1) break;
+ if (buf[i] == 'R') break;
+ i++;
+ }
+ buf[i] = '\0';
+
+ /* Parse it. */
+ if (buf[0] != ESC || buf[1] != '[') return -1;
+ if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
+ return cols;
+}
+
+/* Try to get the number of columns in the current terminal, or assume 80
+ * if it fails. */
+static int getColumns(int ifd, int ofd) {
+ struct winsize ws;
+
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+ /* ioctl() failed. Try to query the terminal itself. */
+ int start, cols;
+
+ /* Get the initial position so we can restore it later. */
+ start = getCursorPosition(ifd,ofd);
+ if (start == -1) goto failed;
+
+ /* Go to right margin and get position. */
+ if (write(ofd,"\x1b[999C",6) != 6) goto failed;
+ cols = getCursorPosition(ifd,ofd);
+ if (cols == -1) goto failed;
+
+ /* Restore position. */
+ if (cols > start) {
+ char seq[32];
+ snprintf(seq,32,"\x1b[%dD",cols-start);
+ if (write(ofd,seq,strlen(seq)) == -1) {
+ /* Can't recover... */
+ }
+ }
+ return cols;
+ } else {
+ return ws.ws_col;
+ }
+
+failed:
+ return 80;
+}
+
+/* Clear the screen. Used to handle ctrl+l */
+void linenoiseClearScreen(void) {
+ if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
+ /* nothing to do, just to avoid warning. */
+ }
+}
+
+/* Beep, used for completion when there is nothing to complete or when all
+ * the choices were already shown. */
+static void linenoiseBeep(void) {
+ fprintf(stderr, "\x7");
+ fflush(stderr);
+}
+
+/* ============================== Completion ================================ */
+
+/* Free a list of completion option populated by linenoiseAddCompletion(). */
+static void freeCompletions(linenoiseCompletions *lc) {
+ size_t i;
+ for (i = 0; i < lc->len; i++)
+ free(lc->cvec[i]);
+ if (lc->cvec != NULL)
+ free(lc->cvec);
+}
+
+/* This is an helper function for linenoiseEdit() and is called when the
+ * user types the <tab> key in order to complete the string currently in the
+ * input.
+ *
+ * The state of the editing is encapsulated into the pointed linenoiseState
+ * structure as described in the structure definition. */
+static int completeLine(struct linenoiseState *ls) {
+ linenoiseCompletions lc = { 0, NULL };
+ int nread, nwritten;
+ char c = 0;
+
+ completionCallback(ls->buf,&lc);
+ if (lc.len == 0) {
+ linenoiseBeep();
+ } else {
+ size_t stop = 0, i = 0;
+
+ while(!stop) {
+ /* Show completion or original buffer */
+ if (i < lc.len) {
+ struct linenoiseState saved = *ls;
+
+ ls->len = ls->pos = strlen(lc.cvec[i]);
+ ls->buf = lc.cvec[i];
+ refreshLine(ls);
+ ls->len = saved.len;
+ ls->pos = saved.pos;
+ ls->buf = saved.buf;
+ } else {
+ refreshLine(ls);
+ }
+
+ nread = read(ls->ifd,&c,1);
+ if (nread <= 0) {
+ freeCompletions(&lc);
+ return -1;
+ }
+
+ switch(c) {
+ case 9: /* tab */
+ i = (i+1) % (lc.len+1);
+ if (i == lc.len) linenoiseBeep();
+ break;
+ case 27: /* escape */
+ /* Re-show original buffer */
+ if (i < lc.len) refreshLine(ls);
+ stop = 1;
+ break;
+ default:
+ /* Update buffer and return */
+ if (i < lc.len) {
+ nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]);
+ ls->len = ls->pos = nwritten;
+ }
+ stop = 1;
+ break;
+ }
+ }
+ }
+
+ freeCompletions(&lc);
+ return c; /* Return last read character */
+}
+
+/* Register a callback function to be called for tab-completion. */
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
+ completionCallback = fn;
+}
+
+/* Register a hits function to be called to show hits to the user at the
+ * right of the prompt. */
+void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
+ hintsCallback = fn;
+}
+
+/* Register a function to free the hints returned by the hints callback
+ * registered with linenoiseSetHintsCallback(). */
+void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
+ freeHintsCallback = fn;
+}
+
+/* This function is used by the callback function registered by the user
+ * in order to add completion options given the input string when the
+ * user typed <tab>. See the example.c source code for a very easy to
+ * understand example. */
+void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
+ size_t len = strlen(str);
+ char *copy, **cvec;
+
+ copy = malloc(len+1);
+ if (copy == NULL) return;
+ memcpy(copy,str,len+1);
+ cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
+ if (cvec == NULL) {
+ free(copy);
+ return;
+ }
+ lc->cvec = cvec;
+ lc->cvec[lc->len++] = copy;
+}
+
+/* =========================== Line editing ================================= */
+
+/* We define a very simple "append buffer" structure, that is an heap
+ * allocated string where we can append to. This is useful in order to
+ * write all the escape sequences in a buffer and flush them to the standard
+ * output in a single call, to avoid flickering effects. */
+struct abuf {
+ char *b;
+ int len;
+};
+
+static void abInit(struct abuf *ab) {
+ ab->b = NULL;
+ ab->len = 0;
+}
+
+static void abAppend(struct abuf *ab, const char *s, int len) {
+ char *new = realloc(ab->b,ab->len+len);
+
+ if (new == NULL) return;
+ memcpy(new+ab->len,s,len);
+ ab->b = new;
+ ab->len += len;
+}
+
+static void abFree(struct abuf *ab) {
+ free(ab->b);
+}
+
+/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
+ * to the right of the prompt. */
+void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
+ char seq[64];
+ if (hintsCallback && plen+l->len < l->cols) {
+ int color = -1, bold = 0;
+ char *hint = hintsCallback(l->buf,&color,&bold);
+ if (hint) {
+ int hintlen = strlen(hint);
+ int hintmaxlen = l->cols-(plen+l->len);
+ if (hintlen > hintmaxlen) hintlen = hintmaxlen;
+ if (bold == 1 && color == -1) color = 37;
+ if (color != -1 || bold != 0)
+ snprintf(seq,64,"\033[%d;%d;49m",bold,color);
+ else
+ seq[0] = '\0';
+ abAppend(ab,seq,strlen(seq));
+ abAppend(ab,hint,hintlen);
+ if (color != -1 || bold != 0)
+ abAppend(ab,"\033[0m",4);
+ /* Call the function to free the hint returned. */
+ if (freeHintsCallback) freeHintsCallback(hint);
+ }
+ }
+}
+
+/* Single line low level line refresh.
+ *
+ * Rewrite the currently edited line accordingly to the buffer content,
+ * cursor position, and number of columns of the terminal. */
+static void refreshSingleLine(struct linenoiseState *l) {
+ char seq[64];
+ size_t plen = strlen(l->prompt);
+ int fd = l->ofd;
+ char *buf = l->buf;
+ size_t len = l->len;
+ size_t pos = l->pos;
+ struct abuf ab;
+
+ while((plen+pos) >= l->cols) {
+ buf++;
+ len--;
+ pos--;
+ }
+ while (plen+len > l->cols) {
+ len--;
+ }
+
+ abInit(&ab);
+ /* Cursor to left edge */
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+ /* Write the prompt and the current buffer content */
+ abAppend(&ab,l->prompt,strlen(l->prompt));
+ if (maskmode == 1) {
+ while (len--) abAppend(&ab,"*",1);
+ } else {
+ abAppend(&ab,buf,len);
+ }
+ /* Show hits if any. */
+ refreshShowHints(&ab,l,plen);
+ /* Erase to right */
+ snprintf(seq,64,"\x1b[0K");
+ abAppend(&ab,seq,strlen(seq));
+ /* Move cursor to original position. */
+ snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
+ abAppend(&ab,seq,strlen(seq));
+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+ abFree(&ab);
+}
+
+/* Multi line low level line refresh.
+ *
+ * Rewrite the currently edited line accordingly to the buffer content,
+ * cursor position, and number of columns of the terminal. */
+static void refreshMultiLine(struct linenoiseState *l) {
+ char seq[64];
+ int plen = strlen(l->prompt);
+ int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */
+ int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */
+ int rpos2; /* rpos after refresh. */
+ int col; /* colum position, zero-based. */
+ int old_rows = l->maxrows;
+ int fd = l->ofd, j;
+ struct abuf ab;
+
+ /* Update maxrows if needed. */
+ if (rows > (int)l->maxrows) l->maxrows = rows;
+
+ /* First step: clear all the lines used before. To do so start by
+ * going to the last row. */
+ abInit(&ab);
+ if (old_rows-rpos > 0) {
+ lndebug("go down %d", old_rows-rpos);
+ snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Now for every row clear it, go up. */
+ for (j = 0; j < old_rows-1; j++) {
+ lndebug("clear+up");
+ snprintf(seq,64,"\r\x1b[0K\x1b[1A");
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Clean the top line. */
+ lndebug("clear");
+ snprintf(seq,64,"\r\x1b[0K");
+ abAppend(&ab,seq,strlen(seq));
+
+ /* Write the prompt and the current buffer content */
+ abAppend(&ab,l->prompt,strlen(l->prompt));
+ if (maskmode == 1) {
+ unsigned int i;
+ for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
+ } else {
+ abAppend(&ab,l->buf,l->len);
+ }
+
+ /* Show hits if any. */
+ refreshShowHints(&ab,l,plen);
+
+ /* If we are at the very end of the screen with our prompt, we need to
+ * emit a newline and move the prompt to the first column. */
+ if (l->pos &&
+ l->pos == l->len &&
+ (l->pos+plen) % l->cols == 0)
+ {
+ lndebug("<newline>");
+ abAppend(&ab,"\n",1);
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+ rows++;
+ if (rows > (int)l->maxrows) l->maxrows = rows;
+ }
+
+ /* Move cursor to right position. */
+ rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
+ lndebug("rpos2 %d", rpos2);
+
+ /* Go up till we reach the expected positon. */
+ if (rows-rpos2 > 0) {
+ lndebug("go-up %d", rows-rpos2);
+ snprintf(seq,64,"\x1b[%dA", rows-rpos2);
+ abAppend(&ab,seq,strlen(seq));
+ }
+
+ /* Set column. */
+ col = (plen+(int)l->pos) % (int)l->cols;
+ lndebug("set col %d", 1+col);
+ if (col)
+ snprintf(seq,64,"\r\x1b[%dC", col);
+ else
+ snprintf(seq,64,"\r");
+ abAppend(&ab,seq,strlen(seq));
+
+ lndebug("\n");
+ l->oldpos = l->pos;
+
+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+ abFree(&ab);
+}
+
+/* Calls the two low level functions refreshSingleLine() or
+ * refreshMultiLine() according to the selected mode. */
+static void refreshLine(struct linenoiseState *l) {
+ if (mlmode)
+ refreshMultiLine(l);
+ else
+ refreshSingleLine(l);
+}
+
+/* Insert the character 'c' at cursor current position.
+ *
+ * On error writing to the terminal -1 is returned, otherwise 0. */
+int linenoiseEditInsert(struct linenoiseState *l, char c) {
+ if (l->len < l->buflen) {
+ if (l->len == l->pos) {
+ l->buf[l->pos] = c;
+ l->pos++;
+ l->len++;
+ l->buf[l->len] = '\0';
+ if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
+ /* Avoid a full update of the line in the
+ * trivial case. */
+ char d = (maskmode==1) ? '*' : c;
+ if (write(l->ofd,&d,1) == -1) return -1;
+ } else {
+ refreshLine(l);
+ }
+ } else {
+ memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);
+ l->buf[l->pos] = c;
+ l->len++;
+ l->pos++;
+ l->buf[l->len] = '\0';
+ refreshLine(l);
+ }
+ }
+ return 0;
+}
+
+/* Move cursor on the left. */
+void linenoiseEditMoveLeft(struct linenoiseState *l) {
+ if (l->pos > 0) {
+ l->pos--;
+ refreshLine(l);
+ }
+}
+
+/* Move cursor on the right. */
+void linenoiseEditMoveRight(struct linenoiseState *l) {
+ if (l->pos != l->len) {
+ l->pos++;
+ refreshLine(l);
+ }
+}
+
+/* Move cursor to the start of the line. */
+void linenoiseEditMoveHome(struct linenoiseState *l) {
+ if (l->pos != 0) {
+ l->pos = 0;
+ refreshLine(l);
+ }
+}
+
+/* Move cursor to the end of the line. */
+void linenoiseEditMoveEnd(struct linenoiseState *l) {
+ if (l->pos != l->len) {
+ l->pos = l->len;
+ refreshLine(l);
+ }
+}
+
+/* Substitute the currently edited line with the next or previous history
+ * entry as specified by 'dir'. */
+#define LINENOISE_HISTORY_NEXT 0
+#define LINENOISE_HISTORY_PREV 1
+void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {
+ if (history_len > 1) {
+ /* Update the current history entry before to
+ * overwrite it with the next one. */
+ free(history[history_len - 1 - l->history_index]);
+ history[history_len - 1 - l->history_index] = strdup(l->buf);
+ /* Show the new entry */
+ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
+ if (l->history_index < 0) {
+ l->history_index = 0;
+ return;
+ } else if (l->history_index >= history_len) {
+ l->history_index = history_len-1;
+ return;
+ }
+ strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);
+ l->buf[l->buflen-1] = '\0';
+ l->len = l->pos = strlen(l->buf);
+ refreshLine(l);
+ }
+}
+
+/* Delete the character at the right of the cursor without altering the cursor
+ * position. Basically this is what happens with the "Delete" keyboard key. */
+void linenoiseEditDelete(struct linenoiseState *l) {
+ if (l->len > 0 && l->pos < l->len) {
+ memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1);
+ l->len--;
+ l->buf[l->len] = '\0';
+ refreshLine(l);
+ }
+}
+
+/* Backspace implementation. */
+void linenoiseEditBackspace(struct linenoiseState *l) {
+ if (l->pos > 0 && l->len > 0) {
+ memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos);
+ l->pos--;
+ l->len--;
+ l->buf[l->len] = '\0';
+ refreshLine(l);
+ }
+}
+
+/* Delete the previosu word, maintaining the cursor at the start of the
+ * current word. */
+void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
+ size_t old_pos = l->pos;
+ size_t diff;
+
+ while (l->pos > 0 && l->buf[l->pos-1] == ' ')
+ l->pos--;
+ while (l->pos > 0 && l->buf[l->pos-1] != ' ')
+ l->pos--;
+ diff = old_pos - l->pos;
+ memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);
+ l->len -= diff;
+ refreshLine(l);
+}
+
+/* This function is the core of the line editing capability of linenoise.
+ * It expects 'fd' to be already in "raw mode" so that every key pressed
+ * will be returned ASAP to read().
+ *
+ * The resulting string is put into 'buf' when the user type enter, or
+ * when ctrl+d is typed.
+ *
+ * The function returns the length of the current buffer. */
+static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
+{
+ struct linenoiseState l;
+
+ /* Populate the linenoise state that we pass to functions implementing
+ * specific editing functionalities. */
+ l.ifd = stdin_fd;
+ l.ofd = stdout_fd;
+ l.buf = buf;
+ l.buflen = buflen;
+ l.prompt = prompt;
+ l.plen = strlen(prompt);
+ l.oldpos = l.pos = 0;
+ l.len = 0;
+ l.cols = getColumns(stdin_fd, stdout_fd);
+ l.maxrows = 0;
+ l.history_index = 0;
+
+ /* Buffer starts empty. */
+ l.buf[0] = '\0';
+ l.buflen--; /* Make sure there is always space for the nulterm */
+
+ /* The latest history entry is always our current buffer, that
+ * initially is just an empty string. */
+ linenoiseHistoryAdd("");
+
+ if (write(l.ofd,prompt,l.plen) == -1) return -1;
+ while(1) {
+ char c;
+ int nread;
+ char seq[3];
+
+ nread = read(l.ifd,&c,1);
+ if (nread <= 0) return l.len;
+
+ /* Only autocomplete when the callback is set. It returns < 0 when
+ * there was an error reading from fd. Otherwise it will return the
+ * character that should be handled next. */
+ if (c == 9 && completionCallback != NULL) {
+ c = completeLine(&l);
+ /* Return on errors */
+ if (c < 0) return l.len;
+ /* Read next character when 0 */
+ if (c == 0) continue;
+ }
+
+ switch(c) {
+ case ENTER: /* enter */
+ history_len--;
+ free(history[history_len]);
+ if (mlmode) linenoiseEditMoveEnd(&l);
+ if (hintsCallback) {
+ /* Force a refresh without hints to leave the previous
+ * line as the user typed it after a newline. */
+ linenoiseHintsCallback *hc = hintsCallback;
+ hintsCallback = NULL;
+ refreshLine(&l);
+ hintsCallback = hc;
+ }
+ return (int)l.len;
+ case CTRL_C: /* ctrl-c */
+ errno = EAGAIN;
+ return -1;
+ case BACKSPACE: /* backspace */
+ case 8: /* ctrl-h */
+ linenoiseEditBackspace(&l);
+ break;
+ case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
+ line is empty, act as end-of-file. */
+ if (l.len > 0) {
+ linenoiseEditDelete(&l);
+ } else {
+ history_len--;
+ free(history[history_len]);
+ return -1;
+ }
+ break;
+ case CTRL_T: /* ctrl-t, swaps current character with previous. */
+ if (l.pos > 0 && l.pos < l.len) {
+ int aux = buf[l.pos-1];
+ buf[l.pos-1] = buf[l.pos];
+ buf[l.pos] = aux;
+ if (l.pos != l.len-1) l.pos++;
+ refreshLine(&l);
+ }
+ break;
+ case CTRL_B: /* ctrl-b */
+ linenoiseEditMoveLeft(&l);
+ break;
+ case CTRL_F: /* ctrl-f */
+ linenoiseEditMoveRight(&l);
+ break;
+ case CTRL_P: /* ctrl-p */
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
+ break;
+ case CTRL_N: /* ctrl-n */
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
+ break;
+ case ESC: /* escape sequence */
+ /* Read the next two bytes representing the escape sequence.
+ * Use two calls to handle slow terminals returning the two
+ * chars at different times. */
+ if (read(l.ifd,seq,1) == -1) break;
+ if (read(l.ifd,seq+1,1) == -1) break;
+
+ /* ESC [ sequences. */
+ if (seq[0] == '[') {
+ if (seq[1] >= '0' && seq[1] <= '9') {
+ /* Extended escape, read additional byte. */
+ if (read(l.ifd,seq+2,1) == -1) break;
+ if (seq[2] == '~') {
+ switch(seq[1]) {
+ case '3': /* Delete key. */
+ linenoiseEditDelete(&l);
+ break;
+ }
+ }
+ } else {
+ switch(seq[1]) {
+ case 'A': /* Up */
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
+ break;
+ case 'B': /* Down */
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
+ break;
+ case 'C': /* Right */
+ linenoiseEditMoveRight(&l);
+ break;
+ case 'D': /* Left */
+ linenoiseEditMoveLeft(&l);
+ break;
+ case 'H': /* Home */
+ linenoiseEditMoveHome(&l);
+ break;
+ case 'F': /* End*/
+ linenoiseEditMoveEnd(&l);
+ break;
+ }
+ }
+ }
+
+ /* ESC O sequences. */
+ else if (seq[0] == 'O') {
+ switch(seq[1]) {
+ case 'H': /* Home */
+ linenoiseEditMoveHome(&l);
+ break;
+ case 'F': /* End*/
+ linenoiseEditMoveEnd(&l);
+ break;
+ }
+ }
+ break;
+ default:
+ if (linenoiseEditInsert(&l,c)) return -1;
+ break;
+ case CTRL_U: /* Ctrl+u, delete the whole line. */
+ buf[0] = '\0';
+ l.pos = l.len = 0;
+ refreshLine(&l);
+ break;
+ case CTRL_K: /* Ctrl+k, delete from current to end of line. */
+ buf[l.pos] = '\0';
+ l.len = l.pos;
+ refreshLine(&l);
+ break;
+ case CTRL_A: /* Ctrl+a, go to the start of the line */
+ linenoiseEditMoveHome(&l);
+ break;
+ case CTRL_E: /* ctrl+e, go to the end of the line */
+ linenoiseEditMoveEnd(&l);
+ break;
+ case CTRL_L: /* ctrl+l, clear screen */
+ linenoiseClearScreen();
+ refreshLine(&l);
+ break;
+ case CTRL_W: /* ctrl+w, delete previous word */
+ linenoiseEditDeletePrevWord(&l);
+ break;
+ }
+ }
+ return l.len;
+}
+
+/* This special mode is used by linenoise in order to print scan codes
+ * on screen for debugging / development purposes. It is implemented
+ * by the linenoise_example program using the --keycodes option. */
+void linenoisePrintKeyCodes(void) {
+ char quit[4];
+
+ printf("Linenoise key codes debugging mode.\n"
+ "Press keys to see scan codes. Type 'quit' at any time to exit.\n");
+ if (enableRawMode(STDIN_FILENO) == -1) return;
+ memset(quit,' ',4);
+ while(1) {
+ char c;
+ int nread;
+
+ nread = read(STDIN_FILENO,&c,1);
+ if (nread <= 0) continue;
+ memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
+ quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
+ if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
+
+ printf("'%c' %02x (%d) (type quit to exit)\n",
+ isprint(c) ? c : '?', (int)c, (int)c);
+ printf("\r"); /* Go left edge manually, we are in raw mode. */
+ fflush(stdout);
+ }
+ disableRawMode(STDIN_FILENO);
+}
+
+/* This function calls the line editing function linenoiseEdit() using
+ * the STDIN file descriptor set in raw mode. */
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+ int count;
+
+ if (buflen == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (enableRawMode(STDIN_FILENO) == -1) return -1;
+ count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
+ disableRawMode(STDIN_FILENO);
+ printf("\n");
+ return count;
+}
+
+/* This function is called when linenoise() is called with the standard
+ * input file descriptor not attached to a TTY. So for example when the
+ * program using linenoise is called in pipe or with a file redirected
+ * to its standard input. In this case, we want to be able to return the
+ * line regardless of its length (by default we are limited to 4k). */
+static char *linenoiseNoTTY(void) {
+ char *line = NULL;
+ size_t len = 0, maxlen = 0;
+
+ while(1) {
+ if (len == maxlen) {
+ if (maxlen == 0) maxlen = 16;
+ maxlen *= 2;
+ char *oldval = line;
+ line = realloc(line,maxlen);
+ if (line == NULL) {
+ if (oldval) free(oldval);
+ return NULL;
+ }
+ }
+ int c = fgetc(stdin);
+ if (c == EOF || c == '\n') {
+ if (c == EOF && len == 0) {
+ free(line);
+ return NULL;
+ } else {
+ line[len] = '\0';
+ return line;
+ }
+ } else {
+ line[len] = c;
+ len++;
+ }
+ }
+}
+
+/* The high level function that is the main API of the linenoise library.
+ * This function checks if the terminal has basic capabilities, just checking
+ * for a blacklist of stupid terminals, and later either calls the line
+ * editing function or uses dummy fgets() so that you will be able to type
+ * something even in the most desperate of the conditions. */
+char *linenoise(const char *prompt) {
+ char buf[LINENOISE_MAX_LINE];
+ int count;
+
+ if (!isatty(STDIN_FILENO)) {
+ /* Not a tty: read from file / pipe. In this mode we don't want any
+ * limit to the line size, so we call a function to handle that. */
+ return linenoiseNoTTY();
+ } else if (isUnsupportedTerm()) {
+ size_t len;
+
+ printf("%s",prompt);
+ fflush(stdout);
+ if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
+ len = strlen(buf);
+ while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
+ len--;
+ buf[len] = '\0';
+ }
+ return strdup(buf);
+ } else {
+ count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
+ if (count == -1) return NULL;
+ return strdup(buf);
+ }
+}
+
+/* This is just a wrapper the user may want to call in order to make sure
+ * the linenoise returned buffer is freed with the same allocator it was
+ * created with. Useful when the main program is using an alternative
+ * allocator. */
+void linenoiseFree(void *ptr) {
+ free(ptr);
+}
+
+/* ================================ History ================================= */
+
+/* Free the history, but does not reset it. Only used when we have to
+ * exit() to avoid memory leaks are reported by valgrind & co. */
+static void freeHistory(void) {
+ if (history) {
+ int j;
+
+ for (j = 0; j < history_len; j++)
+ free(history[j]);
+ free(history);
+ }
+}
+
+/* At exit we'll try to fix the terminal to the initial conditions. */
+static void linenoiseAtExit(void) {
+ disableRawMode(STDIN_FILENO);
+ freeHistory();
+}
+
+/* This is the API call to add a new entry in the linenoise history.
+ * It uses a fixed array of char pointers that are shifted (memmoved)
+ * when the history max length is reached in order to remove the older
+ * entry and make room for the new one, so it is not exactly suitable for huge
+ * histories, but will work well for a few hundred of entries.
+ *
+ * Using a circular buffer is smarter, but a bit more complex to handle. */
+int linenoiseHistoryAdd(const char *line) {
+ char *linecopy;
+
+ if (history_max_len == 0) return 0;
+
+ /* Initialization on first call. */
+ if (history == NULL) {
+ history = malloc(sizeof(char*)*history_max_len);
+ if (history == NULL) return 0;
+ memset(history,0,(sizeof(char*)*history_max_len));
+ }
+
+ /* Don't add duplicated lines. */
+ if (history_len && !strcmp(history[history_len-1], line)) return 0;
+
+ /* Add an heap allocated copy of the line in the history.
+ * If we reached the max length, remove the older line. */
+ linecopy = strdup(line);
+ if (!linecopy) return 0;
+ if (history_len == history_max_len) {
+ free(history[0]);
+ memmove(history,history+1,sizeof(char*)*(history_max_len-1));
+ history_len--;
+ }
+ history[history_len] = linecopy;
+ history_len++;
+ return 1;
+}
+
+/* Set the maximum length for the history. This function can be called even
+ * if there is already some history, the function will make sure to retain
+ * just the latest 'len' elements if the new history length value is smaller
+ * than the amount of items already inside the history. */
+int linenoiseHistorySetMaxLen(int len) {
+ char **new;
+
+ if (len < 1) return 0;
+ if (history) {
+ int tocopy = history_len;
+
+ new = malloc(sizeof(char*)*len);
+ if (new == NULL) return 0;
+
+ /* If we can't copy everything, free the elements we'll not use. */
+ if (len < tocopy) {
+ int j;
+
+ for (j = 0; j < tocopy-len; j++) free(history[j]);
+ tocopy = len;
+ }
+ memset(new,0,sizeof(char*)*len);
+ memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
+ free(history);
+ history = new;
+ }
+ history_max_len = len;
+ if (history_len > history_max_len)
+ history_len = history_max_len;
+ return 1;
+}
+
+/* Save the history in the specified file. On success 0 is returned
+ * otherwise -1 is returned. */
+int linenoiseHistorySave(const char *filename) {
+ mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ FILE *fp;
+ int j;
+
+ fp = fopen(filename,"w");
+ umask(old_umask);
+ if(fp == NULL) return -1;
+ chmod(filename,S_IRUSR|S_IWUSR);
+ for (j = 0; j < history_len; j++)
+ fprintf(fp,"%s\n",history[j]);
+ fclose(fp);
+ return 0;
+}
+
+/* Load the history from the specified file. If the file does not exist
+ * zero is returned and no operation is performed.
+ *
+ * If the file exists and the operation succeeded 0 is returned, otherwise
+ * on error -1 is returned. */
+int linenoiseHistoryLoad(const char *filename) {
+ FILE *fp = fopen(filename,"r");
+ char buf[LINENOISE_MAX_LINE];
+
+ if (fp == NULL) return -1;
+
+ while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
+ char *p;
+
+ p = strchr(buf,'\r');
+ if (!p) p = strchr(buf,'\n');
+ if (p) *p = '\0';
+ linenoiseHistoryAdd(buf);
+ }
+ fclose(fp);
+ return 0;
+}
diff --git a/sys/cmd/rc/linenoise.h b/sys/cmd/rc/linenoise.h
new file mode 100644
index 0000000..9349f1b
--- /dev/null
+++ b/sys/cmd/rc/linenoise.h
@@ -0,0 +1,77 @@
+/* linenoise.h -- VERSION 1.0
+ *
+ * Guerrilla line editing library against the idea that a line editing lib
+ * needs to be 20,000 lines of C code.
+ *
+ * See linenoise.c for more information.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LINENOISE_H
+#define __LINENOISE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct linenoiseCompletions {
+ size_t len;
+ char **cvec;
+} linenoiseCompletions;
+
+typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
+typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
+typedef void(linenoiseFreeHintsCallback)(void *);
+
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
+void linenoiseSetHintsCallback(linenoiseHintsCallback *);
+void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
+void linenoiseAddCompletion(linenoiseCompletions *, const char *);
+
+char *linenoise(const char *prompt);
+void linenoiseFree(void *ptr);
+int linenoiseHistoryAdd(const char *line);
+int linenoiseHistorySetMaxLen(int len);
+int linenoiseHistorySave(const char *filename);
+int linenoiseHistoryLoad(const char *filename);
+
+void linenoiseClearScreen(void);
+void linenoiseSetMultiLine(int ml);
+void linenoisePrintKeyCodes(void);
+void linenoiseMaskModeEnable(void);
+void linenoiseMaskModeDisable(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LINENOISE_H */
diff --git a/sys/cmd/rc/pcmd.c b/sys/cmd/rc/pcmd.c
new file mode 100644
index 0000000..8caf60a
--- /dev/null
+++ b/sys/cmd/rc/pcmd.c
@@ -0,0 +1,147 @@
+#include "rc.h"
+#include "io.h"
+#include "fns.h"
+char nl='\n'; /* change to semicolon for bourne-proofing */
+#define c0 t->child[0]
+#define c1 t->child[1]
+#define c2 t->child[2]
+
+void
+pdeglob(io *f, char *s)
+{
+ while(*s){
+ if(*s==GLOB)
+ s++;
+ pchr(f, *s++);
+ }
+}
+
+void
+pcmd(io *f, tree *t)
+{
+ if(t==0)
+ return;
+ switch(t->type){
+ default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2);
+ break;
+ case '$': pfmt(f, "$%t", c0);
+ break;
+ case '"': pfmt(f, "$\"%t", c0);
+ break;
+ case '&': pfmt(f, "%t&", c0);
+ break;
+ case '^': pfmt(f, "%t^%t", c0, c1);
+ break;
+ case '`': pfmt(f, "`%t", c0);
+ break;
+ case ANDAND: pfmt(f, "%t && %t", c0, c1);
+ break;
+ case BANG: pfmt(f, "! %t", c0);
+ break;
+ case BRACE: pfmt(f, "{%t}", c0);
+ break;
+ case COUNT: pfmt(f, "$#%t", c0);
+ break;
+ case FN: pfmt(f, "fn %t %t", c0, c1);
+ break;
+ case IF: pfmt(f, "if%t%t", c0, c1);
+ break;
+ case NOT: pfmt(f, "if not %t", c0);
+ break;
+ case OROR: pfmt(f, "%t || %t", c0, c1);
+ break;
+ case PCMD:
+ case PAREN: pfmt(f, "(%t)", c0);
+ break;
+ case SUB: pfmt(f, "$%t(%t)", c0, c1);
+ break;
+ case SIMPLE: pfmt(f, "%t", c0);
+ break;
+ case SUBSHELL: pfmt(f, "@ %t", c0);
+ break;
+ case SWITCH: pfmt(f, "switch %t %t", c0, c1);
+ break;
+ case TWIDDLE: pfmt(f, "~ %t %t", c0, c1);
+ break;
+ case WHILE: pfmt(f, "while %t%t", c0, c1);
+ break;
+ case ARGLIST:
+ if(c0==0)
+ pfmt(f, "%t", c1);
+ else if(c1==0)
+ pfmt(f, "%t", c0);
+ else
+ pfmt(f, "%t %t", c0, c1);
+ break;
+ case ';':
+ if(c0){
+ if(c1)
+ pfmt(f, "%t%c%t", c0, nl, c1);
+ else pfmt(f, "%t", c0);
+ }
+ else pfmt(f, "%t", c1);
+ break;
+ case WORDS:
+ if(c0)
+ pfmt(f, "%t ", c0);
+ pfmt(f, "%t", c1);
+ break;
+ case FOR:
+ pfmt(f, "for(%t", c0);
+ if(c1)
+ pfmt(f, " in %t", c1);
+ pfmt(f, ")%t", c2);
+ break;
+ case WORD:
+ if(t->quoted)
+ pfmt(f, "%Q", t->str);
+ else pdeglob(f, t->str);
+ break;
+ case DUP:
+ if(t->rtype==DUPFD)
+ pfmt(f, ">[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */
+ else
+ pfmt(f, ">[%d=]", t->fd0);
+ pfmt(f, "%t", c1);
+ break;
+ case PIPEFD:
+ case REDIR:
+ switch(t->rtype){
+ case HERE:
+ pchr(f, '<');
+ case READ:
+ 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(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;
+ }
+}
diff --git a/sys/cmd/rc/pfnc.c b/sys/cmd/rc/pfnc.c
new file mode 100644
index 0000000..3f2b4c9
--- /dev/null
+++ b/sys/cmd/rc/pfnc.c
@@ -0,0 +1,71 @@
+#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",
+ Xrdwr, "Xrdwr",
+ Xread, "Xread",
+ Xreturn, "Xreturn",
+ Xtrue, "Xtrue",
+ Xif, "Xif",
+ Xwastrue, "Xwastrue",
+ Xword, "Xword",
+ Xwrite, "Xwrite",
+ Xmatch, "Xmatch",
+ Xcase, "Xcase",
+ Xconc, "Xconc",
+ Xassign, "Xassign",
+ Xdol, "Xdol",
+ Xcount, "Xcount",
+ Xlocal, "Xlocal",
+ Xunlocal, "Xunlocal",
+ Xfn, "Xfn",
+ Xdelfn, "Xdelfn",
+ Xpipe, "Xpipe",
+ Xpipewait, "Xpipewait",
+ Xrdcmds, "Xrdcmds",
+ (void (*)(void))Xerror, "Xerror",
+ Xbackq, "Xbackq",
+ Xpipefd, "Xpipefd",
+ Xsubshell, "Xsubshell",
+ Xdelhere, "Xdelhere",
+ Xfor, "Xfor",
+ Xglob, "Xglob",
+ Xrdfn, "Xrdfn",
+ Xsimple, "Xsimple",
+ Xrdfn, "Xrdfn",
+ Xqdol, "Xqdol",
+0};
+
+void
+pfnc(io *fd, thread *t)
+{
+ int i;
+ void (*fn)(void) = t->code[t->pc].f;
+ list *a;
+ pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc);
+ for(i = 0;fname[i].f;i++) if(fname[i].f==fn){
+ pstr(fd, fname[i].name);
+ break;
+ }
+ if(!fname[i].f)
+ pfmt(fd, "%p", fn);
+ for(a = t->argv;a;a = a->next) pfmt(fd, " (%v)", a->words);
+ pchr(fd, '\n');
+ flush(fd);
+}
diff --git a/sys/cmd/rc/prompt.c b/sys/cmd/rc/prompt.c
new file mode 100644
index 0000000..b51e509
--- /dev/null
+++ b/sys/cmd/rc/prompt.c
@@ -0,0 +1,101 @@
+#include "rc.h"
+#include "fns.h"
+#include "io.h"
+#include "exec.h"
+#include "getflags.h"
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "linenoise.h"
+
+#define DEBUG(f, a...) \
+ if(0){}else{pfmt(err, "\n%s: "f, __FUNCTION__, ## a);flush(err);}
+
+#define HISTORY_PATH "~/.local/rc/history"
+
+#define COMPLETE_FN "complete"
+#define COMPLETE_RESULTS "rc_complete_results"
+
+static
+char*
+completion_matches(const char *s, int i)
+{
+ word *w;
+
+ for(w=vlook(COMPLETE_RESULTS)->val; w; w=w->next, i--)
+ if(!i)
+ return strdup(w->word);
+ return nil;
+}
+
+static
+void
+completion(const char *text, linenoiseCompletions *lc)
+{
+ linenoiseAddCompletion(lc, "test");
+}
+
+static
+void
+savehist(void)
+{
+ linenoiseHistorySave(HISTORY_PATH);
+}
+
+static
+void
+readline(void)
+{
+ static int first = 1;
+ io *f = runq->cmdfd;
+ char *s;
+ long n;
+
+ if(first){
+ linenoiseSetMultiLine(1);
+ linenoiseSetCompletionCallback(completion);
+
+ linenoiseHistorySetMaxLen(10000);
+
+ /* history */
+ if(linenoiseHistoryLoad(HISTORY_PATH)!=0 && errno!=ENOENT)
+ pfmt(err, "rc: loadhistory: %s\n", strerror(errno));
+
+ atexit(savehist);
+ first = 0;
+ }
+
+ s = linenoise(promptstr);
+ if(!s)
+ return;
+
+ n = strlen(s);
+ assert(n < NBUF-1);
+ strcpy(f->buf, s);
+ f->buf[n++] = '\n';
+ f->bufp = f->buf;
+ f->ebuf = f->buf+n;
+
+ linenoiseHistoryAdd(s);
+ free(s);
+}
+
+void
+pprompt(void)
+{
+ var *prompt;
+
+ if(runq->iflag){
+ flush(err);
+ readline();
+ prompt = vlook("prompt");
+ if(prompt->val && prompt->val->next)
+ promptstr = prompt->val->next->word;
+ else
+ promptstr="\t";
+ }
+ runq->lineno++;
+ doprompt = 0;
+}
diff --git a/sys/cmd/rc/rcmain.unix b/sys/cmd/rc/rcmain.unix
new file mode 100644
index 0000000..42b3be4
--- /dev/null
+++ b/sys/cmd/rc/rcmain.unix
@@ -0,0 +1,35 @@
+# 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 && test -r $profile) . $profile
+ status=''
+ eval $cflag
+}
+if not if(flag i){
+ if(flag l && test -r $profile) . $profile
+ status=''
+ if(! ~ $#* 0) . $*
+ . -i /dev/fd/0
+}
+if not if(~ $#* 0) . /dev/fd/0
+if not{
+ status=''
+ . $*
+}
+exit $status
diff --git a/sys/cmd/rc/rules.mk b/sys/cmd/rc/rules.mk
index 0a5af08..228e15f 100644
--- a/sys/cmd/rc/rules.mk
+++ b/sys/cmd/rc/rules.mk
@@ -20,6 +20,7 @@ SRCS_$(d) := \
$(d)/y.tab.c\
$(d)/unix.c\
$(d)/havefork.c\
+ $(d)/linenoise.c\
$(d)/prompt.c
BINS_$(d) := $(d)/rc
@@ -33,7 +34,7 @@ $(d)/y.tab.h $(d)/y.tab.c: $(d)/syn.y
$(BINS_$(d)): TCFLAGS = \
-D_XOPEN_SOURCE=500
-$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/libn/libn.a /home/nolln/root/lib/libreadline.a /home/nolln/root/lib/libhistory.a /home/nolln/root/lib/libncursesw.a
+$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/libn/libn.a
$(COMPLINK)
include share/pop.mk
diff --git a/sys/cmd/rc/subr.c b/sys/cmd/rc/subr.c
new file mode 100644
index 0000000..a2d8a18
--- /dev/null
+++ b/sys/cmd/rc/subr.c
@@ -0,0 +1,77 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+void*
+emalloc(long n)
+{
+ char *p = (char *)Malloc(n);
+ if(p==0)
+ panic("Can't malloc %d bytes", n);
+/* if(err){ pfmt(err, "malloc %d->%p\n", n, p); flush(err); } /**/
+ memset(p, 0, n);
+ return p;
+}
+
+void
+efree(void *p)
+{
+/* pfmt(err, "free %p\n", p); flush(err); /**/
+ if(p)
+ free(p);
+ else pfmt(err, "free 0\n");
+}
+extern int lastword, lastdol;
+
+void
+yyerror(char *m)
+{
+ pfmt(err, "rc: ");
+ if(runq->cmdfile && !runq->iflag)
+ pfmt(err, "%s:%d: ", runq->cmdfile, runq->lineno);
+ else if(runq->cmdfile)
+ pfmt(err, "%s: ", runq->cmdfile);
+ else if(!runq->iflag)
+ pfmt(err, "line %d: ", runq->lineno);
+ if(tok[0] && tok[0]!='\n')
+ pfmt(err, "token %q: ", tok);
+ pfmt(err, "%s\n", m);
+ flush(err);
+ lastword = 0;
+ lastdol = 0;
+ while(lastc!='\n' && lastc!=EOF) advance();
+ nerror++;
+ setvar("status", newword(m, (word *)0));
+}
+char *bp;
+
+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, long n)
+{
+ bp = s;
+ iacvt(n);
+ *bp='\0';
+}
+
+void
+panic(char *s, int n)
+{
+ pfmt(err, "rc: ");
+ pfmt(err, s, n);
+ pchr(err, '\n');
+ flush(err);
+ Abort();
+}
diff --git a/sys/cmd/rc/syn.y b/sys/cmd/rc/syn.y
new file mode 100644
index 0000000..c7de353
--- /dev/null
+++ b/sys/cmd/rc/syn.y
@@ -0,0 +1,91 @@
+%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' {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'
+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, $1->rtype==HERE?heredoc($2):$2);}
+| 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, (struct 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 {$$=tree2(ARGLIST, $1, $2);}
+| simple redir {$$=tree2(ARGLIST, $1, $2);}
+first: comword
+| first '^' word {$$=tree2('^', $1, $3);}
+word: keyword {lastword=1; $1->type=WORD;}
+| comword
+| word '^' word {$$=tree2('^', $1, $3);}
+comword: '$' word {$$=tree1('$', $2);}
+| '$' word SUB words ')' {$$=tree2(SUB, $2, $4);}
+| '"' word {$$=tree1('"', $2);}
+| COUNT word {$$=tree1(COUNT, $2);}
+| WORD
+| '`' brace {$$=tree1('`', $2);}
+| '(' words ')' {$$=tree1(PAREN, $2);}
+| REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;}
+keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
+words: {$$=(struct tree*)0;}
+| words word {$$=tree2(WORDS, $1, $2);}
diff --git a/sys/cmd/rc/trap.c b/sys/cmd/rc/trap.c
new file mode 100644
index 0000000..a572cac
--- /dev/null
+++ b/sys/cmd/rc/trap.c
@@ -0,0 +1,37 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+#include "io.h"
+extern char *Signame[];
+
+void
+dotrap(void)
+{
+ int i;
+ struct var *trapreq;
+ struct word *starval;
+ starval = vlook("*")->val;
+ while(ntrap) for(i = 0;i!=NSIG;i++) while(trap[i]){
+ --trap[i];
+ --ntrap;
+ if(getpid()!=mypid) Exit(getstatus());
+ trapreq = vlook(Signame[i]);
+ if(trapreq->fn){
+ start(trapreq->fn, trapreq->pc, (struct var *)0);
+ runq->local = newvar(strdup("*"), runq->local);
+ runq->local->val = copywords(starval, (struct word *)0);
+ runq->local->changed = 1;
+ runq->redir = runq->startredir = 0;
+ }
+ else if(i==SIGINT || i==SIGQUIT){
+ /*
+ * run the stack down until we uncover the
+ * command reading loop. Xreturn will exit
+ * if there is none (i.e. if this is not
+ * an interactive rc.)
+ */
+ while(!runq->iflag) Xreturn();
+ }
+ else Exit(getstatus());
+ }
+}
diff --git a/sys/cmd/rc/unix.c b/sys/cmd/rc/unix.c
new file mode 100644
index 0000000..32a744f
--- /dev/null
+++ b/sys/cmd/rc/unix.c
@@ -0,0 +1,627 @@
+/*
+ * Unix versions of system-specific functions
+ * By convention, exported routines herein have names beginning with an
+ * upper case letter.
+ */
+#include "rc.h"
+#include "fns.h"
+#include "io.h"
+#include "exec.h"
+#include "getflags.h"
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <assert.h>
+#include <limits.h>
+
+char *Rcmain = "/home/nolln/root/sys/cmd/rc/rcmain.unix";
+char *Fdprefix = "/dev/fd/";
+
+void execfinit(void);
+
+struct builtin Builtin[] = {
+ "cd", execcd,
+ "whatis", execwhatis,
+ "eval", execeval,
+ "exec", execexec, /* but with popword first */
+ "exit", execexit,
+ "shift", execshift,
+ "wait", execwait,
+ "umask", execumask,
+ ".", execdot,
+ "finit", execfinit,
+ "flag", execflag,
+ /* "history", exechistory, */
+ 0
+};
+#define SEP '\1'
+char **environp;
+
+struct word*
+enval(register char *s)
+{
+ char *t, c;
+ struct word *v;
+ for(t = s;*t && *t!=SEP;t++);
+ c=*t;
+ *t='\0';
+ v = newword(s, c=='\0'?(struct word *)0:enval(t+1));
+ *t = c;
+ return v;
+}
+
+void
+Vinit(void)
+{
+ extern char **environ;
+ char *s;
+ char **env = environ;
+ environp = env;
+ for(;*env;env++){
+ for(s=*env;*s && *s!='(' && *s!='=';s++);
+ switch(*s){
+ case '\0':
+ pfmt(err, "environment %q?\n", *env);
+ break;
+ case '=':
+ *s='\0';
+ setvar(*env, enval(s+1));
+ *s='=';
+ break;
+ case '(': /* ignore functions for now */
+ break;
+ }
+ }
+}
+
+char **envp;
+
+void
+Xrdfn(void)
+{
+ char *s;
+ int len;
+ for(;*envp;envp++){
+ for(s=*envp;*s && *s!='(' && *s!='=';s++);
+ switch(*s){
+ case '\0':
+ pfmt(err, "environment %q?\n", *envp);
+ break;
+ case '=': /* ignore variables */
+ break;
+ case '(': /* Bourne again */
+ s=*envp+3;
+ envp++;
+ len = strlen(s);
+ s[len]='\n';
+ execcmds(opencore(s, len+1));
+ s[len]='\0';
+ return;
+ }
+ }
+ Xreturn();
+}
+
+union code rdfns[4];
+
+void
+execfinit(void)
+{
+ static int first = 1;
+ if(first){
+ rdfns[0].i = 1;
+ rdfns[1].f = Xrdfn;
+ rdfns[2].f = Xjump;
+ rdfns[3].i = 1;
+ first = 0;
+ }
+ Xpopm();
+ envp = environp;
+ start(rdfns, 1, runq->local);
+}
+
+int
+cmpenv(const void *aa, const void *ab)
+{
+ char **a = (char**)aa, **b = (char**)ab;
+
+ return strcmp(*a, *b);
+}
+
+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;
+}
+char *sigmsg[] = {
+/* 0 normal */ 0,
+/* 1 SIGHUP */ "Hangup",
+/* 2 SIGINT */ 0,
+/* 3 SIGQUIT */ "Quit",
+/* 4 SIGILL */ "Illegal instruction",
+/* 5 SIGTRAP */ "Trace/BPT trap",
+/* 6 SIGIOT */ "abort",
+/* 7 SIGEMT */ "EMT trap",
+/* 8 SIGFPE */ "Floating exception",
+/* 9 SIGKILL */ "Killed",
+/* 10 SIGBUS */ "Bus error",
+/* 11 SIGSEGV */ "Memory fault",
+/* 12 SIGSYS */ "Bad system call",
+/* 13 SIGPIPE */ 0,
+/* 14 SIGALRM */ "Alarm call",
+/* 15 SIGTERM */ "Terminated",
+/* 16 unused */ "signal 16",
+/* 17 SIGSTOP */ "Process stopped",
+/* 18 unused */ "signal 18",
+/* 19 SIGCONT */ "Process continued",
+/* 20 SIGCHLD */ "Child death",
+};
+
+int
+Waitfor(int pid, int persist)
+{
+ int wpid, sig;
+ struct thread *p;
+ int wstat;
+ char wstatstr[12];
+
+ for(;;){
+ errno = 0;
+ wpid = wait(&wstat);
+ if(errno==EINTR && persist)
+ continue;
+ if(wpid==-1)
+ break;
+ sig = wstat&0177;
+ if(sig==0177){
+ pfmt(err, "trace: ");
+ sig = (wstat>>8)&0177;
+ }
+ if(sig>(sizeof sigmsg/sizeof sigmsg[0]) || sigmsg[sig]){
+ if(pid!=wpid)
+ pfmt(err, "%d: ", wpid);
+ if(sig<=(sizeof sigmsg/sizeof sigmsg[0]))
+ pfmt(err, "%s", sigmsg[sig]);
+ else if(sig==0177) pfmt(err, "stopped by ptrace");
+ else pfmt(err, "signal %d", sig);
+ if(wstat&0200)pfmt(err, " -- core dumped");
+ pfmt(err, "\n");
+ }
+ wstat = sig?sig+1000:(wstat>>8)&0xFF;
+ if(wpid==pid){
+ inttoascii(wstatstr, wstat);
+ setstatus(wstatstr);
+ break;
+ }
+ else{
+ for(p = runq->ret;p;p = p->ret)
+ if(p->pid==wpid){
+ p->pid=-1;
+ inttoascii(p->status, wstat);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+char **
+mkargv(register struct word *a)
+{
+ char **argv = (char **)emalloc((count(a)+2)*sizeof(char *));
+ char **argp = argv+1; /* leave one at front for runcoms */
+
+ for(;a;a = a->next)
+ *argp++=a->word;
+ *argp = 0;
+ return argv;
+}
+
+void
+Updenv(void)
+{
+}
+
+void
+Execute(struct word *args, struct word *path)
+{
+ char *msg="not found";
+ int txtbusy = 0;
+ char **env = mkenv();
+ char **argv = mkargv(args);
+ char file[512];
+
+ for(;path;path = path->next){
+ strcpy(file, path->word);
+ if(file[0])
+ strcat(file, "/");
+ strcat(file, argv[1]);
+ReExec:
+ execve(file, argv+1, env);
+ switch(errno){
+ case ENOEXEC:
+ pfmt(err, "%s: Bourne again\n", argv[1]);
+ argv[0]="sh";
+ argv[1] = strdup(file);
+ execve("/bin/sh", argv, env);
+ goto Bad;
+ case ETXTBSY:
+ if(++txtbusy!=5){
+ sleep(txtbusy);
+ goto ReExec;
+ }
+ msg="text busy"; goto Bad;
+ case EACCES:
+ msg="no access";
+ break;
+ case ENOMEM:
+ msg="not enough memory"; goto Bad;
+ case E2BIG:
+ msg="too big"; goto Bad;
+ }
+ }
+Bad:
+ pfmt(err, "%s: %s\n", argv[1], msg);
+ efree((char *)env);
+ efree((char *)argv);
+}
+
+#define NDIR NAME_MAX
+
+int
+Globsize(register char *p)
+{
+ int isglob = 0, globlen = NDIR+1;
+ for(;*p;p++){
+ if(*p==GLOB){
+ p++;
+ if(*p!=GLOB)
+ isglob++;
+ globlen+=*p=='*'?NDIR:1;
+ }
+ else
+ globlen++;
+ }
+ return isglob?globlen:0;
+}
+
+#define NDIRLIST 50
+
+DIR *dirlist[NDIRLIST];
+
+int
+Opendir(char *name)
+{
+ DIR **dp;
+ for(dp = dirlist;dp!=&dirlist[NDIRLIST];dp++)
+ if(*dp==0){
+ *dp = opendir(name);
+ return *dp?dp-dirlist:-1;
+ }
+ return -1;
+}
+
+int
+Readdir(int f, char *p, int onlydirs)
+{
+ struct dirent *dp;
+
+ if(f<0 || f>=NDIRLIST)
+ return 0;
+
+ dp = readdir(dirlist[f]);
+ if(dp==0)
+ return 0;
+ strcpy(p, dp->d_name);
+ return 1;
+}
+
+void
+Closedir(int f)
+{
+ if(f<0 || f>=NDIRLIST)
+ return;
+
+ closedir(dirlist[f]);
+ dirlist[f] = 0;
+}
+
+char *Signame[] = {
+ "sigexit", "sighup", "sigint", "sigquit",
+ "sigill", "sigtrap", "sigiot", "sigemt",
+ "sigfpe", "sigkill", "sigbus", "sigsegv",
+ "sigsys", "sigpipe", "sigalrm", "sigterm",
+ "sig16", "sigstop", "sigtstp", "sigcont",
+ "sigchld", "sigttin", "sigttou", "sigtint",
+ "sigxcpu", "sigxfsz", "sig26", "sig27",
+ "sig28", "sig29", "sig30", "sig31",
+ 0,
+};
+
+void
+gettrap(int sig)
+{
+ signal(sig, gettrap);
+ trap[sig]++;
+ ntrap++;
+ if(ntrap>=NSIG){
+ pfmt(err, "rc: Too many traps (trap %d), dumping core\n", sig);
+ signal(SIGABRT, (void (*)())0);
+ kill(getpid(), SIGABRT);
+ }
+}
+
+void
+Trapinit(void)
+{
+ int i;
+ void (*sig)();
+
+ if(1 || flag['d']){ /* wrong!!! */
+ sig = signal(SIGINT, gettrap);
+ if(sig==SIG_IGN)
+ signal(SIGINT, SIG_IGN);
+ }
+ else{
+ for(i = 1;i<=NSIG;i++) if(i!=SIGCHLD){
+ sig = signal(i, gettrap);
+ if(sig==SIG_IGN)
+ signal(i, SIG_IGN);
+ }
+ }
+}
+
+void
+Unlink(char *name)
+{
+ unlink(name);
+}
+
+long
+Write(int fd, char *buf, long cnt)
+{
+ return write(fd, buf, cnt);
+}
+
+long
+Read(int fd, char *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
+Creat(char *file)
+{
+ return creat(file, 0666);
+}
+
+int
+Dup(int a, int b){
+ return dup2(a, b);
+}
+
+int
+Dup1(int a){
+ return dup(a);
+}
+/*
+ * Wrong: should go through components of a|b|c and return the maximum.
+ */
+void
+Exit(char *stat)
+{
+ int n = 0;
+
+ while(*stat){
+ if(*stat!='|'){
+ if(*stat<'0' || '9'<*stat)
+ exit(1);
+ else n = n*10+*stat-'0';
+ }
+ stat++;
+ }
+ exit(n);
+}
+
+int
+Eintr(void){
+ return errno==EINTR;
+}
+
+void
+Noerror(void)
+{
+ errno = 0;
+}
+
+int
+Isatty(int fd){
+ return isatty(fd);
+}
+
+void
+Abort(void)
+{
+ abort();
+}
+
+void
+execumask(void) /* wrong -- should fork before writing */
+{
+ int m;
+ struct io out[1];
+ switch(count(runq->argv->words)){
+ default:
+ pfmt(err, "Usage: umask [umask]\n");
+ setstatus("umask usage");
+ poplist();
+ return;
+ case 2:
+ umask(octal(runq->argv->words->next->word));
+ break;
+ case 1:
+ umask(m = umask(0));
+ out->fd = mapfd(1);
+ out->bufp = out->buf;
+ out->ebuf=&out->buf[NBUF];
+ out->strp = 0;
+ pfmt(out, "%o\n", m);
+ break;
+ }
+ setstatus("");
+ poplist();
+}
+
+void
+Memcpy(char *a, char *b, long n)
+{
+ memmove(a, b, n);
+}
+
+void*
+Malloc(unsigned long n)
+{
+ return (void *)malloc(n);
+}
+
+void
+errstr(char *buf, int len)
+{
+ strncpy(buf, strerror(errno), len);
+}
+
+int
+needsrcquote(int c)
+{
+ if(c <= ' ')
+ return 1;
+ if(strchr("`^#*[]=|\\?${}()'<>&;", c))
+ return 1;
+ return 0;
+}
+
+int
+rfork(int bits)
+{
+ return fork();
+}
+
+int *waitpids;
+int nwaitpids;
+
+void
+addwaitpid(int pid)
+{
+ waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]);
+ if(waitpids == 0)
+ panic("Can't realloc %d waitpids", nwaitpids+1);
+ 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;
+}
+
+int
+chartorune(wchar_t *rune, const char *str)
+{
+ int r = mbtowc(rune, str, strlen(str));
+ if(r < 0){
+ *rune = 0xFFFD;
+ return 1;
+ }
+ return r;
+}
+
diff --git a/sys/cmd/rc/unix.h b/sys/cmd/rc/unix.h
new file mode 100644
index 0000000..84ff25e
--- /dev/null
+++ b/sys/cmd/rc/unix.h
@@ -0,0 +1,56 @@
+
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <string.h>
+
+#ifndef NSIG
+#define NSIG 32
+#endif
+
+#ifndef ERRMAX
+#define ERRMAX 128
+#endif
+
+/* plan 9 compatibility */
+#define RFPROC 1
+#define RFFDG 1
+#define RFNOTEG 1
+
+#define uintptr uintptr_t
+#define Rune wchar_t
+
+#define nil ((void*)0)
+
+/* in case uchar, etc. are built-in types */
+#define uchar _fmtuchar
+#define ushort _fmtushort
+#define uint _fmtuint
+#define ulong _fmtulong
+#define vlong _fmtvlong
+#define uvlong _fmtuvlong
+
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef unsigned long long uvlong;
+
+#define OREAD O_RDONLY
+#define OWRITE O_WRONLY
+#define ORDWR O_RDWR
+#define OCEXEC 0
+
+extern char *argv0;
+extern int chartorune(wchar_t *, const char *);
+extern int rfork(int);
+extern void errstr(char *, int);
+#define rerrstr errstr
+
+extern void exechistory(void);
+
diff --git a/sys/cmd/rc/x.tab.h b/sys/cmd/rc/x.tab.h
new file mode 100644
index 0000000..0f5b1bd
--- /dev/null
+++ b/sys/cmd/rc/x.tab.h
@@ -0,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;
+
diff --git a/sys/cmd/rc/y.tab.c b/sys/cmd/rc/y.tab.c
new file mode 100644
index 0000000..744b527
--- /dev/null
+++ b/sys/cmd/rc/y.tab.c
@@ -0,0 +1,1979 @@
+/* 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 62
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 346
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 40
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 24
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 71
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 116
+
+/* 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,
+ 181, 183, 185, 187, 189, 191, 193, 195, 197, 199,
+ 201, 202
+};
+
+/* 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, 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, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 90, 91
+};
+#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,
+ 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, 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, 70, 56,
+ 0, 18, 0, 0, 0, 19, 70, 0, 0, 0,
+ 19, 15, 19, 19, 4, 33, 44, 47, 0, 19,
+ 29, 23, 21, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 0, 70, 50, 49, 40, 41, 31,
+ 43, 59, 17, 52, 54, 55, 0, 10, 19, 6,
+ 0, 57, 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, 71, 70,
+ 12, 7, 11, 58, 16, 37, 36, 35, 48, 14,
+ 70, 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, 66,
+ 23, 59, 83, 82, 114, 110, 81, 86, 25, 26,
+ 88, 27, 46, 50
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -30
+static const yytype_int16 yypact[] =
+{
+ 121, -29, -23, -6, 295, 307, 307, 295, -30, -30,
+ 134, -30, 295, 295, 295, 307, -30, -20, 12, -15,
+ 307, -4, 307, 307, 61, 171, -19, -30, 295, 307,
+ -30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
+ -30, -30, -30, -20, 1, -30, -30, 24, 24, 1,
+ 134, -30, 1, 29, -30, -30, 11, -30, 307, 34,
+ 184, -30, -30, -30, -30, 295, -30, -4, 24, 24,
+ 307, 307, 307, -30, -30, -30, 1, 295, 295, 9,
+ 23, 307, 307, 307, 295, 295, -20, -30, 1, -30,
+ -30, -30, -30, -30, -30, -30, 24, 24, -30, 1,
+ -30, -30, -30, 36, 36, 36, -30, -30, 221, 258,
+ 307, -30, -30, 36, 307, 36
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -30, -30, 33, -25, 16, -30, 15, 48, -30, -13,
+ -18, 0, -30, -30, -30, -30, -30, -30, -30, -30,
+ 31, 14, -30, -14
+};
+
+/* 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[] =
+{
+ 24, 31, 60, 67, 80, 47, 48, 75, 28, 77,
+ 65, 11, 62, 100, 29, 15, 20, 63, 45, 78,
+ 24, 45, 68, 69, 45, 51, 45, 45, 45, 84,
+ 85, 29, 61, 91, 101, 44, 20, 84, 49, 45,
+ 70, 52, 45, 53, 54, 55, 89, 90, 102, 67,
+ 70, 32, 70, 64, 94, 0, 76, 0, 51, 79,
+ 71, 72, 71, 72, 45, 87, 92, 73, 74, 0,
+ 95, 96, 97, 0, 45, 108, 0, 70, 0, 45,
+ 0, 103, 104, 105, 0, 0, 109, 71, 72, 0,
+ 0, 45, 45, 0, 73, 74, 52, 0, 45, 45,
+ 0, 107, 0, 0, 0, 0, 0, 0, 98, 99,
+ 113, 0, 0, 0, 115, 106, 0, 0, 0, 0,
+ 0, -2, 45, 45, 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, 93,
+ 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, 111, 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, 112, 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, 7, 16, 21, 29, 5, 6, 25, 37, 28,
+ 14, 15, 0, 4, 37, 35, 0, 32, 4, 38,
+ 20, 7, 22, 23, 10, 10, 12, 13, 14, 28,
+ 44, 37, 17, 58, 25, 4, 20, 28, 7, 25,
+ 16, 10, 28, 12, 13, 14, 17, 36, 25, 67,
+ 16, 3, 16, 20, 67, -1, 25, -1, 43, 28,
+ 26, 27, 26, 27, 50, 50, 32, 33, 34, -1,
+ 70, 71, 72, -1, 60, 89, -1, 16, -1, 65,
+ -1, 81, 82, 83, -1, -1, 100, 26, 27, -1,
+ -1, 77, 78, -1, 33, 34, 65, -1, 84, 85,
+ -1, 86, -1, -1, -1, -1, -1, -1, 77, 78,
+ 110, -1, -1, -1, 114, 84, -1, -1, -1, -1,
+ -1, 0, 108, 109, 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, 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, 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"
+ {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 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[(1) - (2)].tree)->rtype==HERE?heredoc((yyvsp[(2) - (2)].tree)):(yyvsp[(2) - (2)].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), (struct 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)=tree2(ARGLIST, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+ break;
+
+ case 46:
+#line 75 "syn.y"
+ {(yyval.tree)=tree2(ARGLIST, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+ break;
+
+ case 48:
+#line 77 "syn.y"
+ {(yyval.tree)=tree2('^', (yyvsp[(1) - (3)].tree), (yyvsp[(3) - (3)].tree));}
+ break;
+
+ case 49:
+#line 78 "syn.y"
+ {lastword=1; (yyvsp[(1) - (1)].tree)->type=WORD;}
+ break;
+
+ case 51:
+#line 80 "syn.y"
+ {(yyval.tree)=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)=tree1('`', (yyvsp[(2) - (2)].tree));}
+ break;
+
+ case 58:
+#line 87 "syn.y"
+ {(yyval.tree)=tree1(PAREN, (yyvsp[(2) - (3)].tree));}
+ break;
+
+ case 59:
+#line 88 "syn.y"
+ {(yyval.tree)=mung1((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)); (yyval.tree)->type=PIPEFD;}
+ break;
+
+ case 70:
+#line 90 "syn.y"
+ {(yyval.tree)=(struct tree*)0;}
+ break;
+
+ case 71:
+#line 91 "syn.y"
+ {(yyval.tree)=tree2(WORDS, (yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree));}
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 1766 "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);
+}
+
+
+
diff --git a/sys/cmd/rc/y.tab.h b/sys/cmd/rc/y.tab.h
new file mode 100644
index 0000000..0f5b1bd
--- /dev/null
+++ b/sys/cmd/rc/y.tab.h
@@ -0,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;
+