9front - general discussion about 9front
 help / color / mirror / Atom feed
* new rc parser: do we want it?
@ 2020-10-07  3:15 ori
  2020-10-07  3:54 ` [9front] " sl
  2020-10-07 19:02 ` Anthony Martin
  0 siblings, 2 replies; 13+ messages in thread
From: ori @ 2020-10-07  3:15 UTC (permalink / raw)
  To: 9front

So, I sat down today and got rsc's new rc parser
working in 9front. There were a couple of small
tweaks needed to make our `sep{...} syntax work,
but other than that, it was mostly uneventful.

The new parser gives us 2 nice things:

	echo `{echo foo}.c

has the same free caret behavior as:

	echo $foo.c

and unquoted '=' is allowed after the
first word:

	echo a=b

But mostly, it prevents us from diverging from
plan9port.

Do we want this?

If yes, can I get some people to test it out
with their scripts, and people with a longer
memory than I to make sure that I accounted
for all of our changes.

Patch below:

diff -r f7dfc146779d sys/src/cmd/rc/code.c
--- a/sys/src/cmd/rc/code.c	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/code.c	Tue Oct 06 20:14:21 2020 -0700
@@ -37,6 +37,16 @@
 int
 compile(tree *t)
 {
+	if(flag['D']) {
+		struct io *s;
+		s = openstr();
+		pfmt(s, "compile: %u\n", t);
+		write(2, s->strp, strlen((char*)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 -r f7dfc146779d sys/src/cmd/rc/exec.c
--- a/sys/src/cmd/rc/exec.c	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/exec.c	Tue Oct 06 20:14:21 2020 -0700
@@ -164,7 +164,7 @@
 	code bootstrap[17];
 	char num[12], *rcmain;
 	int i;
-	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]=='-')
@@ -927,7 +927,7 @@
 			promptstr="% ";
 	}
 	Noerror();
-	if(yyparse()){
+	if((flag['Y'] ? yyparse : parse)()){
 		if(!p->iflag || p->eof && !Eintr()){
 			if(p->cmdfile)
 				free(p->cmdfile);
diff -r f7dfc146779d sys/src/cmd/rc/fns.h
--- a/sys/src/cmd/rc/fns.h	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/fns.h	Tue Oct 06 20:14:21 2020 -0700
@@ -64,3 +64,4 @@
 void	yyerror(char*);
 int	yylex(void);
 int	yyparse(void);
+int	parse(void);
diff -r f7dfc146779d sys/src/cmd/rc/havefork.c
--- a/sys/src/cmd/rc/havefork.c	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/havefork.c	Tue Oct 06 20:14:21 2020 -0700
@@ -212,7 +212,9 @@
 		break;
 	default:
 		addwaitpid(pid);
-		Waitfor(pid, 1);
+		/* interrupts don't get us out */
+		while(Waitfor(pid, 1) < 0)
+			;
 		runq->pc = runq->code[runq->pc].i;
 		break;
 	}
diff -r f7dfc146779d sys/src/cmd/rc/io.c
--- a/sys/src/cmd/rc/io.c	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/io.c	Tue Oct 06 20:14:21 2020 -0700
@@ -48,7 +48,10 @@
 			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 -r f7dfc146779d sys/src/cmd/rc/io.h
--- a/sys/src/cmd/rc/io.h	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/io.h	Tue Oct 06 20:14:21 2020 -0700
@@ -22,6 +22,7 @@
 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 -r f7dfc146779d sys/src/cmd/rc/lex.c
--- a/sys/src/cmd/rc/lex.c	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/lex.c	Tue Oct 06 20:14:21 2020 -0700
@@ -112,15 +112,17 @@
 	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) {
@@ -130,9 +132,12 @@
 				advance();
 			}
 		}
-		if(c==' ' || c=='\t')
+		if(c==' ' || c=='\t') {
+			skipped = 1;
 			advance();
-		else return;
+		}
+		else
+			return skipped;
 	}
 }
 
@@ -203,7 +208,7 @@
 {
 	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
@@ -212,11 +217,11 @@
 	 * 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=='"'){
@@ -225,7 +230,8 @@
 		}
 	}
 	inquote = 0;
