public inbox for dash@vger.kernel.org
 help / color / mirror / Atom feed
From: Denys Vlasenko <dvlasenk@redhat.com>
To: dash@vger.kernel.org, Herbert Xu <herbert.xu@redhat.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Subject: [PATCH] Handle line continuation in unquoted heredocs
Date: Thu,  7 Aug 2025 22:10:10 +0200	[thread overview]
Message-ID: <20250807201015.11358-1-dvlasenk@redhat.com> (raw)

Testcases:

cat <<nice
heredoc1
ni\
ce
nice # this isn't heredoc end, the end is above

cat <<-nice
	heredoc2
	\
	ni\
ce
nice # this isn't heredoc end, the end is above

cat <<"nice"
heredoc3
ni\
ce
nice

cat <<-"nice"
	heredoc4
	\
	ni\
ce
	nice

Correct output:

heredoc1
0
heredoc2
0
heredoc3
ni\
ce
heredoc4
\
ni\
ce

Before patch, cases 1 and 2 are mishandled.

A more simplistic apparent solution for cases 1 and 2,
s/pgetc/pgetc_eatbnl/, breaks cases 3 and 4

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
---
 src/parser.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/src/parser.c b/src/parser.c
index aecc18f..edef0ba 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -74,7 +74,7 @@
 
 
 /* Used by expandstr to get here-doc like behaviour. */
-#define FAKEEOFMARK (char *)1
+#define FAKEEOFMARK (struct heredoc *)1
 
 
 
@@ -123,7 +123,7 @@ STATIC void parseheredoc(void);
 STATIC int readtoken(void);
 STATIC int xxreadtoken(void);
 STATIC int pgetc_eatbnl();
-STATIC int readtoken1(int, char const *, char *, int);
+STATIC int readtoken1(int, char const *, struct heredoc *);
 STATIC void synexpect(int) __attribute__((__noreturn__));
 STATIC void synerror(const char *) __attribute__((__noreturn__));
 STATIC void setprompt(int);
@@ -143,7 +143,7 @@ int issimplecmd(union node *n, const char *name)
 	       equal(n->ncmd.args->narg.text, name);
 }
 
-static inline int realeofmark(const char *eofmark)
+static inline int realeofmark(struct heredoc *eofmark)
 {
 	return eofmark && eofmark != FAKEEOFMARK;
 }
@@ -685,9 +685,9 @@ parseheredoc(void)
 			setprompt(2);
 		}
 		if (here->here->type == NHERE)
-			readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs);
+			readtoken1(pgetc(), SQSYNTAX, here);
 		else
-			readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs);
+			readtoken1(pgetc_eatbnl(), DQSYNTAX, here);
 		n = (union node *)stalloc(sizeof (struct narg));
 		n->narg.type = NARG;
 		n->narg.next = NULL;
@@ -840,7 +840,7 @@ xxreadtoken(void)
 		case ')':
 			RETURN(TRP);
 		}
-		tok = readtoken1(c, BASESYNTAX, (char *)NULL, 0);
+		tok = readtoken1(c, BASESYNTAX, (struct heredoc *)NULL);
 		if (tok != TBLANK)
 			return tok;
 	}
@@ -994,7 +994,7 @@ static char *dollarsq_escape(char *out)
 #define	PARSEARITH()	{goto parsearith; parsearith_return:;}
 
 STATIC int
-readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs)
+readtoken1(int firstc, char const *syntax, struct heredoc *eofmark)
 {
 	struct synstack synbase = { .syntax = syntax };
 	int chkeofmark = checkkwd & CHKEOFMARK;
@@ -1233,17 +1233,24 @@ checkend: {
 		int markloc;
 		char *p;
 
-		if (striptabs) {
-			while (c == '\t')
-				c = pgetc();
+		if (eofmark->striptabs) {
+			while (c == '\t') {
+				if (eofmark->here->type == NHERE)
+					c = pgetc();
+				else /* NXHERE */
+					c = pgetc_eatbnl();
+			}
 		}
 
 		markloc = out - (char *)stackblock();
-		for (p = eofmark; STPUTC(c, out), *p; p++) {
+		for (p = eofmark->eofmark; STPUTC(c, out), *p; p++) {
 			if (c != *p)
 				goto more_heredoc;
 
-			c = pgetc();
+			if (eofmark->here->type == NHERE)
+				c = pgetc();
+			else /* NXHERE */
+				c = pgetc_eatbnl();
 		}
 
 		if (c == '\n' || c == PEOF) {
@@ -1748,7 +1755,7 @@ expandstr(const char *ps)
 		goto out;
 	handler = &jmploc;
 
-	readtoken1(pgetc_eatbnl(), DQSYNTAX, FAKEEOFMARK, 0);
+	readtoken1(pgetc_eatbnl(), DQSYNTAX, FAKEEOFMARK);
 
 	n.narg.type = NARG;
 	n.narg.next = NULL;
-- 
2.41.0


             reply	other threads:[~2025-08-07 20:10 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-07 20:10 Denys Vlasenko [this message]
2025-08-07 20:50 ` [PATCH] Handle line continuation in unquoted heredocs Harald van Dijk
2025-08-28  9:47 ` Herbert Xu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250807201015.11358-1-dvlasenk@redhat.com \
    --to=dvlasenk@redhat.com \
    --cc=dash@vger.kernel.org \
    --cc=herbert.xu@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox