* [PATCH] Handle line continuation in unquoted heredocs
@ 2025-08-07 20:10 Denys Vlasenko
2025-08-07 20:50 ` Harald van Dijk
2025-08-28 9:47 ` Herbert Xu
0 siblings, 2 replies; 3+ messages in thread
From: Denys Vlasenko @ 2025-08-07 20:10 UTC (permalink / raw)
To: dash, Herbert Xu; +Cc: Denys Vlasenko
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
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] Handle line continuation in unquoted heredocs
2025-08-07 20:10 [PATCH] Handle line continuation in unquoted heredocs Denys Vlasenko
@ 2025-08-07 20:50 ` Harald van Dijk
2025-08-28 9:47 ` Herbert Xu
1 sibling, 0 replies; 3+ messages in thread
From: Harald van Dijk @ 2025-08-07 20:50 UTC (permalink / raw)
To: Denys Vlasenko, dash, Herbert Xu
On 07/08/2025 21:10, Denys Vlasenko wrote:
> Testcases:
>
> cat <<nice
> heredoc1
> ni\
> ce
> nice # this isn't heredoc end, the end is above
Interesting. I am having trouble telling from the POSIX wording whether
this is meant to be valid. Is this the case that "It is unspecified
whether the line containing the trailing delimiter is itself subject to
this line continuation." is meant to apply to, or not?
> A more simplistic apparent solution for cases 1 and 2,
> s/pgetc/pgetc_eatbnl/, breaks cases 3 and 4
Would s/pgetc/pgetc_top(synstack)/ work instead?
Cheers,
Harald van Dijk
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] Handle line continuation in unquoted heredocs
2025-08-07 20:10 [PATCH] Handle line continuation in unquoted heredocs Denys Vlasenko
2025-08-07 20:50 ` Harald van Dijk
@ 2025-08-28 9:47 ` Herbert Xu
1 sibling, 0 replies; 3+ messages in thread
From: Herbert Xu @ 2025-08-28 9:47 UTC (permalink / raw)
To: Denys Vlasenko; +Cc: dash, herbert.xu, dvlasenk
Denys Vlasenko <dvlasenk@redhat.com> wrote:
> Testcases:
>
> cat <<nice
> heredoc1
> ni\
> ce
> nice # this isn't heredoc end, the end is above
I think this is debatable. The current behaviour differs from
bash, but matches ksh.
Since POSIX apparently left this as unspecified, I'm not changing
dash.
Thanks,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-08-28 9:47 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-07 20:10 [PATCH] Handle line continuation in unquoted heredocs Denys Vlasenko
2025-08-07 20:50 ` Harald van Dijk
2025-08-28 9:47 ` Herbert Xu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox