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
next 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