-	skipwhite();
+	if(skipwhite() && !flag['Y'])
+		return ' ';
 	switch(c = advance()){
 	case EOF:
 		lastdol = 0;
@@ -246,7 +252,8 @@
 	case '&':
 		lastdol = 0;
 		if(nextis('&')){
-			skipnl();
+			if(flag['Y'])
+				skipnl();
 			strcpy(tok, "&&");
 			return ANDAND;
 		}
@@ -255,7 +262,8 @@
 	case '|':
 		lastdol = 0;
 		if(nextis(c)){
-			skipnl();
+			if(flag['Y'])
+				skipnl();
 			strcpy(tok, "||");
 			return OROR;
 		}
@@ -344,8 +352,13 @@
 		}
 		*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 -r f7dfc146779d sys/src/cmd/rc/mkfile
--- a/sys/src/cmd/rc/mkfile	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/mkfile	Tue Oct 06 20:14:21 2020 -0700
@@ -9,6 +9,7 @@
 	here.$O\
 	io.$O\
 	lex.$O\
+	parse.$O\
 	pcmd.$O\
 	pfnc.$O\
 	simple.$O\
diff -r f7dfc146779d sys/src/cmd/rc/parse.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/cmd/rc/parse.c	Tue Oct 06 20:14:21 2020 -0700
@@ -0,0 +1,555 @@
+#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);
+
+	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('`', nil, $2);}
+		// |	'`' word brace		{$$=tree1('`', $2, $3);}
+		w = nil;
+		tok = yylex();
+		if(tok != '{')
+			w = yyword(tok, &tok, 1);
+		t = tree2('`', w, brace(tok));
+		*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 -r f7dfc146779d sys/src/cmd/rc/pcmd.c
--- a/sys/src/cmd/rc/pcmd.c	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/pcmd.c	Tue Oct 06 20:14:21 2020 -0700
@@ -32,7 +32,7 @@
 	break;
 	case '^':	pfmt(f, "%t^%t", c0, c1);
 	break;
-	case '`':	pfmt(f, "`%t%t", c0, c1);
+	case '`':	pfmt(f, "`%t%t%t", c0, c1, c2);
 	break;
 	case ANDAND:	pfmt(f, "%t && %t", c0, c1);
 	break;
@@ -146,3 +146,121 @@
 		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 %u)", c0, c1);
+	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 -r f7dfc146779d sys/src/cmd/rc/syn.y
--- a/sys/src/cmd/rc/syn.y	Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/syn.y	Tue Oct 06 20:14:21 2020 -0700
@@ -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 @@
 |	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 @@
 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
@@ -86,7 +86,7 @@
 |	'`' brace		{$$=tree2('`', (struct tree*)0, $2);}
 |	'`' word brace		{$$=tree2('`', $2, $3);}
 |	'(' 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);}



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
  2020-10-07  3:15 new rc parser: do we want it? ori
@ 2020-10-07  3:54 ` sl
  2020-10-07 19:02 ` Anthony Martin
  1 sibling, 0 replies; 13+ messages in thread
From: sl @ 2020-10-07  3:54 UTC (permalink / raw)
  To: 9front

> and unquoted '=' is allowed after the
> first word:
>
> echo a=b

fwiw, when aiju suggested this a couple years ago i made a
big stink about how this breaks the predictable quoting
rules, but if rsc is dispensing with regularity, i don't
know what to say anymore.  i suppose it's just one more thing to
remember.

sl


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: new rc parser: do we want it?
  2020-10-07  3:15 new rc parser: do we want it? ori
  2020-10-07  3:54 ` [9front] " sl
@ 2020-10-07 19:02 ` Anthony Martin
  1 sibling, 0 replies; 13+ messages in thread
From: Anthony Martin @ 2020-10-07 19:02 UTC (permalink / raw)
  To: 9front

ori@eigenstate.org once said:
> But mostly, it prevents us from diverging from
> plan9port.
>
> Do we want this?

Yes. I've been using it locally ever since it went into plan9port.
It's a small improvement and I think it's important that rc(1) is
essentially the same everywhere.

Cheers,
  Anthony


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
  2020-10-15 23:14         ` Roman Shaposhnik
@ 2020-10-15 23:32           ` Stanley Lieber
  0 siblings, 0 replies; 13+ messages in thread
From: Stanley Lieber @ 2020-10-15 23:32 UTC (permalink / raw)
  To: 9front

