[PATCH] Handle line continuation in unquoted heredocs
Denys Vlasenko <[email protected]> Thu, 7 Aug 2025 22:10:10 +0200
| Newsgroups | org.kernel.vger.dash |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- 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