aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPetter Rodhelind <petter.rodhelind@gmail.com>2020-05-06 20:13:52 +0200
committerPetter Rodhelind <petter.rodhelind@gmail.com>2020-05-06 20:13:52 +0200
commitea23656f7c3afcfd8516b00c0db09879ae80a09f (patch)
tree5fc057fb21b7efb41c096148da7023ca9ed10e3d /src
parent9c79e48c93c0c4d14aabcb490fab048d68934cb2 (diff)
parentacffdcb6eed3385e1566c0ac86fb6b4bc130664b (diff)
downloadplan9port-ea23656f7c3afcfd8516b00c0db09879ae80a09f.tar.gz
plan9port-ea23656f7c3afcfd8516b00c0db09879ae80a09f.tar.bz2
plan9port-ea23656f7c3afcfd8516b00c0db09879ae80a09f.zip
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src')
-rw-r--r--src/cmd/acme/cols.c8
-rwxr-xr-xsrc/cmd/rc/checkparse23
-rw-r--r--src/cmd/rc/code.c10
-rw-r--r--src/cmd/rc/exec.c4
-rw-r--r--src/cmd/rc/fns.h1
-rw-r--r--src/cmd/rc/io.c5
-rw-r--r--src/cmd/rc/io.h1
-rw-r--r--src/cmd/rc/lex.c35
-rw-r--r--src/cmd/rc/mkfile1
-rw-r--r--src/cmd/rc/parse.c552
-rw-r--r--src/cmd/rc/pcmd.c118
-rw-r--r--src/cmd/rc/simple.c1
-rw-r--r--src/cmd/rc/syn.y10
-rw-r--r--src/cmd/rc/test.rc93
14 files changed, 840 insertions, 22 deletions
diff --git a/src/cmd/acme/cols.c b/src/cmd/acme/cols.c
index 63c33ac1..63247a84 100644
--- a/src/cmd/acme/cols.c
+++ b/src/cmd/acme/cols.c
@@ -232,7 +232,7 @@ colmousebut(Column *c)
void
colresize(Column *c, Rectangle r)
{
- int i;
+ int i, old, new;
Rectangle r1, r2;
Window *w;
@@ -245,6 +245,8 @@ colresize(Column *c, Rectangle r)
r1.max.y += Border;
draw(screen, r1, display->black, nil, ZP);
r1.max.y = r.max.y;
+ new = Dy(r) - c->nw*(Border + font->height);
+ old = Dy(c->r) - c->nw*(Border + font->height);
for(i=0; i<c->nw; i++){
w = c->w[i];
w->maxlines = 0;
@@ -252,8 +254,8 @@ colresize(Column *c, Rectangle r)
r1.max.y = r.max.y;
else{
r1.max.y = r1.min.y;
- if(Dy(c->r) != 0){
- r1.max.y += (Dy(w->r)+Border)*Dy(r)/Dy(c->r);
+ if(new > 0 && old > 0 && Dy(w->r) > Border+font->height){
+ r1.max.y += (Dy(w->r)-Border-font->height)*new/old + Border + font->height;
}
}
r1.max.y = max(r1.max.y, r1.min.y + Border+font->height);
diff --git a/src/cmd/rc/checkparse b/src/cmd/rc/checkparse
new file mode 100755
index 00000000..0a1472b2
--- /dev/null
+++ b/src/cmd/rc/checkparse
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+aflag=false
+if [ "$1" = -a ]; then
+ aflag=true
+ shift
+fi
+
+files="$@"
+if [ $# = 0 ]; then
+ files=$(echo ./test.rc;
+ grep -l '^#!/usr/local/plan9/bin/rc' /usr/local/plan9/bin/{*,*/*} 2>/dev/null;
+ grep -R -l '^#!/bin/rc' $HOME/pub/plan9 | egrep -v 'plan9/(lib/(oui|pci)|sys/man|sys/lib/man|sys/lib/scsicodes)' 2>/dev/null)
+fi
+
+for i in $files
+do
+ if ! diff <(./o.rc -DY $i 2>&1) <(./o.rc -D $i 2>&1); then
+ echo '^^^' $i
+ ! $aflag && exit 1
+ fi
+done
+
diff --git a/src/cmd/rc/code.c b/src/cmd/rc/code.c
index eeaa3ed8..208476ae 100644
--- a/src/cmd/rc/code.c
+++ b/src/cmd/rc/code.c
@@ -41,6 +41,16 @@ stuffdot(int a)
int
compile(tree *t)
{
+ if(flag['D']) {
+ struct io *s;
+ s = openstr();
+ pfmt(s, "compile: %u\n", t);
+ write(2, s->strp, strlen(s->strp));
+ closeio(s);
+ if(eflagok) // made it out of rcmain - stop executing commands, just print them
+ t = nil;
+ }
+
ncode = 100;
codebuf = (code *)emalloc(ncode*sizeof codebuf[0]);
codep = 0;
diff --git a/src/cmd/rc/exec.c b/src/cmd/rc/exec.c
index 172acfa3..0320976a 100644
--- a/src/cmd/rc/exec.c
+++ b/src/cmd/rc/exec.c
@@ -139,7 +139,7 @@ main(int argc, char *argv[])
/* needed for rcmain later */
putenv("PLAN9", unsharp("#9"));
- argc = getflags(argc, argv, "SsrdiIlxepvVc:1m:1[command]", 1);
+ argc = getflags(argc, argv, "DSYsrdiIlxepvVc:1m:1[command]", 1);
if(argc==-1)
usage("[file [arg ...]]");
if(argv[0][0]=='-')
@@ -901,7 +901,7 @@ Xrdcmds(void)
promptstr="% ";
}
Noerror();
- if(yyparse()){
+ if((flag['Y'] ? yyparse : parse)()){
if(!p->iflag || p->eof && !Eintr()){
if(p->cmdfile)
efree(p->cmdfile);
diff --git a/src/cmd/rc/fns.h b/src/cmd/rc/fns.h
index c4d201dc..5721d617 100644
--- a/src/cmd/rc/fns.h
+++ b/src/cmd/rc/fns.h
@@ -65,3 +65,4 @@ int wordchr(int);
void yyerror(char*);
int yylex(void);
int yyparse(void);
+int parse(void);
diff --git a/src/cmd/rc/io.c b/src/cmd/rc/io.c
index bb8af4ab..c2e9d7b4 100644
--- a/src/cmd/rc/io.c
+++ b/src/cmd/rc/io.c
@@ -44,7 +44,10 @@ pfmt(io *f, char *fmt, ...)
pstr(f, va_arg(ap, char *));
break;
case 't':
- pcmd(f, va_arg(ap, struct tree *));
+ pcmd(f, va_arg(ap, tree *));
+ break;
+ case 'u':
+ pcmdu(f, va_arg(ap, tree *));
break;
case 'v':
pval(f, va_arg(ap, struct word *));
diff --git a/src/cmd/rc/io.h b/src/cmd/rc/io.h
index 21cc6b8e..6c75cc5b 100644
--- a/src/cmd/rc/io.h
+++ b/src/cmd/rc/io.h
@@ -25,6 +25,7 @@ void pquo(io*, char*);
void pwrd(io*, char*);
void pstr(io*, char*);
void pcmd(io*, tree*);
+void pcmdu(io*, tree*);
void pval(io*, word*);
void pfnc(io*, thread*);
void pfmt(io*, char*, ...);
diff --git a/src/cmd/rc/lex.c b/src/cmd/rc/lex.c
index 973ddee8..e4410c00 100644
--- a/src/cmd/rc/lex.c
+++ b/src/cmd/rc/lex.c
@@ -102,15 +102,17 @@ pprompt(void)
doprompt = 0;
}
-void
+int
skipwhite(void)
{
- int c;
+ int c, skipped;
+ skipped = 0;
for(;;){
c = nextc();
/* Why did this used to be if(!inquote && c=='#') ?? */
if(c=='#'){
incomm = 1;
+ skipped = 1;
for(;;){
c = nextc();
if(c=='\n' || c==EOF) {
@@ -120,9 +122,12 @@ skipwhite(void)
advance();
}
}
- if(c==' ' || c=='\t')
+ if(c==' ' || c=='\t') {
+ skipped = 1;
advance();
- else return;
+ }
+ else
+ return skipped;
}
}
@@ -188,7 +193,7 @@ yylex(void)
{
int c, d = nextc();
char *w = tok;
- struct tree *t;
+ tree *t;
yylval.tree = 0;
/*
* Embarassing sneakiness: if the last token read was a quoted or unquoted
@@ -197,11 +202,11 @@ yylex(void)
* if the next character is the first character of a simple or compound word,
* we insert a `^' before it.
*/
- if(lastword){
+ if(lastword && flag['Y']){
lastword = 0;
if(d=='('){
advance();
- strcpy(tok, "( [SUB]");
+ strcpy(tok, "(");
return SUB;
}
if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
@@ -210,7 +215,8 @@ yylex(void)
}
}
inquote = 0;
- skipwhite();
+ if(skipwhite() && !flag['Y'])
+ return ' ';
switch(c = advance()){
case EOF:
lastdol = 0;
@@ -231,7 +237,8 @@ yylex(void)
case '&':
lastdol = 0;
if(nextis('&')){
- skipnl();
+ if(flag['Y'])
+ skipnl();
strcpy(tok, "&&");
return ANDAND;
}
@@ -240,7 +247,8 @@ yylex(void)
case '|':
lastdol = 0;
if(nextis(c)){
- skipnl();
+ if(flag['Y'])
+ skipnl();
strcpy(tok, "||");
return OROR;
}
@@ -329,8 +337,13 @@ yylex(void)
}
*w='\0';
yylval.tree = t;
- if(t->type==PIPE)
+ if(t->type==PIPE && flag['Y'])
skipnl();
+ if(t->type==REDIR) {
+ skipwhite();
+ if(nextc() == '{')
+ t->type = REDIRW;
+ }
return t->type;
case '\'':
lastdol = 0;
diff --git a/src/cmd/rc/mkfile b/src/cmd/rc/mkfile
index 79b407f8..c1e77aef 100644
--- a/src/cmd/rc/mkfile
+++ b/src/cmd/rc/mkfile
@@ -10,6 +10,7 @@ OFILES=\
here.$O\
io.$O\
lex.$O\
+ parse.$O\
pcmd.$O\
pfnc.$O\
simple.$O\
diff --git a/src/cmd/rc/parse.c b/src/cmd/rc/parse.c
new file mode 100644
index 00000000..dd102190
--- /dev/null
+++ b/src/cmd/rc/parse.c
@@ -0,0 +1,552 @@
+#include "rc.h"
+#include "io.h"
+#include "fns.h"
+
+static tree* body(int tok, int *ptok);
+static tree* brace(int tok);
+static tree* cmd(int tok, int *ptok);
+static tree* cmd2(int tok, int *ptok);
+static tree* cmd3(int tok, int *ptok);
+static tree* cmds(int tok, int *ptok, int nlok);
+static tree* epilog(int tok, int *ptok);
+static int iswordtok(int tok);
+static tree* line(int tok, int *ptok);
+static tree* paren(int tok);
+static tree* yyredir(int tok, int *ptok);
+static tree* yyword(int tok, int *ptok, int eqok);
+static tree* word1(int tok, int *ptok);
+static tree* words(int tok, int *ptok);
+
+static jmp_buf yyjmp;
+
+static int
+dropnl(int tok)
+{
+ while(tok == ' ' || tok == '\n')
+ tok = yylex();
+ return tok;
+}
+
+static int
+dropsp(int tok)
+{
+ while(tok == ' ')
+ tok = yylex();
+ return tok;
+}
+
+static void
+syntax(int tok)
+{
+ USED(tok);
+ yyerror("syntax error");
+ longjmp(yyjmp, 1);
+}
+
+int
+parse(void)
+{
+ tree *t;
+ int tok;
+
+ if(setjmp(yyjmp))
+ return 1;
+
+ // rc: { return 1;}
+ // | line '\n' {return !compile($1);}
+
+ tok = dropsp(yylex());
+ if(tok == EOF)
+ return 1;
+ t = line(tok, &tok);
+ if(tok != '\n')
+ yyerror("missing newline at end of line");
+ yylval.tree = t;
+ return !compile(t);
+}
+
+static tree*
+line(int tok, int *ptok)
+{
+ return cmds(tok, ptok, 0);
+}
+
+static tree*
+body(int tok, int *ptok)
+{
+ return cmds(tok, ptok, 1);
+}
+
+static tree*
+cmds(int tok, int *ptok, int nlok)
+{
+ tree *t, **last, *t2;
+
+ // line: cmd
+ // | cmdsa line {$$=tree2(';', $1, $2);}
+ // cmdsa: cmd ';'
+ // | cmd '&' {$$=tree1('&', $1);}
+
+ // body: cmd
+ // | cmdsan body {$$=tree2(';', $1, $2);}
+ // cmdsan: cmdsa
+ // | cmd '\n'
+
+ t = nil;
+ last = nil;
+ for(;;) {
+ t2 = cmd(tok, &tok);
+ if(tok == '&')
+ t2 = tree1('&', t2);
+ if(t2 != nil) {
+ // slot into list t
+ if(last == nil) {
+ t = t2;
+ last = &t;
+ } else {
+ *last = tree2(';', *last, t2);
+ last = &(*last)->child[1];
+ }
+ }
+ if(tok != ';' && tok != '&' && (!nlok || tok != '\n'))
+ break;
+ tok = yylex();
+ }
+ *ptok = tok;
+ return t;
+}
+
+static tree*
+brace(int tok)
+{
+ tree *t;
+
+ // brace: '{' body '}' {$$=tree1(BRACE, $2);}
+
+ tok = dropsp(tok);
+ if(tok != '{')
+ syntax(tok);
+ t = body(yylex(), &tok);
+ if(tok != '}')
+ syntax(tok);
+ return tree1(BRACE, t);
+}
+
+static tree*
+paren(int tok)
+{
+ tree *t;
+
+ // paren: '(' body ')' {$$=tree1(PCMD, $2);}
+
+ tok = dropsp(tok);
+ if(tok != '(')
+ syntax(tok);
+ t = body(yylex(), &tok);
+ if(tok != ')')
+ syntax(tok);
+ return tree1(PCMD, t);
+}
+
+static tree*
+epilog(int tok, int *ptok)
+{
+ tree *t, *r;
+
+ // epilog: {$$=0;}
+ // | redir epilog {$$=mung2($1, $1->child[0], $2);}
+
+ if(tok != REDIR && tok != DUP) {
+ *ptok = tok;
+ return nil;
+ }
+
+ r = yyredir(tok, &tok);
+ t = epilog(tok, &tok);
+ *ptok = tok;
+ return mung2(r, r->child[0], t);
+}
+
+static tree*
+yyredir(int tok, int *ptok)
+{
+ tree *r, *w;
+
+ // redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);}
+ // | DUP
+
+ switch(tok) {
+ default:
+ syntax(tok);
+ case DUP:
+ r = yylval.tree;
+ *ptok = dropsp(yylex());
+ break;
+ case REDIR:
+ r = yylval.tree;
+ w = yyword(yylex(), &tok, 1);
+ *ptok = dropsp(tok);
+ r = mung1(r, r->rtype==HERE?heredoc(w):w);
+ break;
+ }
+ return r;
+}
+
+static tree*
+cmd(int tok, int *ptok)
+{
+ int op;
+ tree *t1, *t2;
+
+ // | cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);}
+ // | cmd OROR cmd {$$=tree2(OROR, $1, $3);}
+
+ tok = dropsp(tok);
+ t1 = cmd2(tok, &tok);
+ while(tok == ANDAND || tok == OROR) {
+ op = tok;
+ t2 = cmd2(dropnl(yylex()), &tok);
+ t1 = tree2(op, t1, t2);
+ }
+ *ptok = tok;
+ return t1;
+}
+
+static tree*
+cmd2(int tok, int *ptok)
+{
+ tree *t1, *t2, *t3;
+
+ // | cmd PIPE cmd {$$=mung2($2, $1, $3);}
+ t1 = cmd3(tok, &tok);
+ while(tok == PIPE) {
+ t2 = yylval.tree;
+ t3 = cmd3(dropnl(yylex()), &tok);
+ t1 = mung2(t2, t1, t3);
+ }
+ *ptok = tok;
+ return t1;
+}
+
+static tree*
+cmd3(int tok, int *ptok)
+{
+ tree *t1, *t2, *t3, *t4;
+
+ tok = dropsp(tok);
+ switch(tok) {
+ case ';':
+ case '&':
+ case '\n':
+ *ptok = tok;
+ return nil;
+
+ case IF:
+ // | IF paren {skipnl();} cmd {$$=mung2($1, $2, $4);}
+ // | IF NOT {skipnl();} cmd {$$=mung1($2, $4);}
+ t1 = yylval.tree;
+ tok = dropsp(yylex());
+ if(tok == NOT) {
+ t1 = yylval.tree;
+ t2 = cmd(dropnl(yylex()), ptok);
+ return mung1(t1, t2);
+ }
+ t2 = paren(tok);
+ t3 = cmd(dropnl(yylex()), ptok);
+ return mung2(t1, t2, t3);
+
+ case FOR:
+ // | FOR '(' word IN words ')' {skipnl();} cmd
+ // {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);}
+ // | FOR '(' word ')' {skipnl();} cmd
+ // {$$=mung3($1, $3, (tree *)0, $6);}
+ t1 = yylval.tree;
+ tok = dropsp(yylex());
+ if(tok != '(')
+ syntax(tok);
+ t2 = yyword(yylex(), &tok, 1);
+ switch(tok) {
+ default:
+ syntax(tok);
+ case ')':
+ t3 = nil;
+ break;
+ case IN:
+ t3 = words(yylex(), &tok);
+ if(t3 == nil)
+ t3 = tree1(PAREN, nil);
+ if(tok != ')')
+ syntax(tok);
+ break;
+ }
+ t4 = cmd(dropnl(yylex()), ptok);
+ return mung3(t1, t2, t3, t4);
+
+ case WHILE:
+ // | WHILE paren {skipnl();} cmd
+ // {$$=mung2($1, $2, $4);}
+ t1 = yylval.tree;
+ t2 = paren(yylex());
+ t3 = cmd(dropnl(yylex()), ptok);
+ return mung2(t1, t2, t3);
+
+ case SWITCH:
+ // | SWITCH word {skipnl();} brace
+ // {$$=tree2(SWITCH, $2, $4);}
+ t1 = yyword(yylex(), &tok, 1);
+ tok = dropnl(tok); // doesn't work in yacc grammar but works here!
+ t2 = brace(tok);
+ *ptok = dropsp(yylex());
+ return tree2(SWITCH, t1, t2);
+ // Note: cmd: a && for(x) y && b is a && {for (x) {y && b}}.
+ return cmd(tok, ptok);
+
+ case FN:
+ // | FN words brace {$$=tree2(FN, $2, $3);}
+ // | FN words {$$=tree1(FN, $2);}
+ t1 = words(yylex(), &tok);
+ if(tok != '{') {
+ *ptok = tok;
+ return tree1(FN, t1);
+ }
+ t2 = brace(tok);
+ *ptok = dropsp(yylex());
+ return tree2(FN, t1, t2);
+
+ case TWIDDLE:
+ // | TWIDDLE word words {$$=mung2($1, $2, $3);}
+ t1 = yylval.tree;
+ t2 = yyword(yylex(), &tok, 1);
+ t3 = words(tok, ptok);
+ return mung2(t1, t2, t3);
+
+ case BANG:
+ case SUBSHELL:
+ // | BANG cmd {$$=mung1($1, $2);}
+ // | SUBSHELL cmd {$$=mung1($1, $2);}
+ // Note: cmd2: ! x | y is !{x | y} not {!x} | y.
+ t1 = yylval.tree;
+ return mung1(t1, cmd2(yylex(), ptok));
+
+ case REDIR:
+ case DUP:
+ // | redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);}
+ // Note: cmd2: {>x echo a | tr a-z A-Z} writes A to x.
+ t1 = yyredir(tok, &tok);
+ t2 = cmd2(tok, ptok);
+ return mung2(t1, t1->child[0], t2);
+
+ case '{':
+ // | brace epilog {$$=epimung($1, $2);}
+ t1 = brace(tok);
+ tok = dropsp(yylex());
+ t2 = epilog(tok, ptok);
+ return epimung(t1, t2);
+ }
+
+ if(!iswordtok(tok)) {
+ *ptok = tok;
+ return nil;
+ }
+
+ // cmd: ...
+ // | simple {$$=simplemung($1);}
+ // | assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);}
+ // assign: first '=' word {$$=tree2('=', $1, $3);}
+ // Note: first is same as word except for disallowing all the leading keywords,
+ // but all those keywords have been picked off in the switch above.
+ // Except NOT, but disallowing that in yacc was likely a mistake anyway:
+ // there's no ambiguity in not=1 or not x y z.
+ t1 = yyword(tok, &tok, 0);
+ if(tok == '=') {
+ // assignment
+ // Note: cmd2: {x=1 true | echo $x} echoes 1.
+ t1 = tree2('=', t1, yyword(yylex(), &tok, 1));
+ t2 = cmd2(tok, ptok);
+ return mung3(t1, t1->child[0], t1->child[1], t2);
+ }
+
+ // simple: first
+ // | simple word {$$=tree2(ARGLIST, $1, $2);}
+ // | simple redir {$$=tree2(ARGLIST, $1, $2);}
+ for(;;) {
+ if(tok == REDIR || tok == DUP) {
+ t1 = tree2(ARGLIST, t1, yyredir(tok, &tok));
+ } else if(iswordtok(tok)) {
+ t1 = tree2(ARGLIST, t1, yyword(tok, &tok, 1));
+ } else {
+ break;
+ }
+ }
+ *ptok = tok;
+ return simplemung(t1);
+}
+
+static tree*
+words(int tok, int *ptok)
+{
+ tree *t;
+
+ // words: {$$=(tree*)0;}
+ // | words word {$$=tree2(WORDS, $1, $2);}
+
+ t = nil;
+ tok = dropsp(tok);
+ while(iswordtok(tok))
+ t = tree2(WORDS, t, yyword(tok, &tok, 1));
+ *ptok = tok;
+ return t;
+}
+
+static tree*
+yyword(int tok, int *ptok, int eqok)
+{
+ tree *t;
+
+ // 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
+ //
+ // factored into:
+ //
+ // word: word1
+ // | word '^' word1
+ //
+ // word1: keyword | comword
+
+ t = word1(tok, &tok);
+ if(tok == '=' && !eqok)
+ goto out;
+ for(;;) {
+ if(iswordtok(tok)) {
+ // No free carats around parens.
+ if(t->type == PAREN || tok == '(')
+ syntax(tok);
+ t = tree2('^', t, word1(tok, &tok));
+ continue;
+ }
+ tok = dropsp(tok);
+ if(tok == '^') {
+ t = tree2('^', t, word1(yylex(), &tok));
+ continue;
+ }
+ break;
+ }
+out:
+ *ptok = dropsp(tok);
+ return t;
+}
+
+static tree*
+word1(int tok, int *ptok)
+{
+ tree *w, *sub, *t;
+
+ tok = dropsp(tok);
+ switch(tok) {
+ default:
+ syntax(tok);
+
+ case WORD:
+ case FOR:
+ case IN:
+ case WHILE:
+ case IF:
+ case NOT:
+ case TWIDDLE:
+ case BANG:
+ case SUBSHELL:
+ case SWITCH:
+ case FN:
+ // | WORD
+ // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
+ t = yylval.tree;
+ t->type = WORD;
+ *ptok = yylex();
+ return t;
+
+ case '=':
+ *ptok = yylex();
+ return token("=", WORD);
+
+ case '$':
+ // comword: '$' word1 {$$=tree1('$', $2);}
+ // | '$' word1 SUB words ')' {$$=tree2(SUB, $2, $4);}
+ w = word1(yylex(), &tok);
+ if(tok == '(') {
+ sub = words(yylex(), &tok);
+ if(tok != ')')
+ syntax(tok);
+ *ptok = yylex();
+ return tree2(SUB, w, sub);
+ }
+ *ptok = tok;
+ return tree1('$', w);
+
+ case '"':
+ // | '"' word1 {$$=tree1('"', $2);}
+ return tree1('"', word1(yylex(), ptok));
+
+ case COUNT:
+ // | COUNT word1 {$$=tree1(COUNT, $2);}
+ return tree1(COUNT, word1(yylex(), ptok));
+
+ case '`':
+ // | '`' brace {$$=tree1('`', $2);}
+ t = tree1('`', brace(yylex()));
+ *ptok = yylex();
+ return t;
+
+ case '(':
+ // | '(' words ')' {$$=tree1(PAREN, $2);}
+ t = tree1(PAREN, words(yylex(), &tok));
+ if(tok != ')')
+ syntax(tok);
+ *ptok = yylex();
+ return t;
+
+ case REDIRW:
+ // | REDIRW brace {$$=mung1($1, $2); $$->type=PIPEFD;}
+ t = yylval.tree;
+ t = mung1(t, brace(yylex()));
+ t->type = PIPEFD;
+ *ptok = yylex();
+ return t;
+ }
+}
+
+static int
+iswordtok(int tok)
+{
+ switch(tok) {
+ case FOR:
+ case IN:
+ case WHILE:
+ case IF:
+ case NOT:
+ case TWIDDLE:
+ case BANG:
+ case SUBSHELL:
+ case SWITCH:
+ case FN:
+ case '$':
+ case '"':
+ case COUNT:
+ case WORD:
+ case '`':
+ case '(':
+ case REDIRW:
+ case '=':
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/cmd/rc/pcmd.c b/src/cmd/rc/pcmd.c
index 8caf60a2..cae84737 100644
--- a/src/cmd/rc/pcmd.c
+++ b/src/cmd/rc/pcmd.c
@@ -145,3 +145,121 @@ pcmd(io *f, tree *t)
break;
}
}
+
+void
+pcmdu(io *f, tree *t) /* unambiguous */
+{
+ if(t==0) {
+ pfmt(f, "<nil>");
+ return;
+ }
+
+ switch(t->type){
+ default: pfmt(f, "(bad %d %p %p %p)", t->type, c0, c1, c2);
+ break;
+ case '$': pfmt(f, "($ %u)", c0);
+ break;
+ case '"': pfmt(f, "($\" %u)", c0);
+ break;
+ case '&': pfmt(f, "(& %u)", c0);
+ break;
+ case '^': pfmt(f, "(^ %u %u)", c0, c1);
+ break;
+ case '`': pfmt(f, "(` %u)", c0);
+ break;
+ case ANDAND: pfmt(f, "(&& %u %u)", c0, c1);
+ break;
+ case BANG: pfmt(f, "(! %u)", c0);
+ break;
+ case BRACE: pfmt(f, "(brace %u)", c0);
+ break;
+ case COUNT: pfmt(f, "($# %u)", c0);
+ break;
+ case FN: pfmt(f, "(fn %u %u)", c0, c1);
+ break;
+ case IF: pfmt(f, "(if %u %u)", c0, c1);
+ break;
+ case NOT: pfmt(f, "(if not %u)", c0);
+ break;
+ case OROR: pfmt(f, "(|| %u %u)", c0, c1);
+ break;
+ case PCMD:
+ case PAREN: pfmt(f, "(paren %u)", c0);
+ break;
+ case SUB: pfmt(f, "($sub %u %u)", c0, c1);
+ break;
+ case SIMPLE: pfmt(f, "(simple %u)", c0);
+ break;
+ case SUBSHELL: pfmt(f, "(@ %u)", c0);
+ break;
+ case SWITCH: pfmt(f, "(switch %u %u)", c0, c1);
+ break;
+ case TWIDDLE: pfmt(f, "(~ %u %u)", c0, c1);
+ break;
+ case WHILE: pfmt(f, "(while %u %u)", c0, c1);
+ break;
+ case ARGLIST:
+ pfmt(f, "(arglist %u %u)", c0, c1);
+ break;
+ case ';':
+ pfmt(f, "(; %u %u)", c0, c1);
+ break;
+ case WORDS:
+ pfmt(f, "(words %u %u)", c0, c1);
+ break;
+ case FOR:
+ pfmt(f, "(for %u %u %u)", c0, c1, 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, " %u)", c1);
+ break;
+ case PIPEFD:
+ case REDIR:
+ pfmt(f, "(");
+ 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;
+ }
+ if(t->rtype == HERE)
+ pfmt(f, "HERE %u)", c1);
+ else
+ pfmt(f, "%u %u)", c0, c1);
+ break;
+ case '=':
+ pfmt(f, "(%u=%u %u)", c0, c1, c2);
+ break;
+ case PIPE:
+ pfmt(f, "(|");
+ if(t->fd1==0){
+ if(t->fd0!=1)
+ pfmt(f, "[%d]", t->fd0);
+ }
+ else pfmt(f, "[%d=%d]", t->fd0, t->fd1);
+ pfmt(f, " %u %u", c0, c1);
+ break;
+ }
+}
diff --git a/src/cmd/rc/simple.c b/src/cmd/rc/simple.c
index d587227a..a7d78f6f 100644
--- a/src/cmd/rc/simple.c
+++ b/src/cmd/rc/simple.c
@@ -322,6 +322,7 @@ execdot(void)
static int first = 1;
char file[512];
word *path;
+
if(first){
dotcmds[0].i = 1;
dotcmds[1].f = Xmark;
diff --git a/src/cmd/rc/syn.y b/src/cmd/rc/syn.y
index c7de3531..5c98ef80 100644
--- a/src/cmd/rc/syn.y
+++ b/src/cmd/rc/syn.y
@@ -1,5 +1,5 @@
%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN
-%term WORD REDIR DUP PIPE SUB
+%term WORD REDIR REDIRW 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
@@ -19,7 +19,7 @@
%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
+%type<tree> WORD REDIR REDIRW DUP PIPE
%%
rc: { return 1;}
| line '\n' {return !compile($1);}
@@ -45,7 +45,7 @@ cmd: {$$=0;}
| 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
+ * 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
@@ -73,7 +73,7 @@ cmd: {$$=0;}
simple: first
| simple word {$$=tree2(ARGLIST, $1, $2);}
| simple redir {$$=tree2(ARGLIST, $1, $2);}
-first: comword
+first: comword
| first '^' word {$$=tree2('^', $1, $3);}
word: keyword {lastword=1; $1->type=WORD;}
| comword
@@ -85,7 +85,7 @@ comword: '$' word {$$=tree1('$', $2);}
| WORD
| '`' brace {$$=tree1('`', $2);}
| '(' words ')' {$$=tree1(PAREN, $2);}
-| REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;}
+| REDIRW 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/src/cmd/rc/test.rc b/src/cmd/rc/test.rc
new file mode 100644
index 00000000..4a33d87c
--- /dev/null
+++ b/src/cmd/rc/test.rc
@@ -0,0 +1,93 @@
+# test for parser
+
+a
+a b
+a|b
+a | b
+{a; b; c}
+x=y a && b || c
+x=y a | b | c
+x=y for(i) a | b
+>x for(i) a | b
+>x a || b && c
+a >x || b && c
+a | for(i) b | c
+fn x {y; z} | b && c
+if (x) y
+if not z
+`{} >x >[1=2]y >[3=] z <w
+~ x y && z
+x | y && z
+x | y || z
+! x | y | z
+@ x | y | z
+x | ! y | z
+x | @y | z
+! x | if(y) z
+@ x | if(y) z
+x=1 y | if(z) w
+a'b' c
+a^'b'^ c
+a^'b' c
+$$x
+$x($y)
+$"x
+$#x
+$#$x
+-$x-
+`{a;b;c}
+<{a;b}
+x for in while if not ~ ! @ switch fn
+x not$y
+a;b;c
+if(x)
+y
+if(x)
+{
+y
+}
+if not
+z
+for(x)
+y
+for(x in y)
+z
+while(x)
+y
+# yacc doesn't accept a newline before the brace
+# even though the rule is written as if it would
+switch x {
+}
+switch (x) {
+}
+z
+x &&
+y
+x ||
+y
+x |
+y
+switch x {y} && z
+switch x {} | y
+
+OPTIONS=$OPTIONS' /axescount '^`{echo $1 | sed s/-a//}^' def'
+
+# bug in old printfont script - expected more free carats
+# OPTIONS=$OPTIONS' /axescount '`{echo $1 | sed s/-a//}' def'
+
+(x) = y
+x=y
+x = y
+
+# works now!
+# x y=z
+# x =y
+# x -flag=y
+
+>z x | y
+
+# rejected now, was like parens were spaces before.
+# echo Formatting Venti arenas and indices (this takes a while).
+
+
+# echo $STATLINE(1)^$STATLINE(3)' '$STATLINE(2)' '$STATLINE(4)' '$LSLINE(6)