On October 15, 2020 7:14:50 PM EDT, Roman Shaposhnik <roman@shaposhnik.org> wrote:
>On Thu, Oct 15, 2020 at 10:23 AM Anthony Martin <ality@pbrane.org>
>wrote:
>>
>> Stanley Lieber <sl@stanleylieber.com> once said:
>> > rc's v10 online man page refers to it as "the plan 9 shell." my
>print
>> > edition is in storage, but i don't think it mentioned plan 9. the
>> > online version may represent a later extraction from the labs'
>running
>> > system. plan 9 was percolating at least as early as 1988. it's
>unclear
>> > exactly when rc was first written, but based on the timeline of
>> > releases it first shipped in v10.
>>
>> The v10 tree on TUHS has /vol2/rc/rc.ms¹ but it's dated 1993-06-19.
>> The title from that version is "Rc — A shell for Plan 9 and UNIX".
>>
>> I don't have a copy of the printed v10 manual to check what it
>> was in 1989/1990. I'd be curious to see what yours says.
>
>My printed Second Edition manual says:
>
>NAME
>   rc, cd, eval, exec... - command language
>SYNOPSIS
>   rc ...
>DESCRIPTION
>   Rc is the Plan 9 shell.
>
>Thanks,
>Roman.
>
>Thanks,
>Roman.

everyone:

i screwed up and cc'd rob on my post (thought better of it and forwarded him the message, but forgot to remove the cc -- this mailing list silently drops messages from addresses that are not suscribed). please don't cc him on every single reply.

thanks,

sl


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
  2020-10-15 17:22       ` Anthony Martin
@ 2020-10-15 23:14         ` Roman Shaposhnik
  2020-10-15 23:32           ` Stanley Lieber
  0 siblings, 1 reply; 13+ messages in thread
From: Roman Shaposhnik @ 2020-10-15 23:14 UTC (permalink / raw)
  To: 9front; +Cc: r

On Thu, Oct 15, 2020 at 10:23 AM Anthony Martin <ality@pbrane.org> wrote:
>
> Stanley Lieber <sl@stanleylieber.com> once said:
> > rc's v10 online man page refers to it as "the plan 9 shell." my print
> > edition is in storage, but i don't think it mentioned plan 9. the
> > online version may represent a later extraction from the labs' running
> > system. plan 9 was percolating at least as early as 1988. it's unclear
> > exactly when rc was first written, but based on the timeline of
> > releases it first shipped in v10.
>
> The v10 tree on TUHS has /vol2/rc/rc.ms¹ but it's dated 1993-06-19.
> The title from that version is "Rc — A shell for Plan 9 and UNIX".
>
> I don't have a copy of the printed v10 manual to check what it
> was in 1989/1990. I'd be curious to see what yours says.

My printed Second Edition manual says:

NAME
   rc, cd, eval, exec... - command language
SYNOPSIS
   rc ...
DESCRIPTION
   Rc is the Plan 9 shell.

Thanks,
Roman.

Thanks,
Roman.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
  2020-10-15 12:11     ` Stanley Lieber
  2020-10-15 16:41       ` Romano
@ 2020-10-15 17:22       ` Anthony Martin
  2020-10-15 23:14         ` Roman Shaposhnik
  1 sibling, 1 reply; 13+ messages in thread
From: Anthony Martin @ 2020-10-15 17:22 UTC (permalink / raw)
  To: 9front; +Cc: r

Stanley Lieber <sl@stanleylieber.com> once said:
> rc's v10 online man page refers to it as "the plan 9 shell." my print
> edition is in storage, but i don't think it mentioned plan 9. the
> online version may represent a later extraction from the labs' running
> system. plan 9 was percolating at least as early as 1988. it's unclear
> exactly when rc was first written, but based on the timeline of
> releases it first shipped in v10.

The v10 tree on TUHS has /vol2/rc/rc.ms¹ but it's dated 1993-06-19.
The title from that version is "Rc — A shell for Plan 9 and UNIX".

I don't have a copy of the printed v10 manual to check what it
was in 1989/1990. I'd be curious to see what yours says.

  Anthony

1. https://www.tuhs.org/cgi-bin/utree.pl?file=V10/vol2/rc/rc.ms


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
  2020-10-15 12:11     ` Stanley Lieber
@ 2020-10-15 16:41       ` Romano
  2020-10-15 17:22       ` Anthony Martin
  1 sibling, 0 replies; 13+ messages in thread
From: Romano @ 2020-10-15 16:41 UTC (permalink / raw)
  To: 9front; +Cc: r

I probably didn't give enough context.

TUPE was referring to the Bourne shell, at least according to to page 100. So the comment is not referring to rc directly.

I brought it up because of what Duffy wrote in "Rc -- The Plan 9 Shell" under Design Principles (section 28), which discusses Rc's heavy reliance on Bourne's /bin/sh. I didn't see Duffy explicitly touch on why assignments anywhere in the command line were restricted, and so considered the parenthetical comment I found re: Bourne's shell to be the reason.

