diff options
author | jilles <jilles@FreeBSD.org> | 2010-05-30 14:11:27 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2010-05-30 14:11:27 +0000 |
commit | 930ce3922652c50fc8b621b14b6238b325d7f16f (patch) | |
tree | c79ef6f99cbe768f9553a0f30a5be30bc08fa88d | |
parent | dde904bba43789f8e973ff4a527dcd59b5d4d0f1 (diff) | |
download | FreeBSD-src-930ce3922652c50fc8b621b14b6238b325d7f16f.zip FreeBSD-src-930ce3922652c50fc8b621b14b6238b325d7f16f.tar.gz |
sh: Change interaction of command substitution and here documents.
If a command substitution contains a newline token, this no longer starts
here documents of outer commands. This way, we follow POSIX's idea of the
command substitution being a separate script more closely. It also matches
other shells better and is consistent with newline characters in quotes not
starting here documents.
The extension tested in parser/heredoc3.0 ($(cat <<EOF)\ntext\nEOF\n)
continues to be supported.
In particular, this change allows things like
cat <<EOF && echo `pwd`
(a `` command substitution after a here document)
which formerly silently used an empty file as the here document, because the
EOF of the inner command "pwd" also forced an empty here document.
-rw-r--r-- | bin/sh/parser.c | 17 | ||||
-rw-r--r-- | tools/regression/bin/sh/parser/heredoc4.0 | 44 | ||||
-rw-r--r-- | tools/regression/bin/sh/parser/heredoc5.0 | 56 |
3 files changed, 113 insertions, 4 deletions
diff --git a/bin/sh/parser.c b/bin/sh/parser.c index 29ccd83..c9305b3 100644 --- a/bin/sh/parser.c +++ b/bin/sh/parser.c @@ -973,6 +973,8 @@ parsebackq(char *out, struct nodelist **pbqlist, const int bq_startlinno = plinno; char *volatile ostr = NULL; struct parsefile *const savetopfile = getcurrentfile(); + struct heredoc *const saveheredoclist = heredoclist; + struct heredoc *here; str = NULL; if (setjmp(jmploc.loc)) { @@ -981,6 +983,7 @@ parsebackq(char *out, struct nodelist **pbqlist, ckfree(str); if (ostr) ckfree(ostr); + heredoclist = saveheredoclist; handler = savehandler; if (exception == EXERROR) { startlinno = bq_startlinno; @@ -995,6 +998,7 @@ parsebackq(char *out, struct nodelist **pbqlist, memcpy(str, stackblock(), savelen); } handler = &jmploc; + heredoclist = NULL; INTON; if (oldstyle) { /* We must read until the closing backquote, giving special @@ -1091,21 +1095,26 @@ done: while (stackblocksize() <= savelen) growstackblock(); STARTSTACKSTR(out); + INTOFF; if (str) { memcpy(out, str, savelen); STADJUST(savelen, out); - INTOFF; ckfree(str); str = NULL; - INTON; } if (ostr) { - INTOFF; ckfree(ostr); ostr = NULL; - INTON; + } + here = saveheredoclist; + if (here != NULL) { + while (here->next != NULL) + here = here->next; + here->next = heredoclist; + heredoclist = saveheredoclist; } handler = savehandler; + INTON; if (quoted) USTPUTC(CTLBACKQ | CTLQUOTE, out); else diff --git a/tools/regression/bin/sh/parser/heredoc4.0 b/tools/regression/bin/sh/parser/heredoc4.0 new file mode 100644 index 0000000..fa3af5f --- /dev/null +++ b/tools/regression/bin/sh/parser/heredoc4.0 @@ -0,0 +1,44 @@ +# $FreeBSD$ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +f() { + cat <<EOF && echo `echo bar` +foo +EOF +} +check '"`f`" = "foo +bar"' + +f() { + cat <<EOF && echo $(echo bar) +foo +EOF +} +check '"$(f)" = "foo +bar"' + +f() { + echo `echo bar` && cat <<EOF +foo +EOF +} +check '"`f`" = "bar +foo"' + +f() { + echo $(echo bar) && cat <<EOF +foo +EOF +} +check '"$(f)" = "bar +foo"' + +exit $((failures != 0)) diff --git a/tools/regression/bin/sh/parser/heredoc5.0 b/tools/regression/bin/sh/parser/heredoc5.0 new file mode 100644 index 0000000..84b0eb2 --- /dev/null +++ b/tools/regression/bin/sh/parser/heredoc5.0 @@ -0,0 +1,56 @@ +# $FreeBSD$ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +f() { + cat <<EOF && echo `cat <<EOF +bar +EOF +` +foo +EOF +} +check '"`f`" = "foo +bar"' + +f() { + cat <<EOF && echo $(cat <<EOF +bar +EOF +) +foo +EOF +} +check '"$(f)" = "foo +bar"' + +f() { + echo `cat <<EOF +bar +EOF +` && cat <<EOF +foo +EOF +} +check '"`f`" = "bar +foo"' + +f() { + echo $(cat <<EOF +bar +EOF +) && cat <<EOF +foo +EOF +} +check '"$(f)" = "bar +foo"' + +exit $((failures != 0)) |