shithub: rc

Download patch

ref: 11d6d33e28309ee553d629c8964d387039bb5524
parent: 67c0977e162f7addd6a3a260205675bd7f949015
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Jan 7 15:53:05 EST 2022

rc: read heredoc when receiving '\n' (thanks Eckard Brauer)

Eckard's test case:

cat <<! | cat
asdf
!

The issue is that we have to continue parsing until we see
the '\n' before consuming the here document.

So we revert to the old approach of having two functions:

heredoc() which remembers if we'v seen a heredoc redirection
and a second readhere() function that reads the doc from
the lexers input and sets Tree.str on thee REDIR node.

--- a/fns.h
+++ b/fns.h
@@ -58,7 +58,8 @@
 void	pushlist(void);
 void	pushredir(int, int, int);
 word*	pushword(char*);
-char*	readhere(tree*, io*);
+void	readhere(io*);
+void	heredoc(tree*);
 void	setstatus(char*);
 void	skipnl(void);
 void	start(code*, int, var*, redir*);
--- a/here.c
+++ b/here.c
@@ -6,16 +6,12 @@
 void psubst(io*, unsigned char*);
 void pstrs(io*, word*);
 
-char*
-readhere(tree *tag, io *in)
+static char*
+readhere1(tree *tag, io *in)
 {
 	io *out;
 	char c, *m;
 
-	if(tag->type!=WORD){
-		yyerror("Bad here tag");
-		return 0;
-	}
 	pprompt();
 	out = openiostr();
 	m = tag->str;
@@ -45,6 +41,34 @@
 	}
 	doprompt = 1;
 	return closeiostr(out);
+}
+
+static tree *head, *tail;
+
+void
+heredoc(tree *redir)
+{
+	if(redir->child[0]->type!=WORD){
+		yyerror("Bad here tag");
+		return;
+	}
+	redir->child[2]=0;
+	if(head)
+		tail->child[2]=redir;
+	else
+		head=redir;
+	tail=redir;
+}
+
+void
+readhere(io *in)
+{
+	while(head){
+		tail=head->child[2];
+		head->child[2]=0;
+		head->str=readhere1(head->child[0], in);
+		head=tail;
+	}
 }
 
 void
--- a/syn.y
+++ b/syn.y
@@ -22,7 +22,7 @@
 %type<tree> WORD REDIR DUP PIPE
 %%
 rc:				{ return 1;}
-|	line '\n'		{return !compile($1);}
+|	line '\n'		{readhere(lex->input); return !compile($1);}
 line:	cmd
 |	cmdsa line		{$$=tree2(';', $1, $2);}
 body:	cmd
@@ -30,13 +30,13 @@
 cmdsa:	cmd ';'
 |	cmd '&'			{$$=tree1('&', $1);}
 cmdsan:	cmdsa
-|	cmd '\n'
+|	cmd '\n'		{readhere(lex->input);}
 brace:	'{' body '}'		{$$=tree1(BRACE, $2);}
 paren:	'(' body ')'		{$$=tree1(PCMD, $2);}
 assign:	first '=' word		{$$=tree2('=', $1, $3);}
 epilog:				{$$=0;}
 |	redir epilog		{$$=mung2($1, $1->child[0], $2);}
-redir:	REDIR word		{($$=mung1($1, $2))->str=$1->rtype==HERE?readhere($2,lex->input):0;}
+redir:	REDIR word		{$$=mung1($1, $2); if($$->rtype==HERE) heredoc($$);}
 |	DUP
 cmd:				{$$=0;}
 |	brace epilog		{$$=epimung($1, $2);}