On October 15, 2020 12:11:10 PM UTC, Stanley Lieber <sl@stanleylieber.com> wrote:
>On October 15, 2020 6:11:13 AM EDT, Ethan Gardener
><eekee57@fastmail.fm> wrote:
>>On Mon, Oct 12, 2020, at 9:44 PM, Romano wrote:
>>> There is a parenthetical comment on page 90 of "The UNIX Programming
>
>>> Environment":
>>> "(Originally, assignments anywhere in the command line were passed
>to
>>
>>> the command, but this interfered with dd(1).)"
>>
>>So it was regular, initially. By the time I started to use Plan 9 &
>P9P
>>(2009 or so), it was broken. Rc was making errors of assignments after
>>the command word *and* dd's syntax was different. But Rc wouldn't have
>>been in TUPE, would it? I admit I never got around to reading it.
>
>if i understand correctly:
>
>tupe came out in 1984, and refers to rob's rewritten v8 sh[0].
>
>rc[1] first appeared in v10, and must have been developed around 1988
>or 1989.
>
>rc's v10 online man page refers to it as "the plan 9 shell." my print
>edition is in storage, but i don't think it mentioned plan 9. the
>online version may represent a later extraction from the labs' running
>system. plan 9 was percolating at least as early as 1988. it's unclear
>exactly when rc was first written, but based on the timeline of
>releases it first shipped in v10.
>
>sl
>
>[0] http://man.cat-v.org/unix_8th/1/sh
>[1] http://man.cat-v.org/unix_10th/1/rc


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
  2020-10-15 10:11   ` Ethan Gardener
@ 2020-10-15 12:11     ` Stanley Lieber
  2020-10-15 16:41       ` Romano
  2020-10-15 17:22       ` Anthony Martin
  0 siblings, 2 replies; 13+ messages in thread
From: Stanley Lieber @ 2020-10-15 12:11 UTC (permalink / raw)
  To: 9front; +Cc: r

On October 15, 2020 6:11:13 AM EDT, Ethan Gardener <eekee57@fastmail.fm> wrote:
>On Mon, Oct 12, 2020, at 9:44 PM, Romano wrote:
>> There is a parenthetical comment on page 90 of "The UNIX Programming 
>> Environment":
>> "(Originally, assignments anywhere in the command line were passed to
>
>> the command, but this interfered with dd(1).)"
>
>So it was regular, initially. By the time I started to use Plan 9 & P9P
>(2009 or so), it was broken. Rc was making errors of assignments after
>the command word *and* dd's syntax was different. But Rc wouldn't have
>been in TUPE, would it? I admit I never got around to reading it.

if i understand correctly:

tupe came out in 1984, and refers to rob's rewritten v8 sh[0].

rc[1] first appeared in v10, and must have been developed around 1988 or 1989.

rc's v10 online man page refers to it as "the plan 9 shell." my print edition is in storage, but i don't think it mentioned plan 9. the online version may represent a later extraction from the labs' running system. plan 9 was percolating at least as early as 1988. it's unclear exactly when rc was first written, but based on the timeline of releases it first shipped in v10.

sl

[0] http://man.cat-v.org/unix_8th/1/sh
[1] http://man.cat-v.org/unix_10th/1/rc



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
  2020-10-12 20:44 ` Romano
@ 2020-10-15 10:11   ` Ethan Gardener
  2020-10-15 12:11     ` Stanley Lieber
  0 siblings, 1 reply; 13+ messages in thread
From: Ethan Gardener @ 2020-10-15 10:11 UTC (permalink / raw)
  To: 9front

On Mon, Oct 12, 2020, at 9:44 PM, Romano wrote:
> There is a parenthetical comment on page 90 of "The UNIX Programming 
> Environment":
> "(Originally, assignments anywhere in the command line were passed to 
> the command, but this interfered with dd(1).)"

So it was regular, initially. By the time I started to use Plan 9 & P9P (2009 or so), it was broken. Rc was making errors of assignments after the command word *and* dd's syntax was different. But Rc wouldn't have been in TUPE, would it? I admit I never got around to reading it.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
       [not found] <5EF01FE4B564E504DD635D7E391EE0C8@ewsd.inri.net>
@ 2020-10-12 20:44 ` Romano
  2020-10-15 10:11   ` Ethan Gardener
  0 siblings, 1 reply; 13+ messages in thread
From: Romano @ 2020-10-12 20:44 UTC (permalink / raw)
  To: 9front

There is a parenthetical comment on page 90 of "The UNIX Programming Environment":
"(Originally, assignments anywhere in the command line were passed to the command, but this interfered with dd(1).)"


On October 7, 2020 3:54:20 AM UTC, sl@stanleylieber.com wrote:
>> and unquoted '=' is allowed after the
>> first word:
>>
>> echo a=b
>
>fwiw, when aiju suggested this a couple years ago i made a
>big stink about how this breaks the predictable quoting
>rules, but if rsc is dispensing with regularity, i don't
>know what to say anymore.  i suppose it's just one more thing to
>remember.
>
>sl


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
  2020-10-07  4:46 ` [9front] " ori
@ 2020-10-07 13:06   ` kvik
  0 siblings, 0 replies; 13+ messages in thread
From: kvik @ 2020-10-07 13:06 UTC (permalink / raw)
  To: 9front

> It matters more in plan9port

ssh(1) and os(1) arc the matters into 9front as well.

For me this is a great quality of life improvement, totally
worth the supposed break of predictability.

I've applied the patch and tested it on some of my more
horrible scripts.  Seems to work fine.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
       [not found] <1975289B8C108B2FCF360524D16AA25B@ewsd.inri.net>
@ 2020-10-07 10:00 ` Ethan Gardener
  0 siblings, 0 replies; 13+ messages in thread
From: Ethan Gardener @ 2020-10-07 10:00 UTC (permalink / raw)
  To: 9front

On Wed, Oct 7, 2020, at 4:54 AM, sl@stanleylieber.com wrote:
> > and unquoted '=' is allowed after the
> > first word:
> >
> > echo a=b
> 
> fwiw, when aiju suggested this a couple years ago i made a
> big stink about how this breaks the predictable quoting
> rules, but if rsc is dispensing with regularity, i don't
> know what to say anymore.  i suppose it's just one more thing to
> remember.

"this breaks the predictable quoting rules" has me a little concerned, but i'm not sure how it does. '=' behaves differently on either side of the first non-assignment word anyway. in the way i look at it, there's nothing to lose; it's already irregular. ;)

even if i'm wrong about that, i'd still be in favour of the change because the old way means urls typically break the convenience of 'send'. at one time, this was a big issue for me. still, my 9front use has dropped to nil, so my opinion's worth nothing unless anyone else feels the same. thinking about it just develops my forth ideas. i've got regular syntax, i can turn the rest of the line into a string and keep it on the stack to work with send (or even pull in the clipboard directly), but optional arguments are not so easy. ;) the fix is probably to break things down into even smaller parts.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] new rc parser: do we want it?
       [not found] <01DB736907F1AF8292BF2D9C25564677@ewsd.inri.net>
@ 2020-10-07  4:46 ` ori
  2020-10-07 13:06   ` kvik
  0 siblings, 1 reply; 13+ messages in thread
From: ori @ 2020-10-07  4:46 UTC (permalink / raw)
  To: sl, 9front

>> and unquoted '=' is allowed after the
>> first word:
>>
>> echo a=b
> 
> fwiw, when aiju suggested this a couple years ago i made a
> big stink about how this breaks the predictable quoting
> rules, but if rsc is dispensing with regularity, i don't
> know what to say anymore.  i suppose it's just one more thing to
> remember.
> 
> sl

I agree. I'm not sure how I feel about making '=' parsing
stateful. It matters more in plan9port, where a lot of
tools these days expect:

	--long-fucking-arg-name=thing

The free caret handling is slighty more interesting to me,
I've been tripped up by x`{y} getting a caret, but `{y}x
not getting one -- and it feels messy.

I also find the yacc grammar more readable than the C
rewrite.



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2020-10-15 23:32 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-07  3:15 new rc parser: do we want it? ori
2020-10-07  3:54 ` [9front] " sl
2020-10-07 19:02 ` Anthony Martin
     [not found] <01DB736907F1AF8292BF2D9C25564677@ewsd.inri.net>
2020-10-07  4:46 ` [9front] " ori
2020-10-07 13:06   ` kvik
     [not found] <1975289B8C108B2FCF360524D16AA25B@ewsd.inri.net>
2020-10-07 10:00 ` Ethan Gardener
     [not found] <5EF01FE4B564E504DD635D7E391EE0C8@ewsd.inri.net>
2020-10-12 20:44 ` Romano
2020-10-15 10:11   ` Ethan Gardener
2020-10-15 12:11     ` Stanley Lieber
2020-10-15 16:41       ` Romano
2020-10-15 17:22       ` Anthony Martin
2020-10-15 23:14         ` Roman Shaposhnik
2020-10-15 23:32           ` Stanley Lieber

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).