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.
This commit is contained in:
Jilles Tjoelker 2010-05-30 14:11:27 +00:00
parent 7e0fed92d8
commit ba02a307fe
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=208655
3 changed files with 113 additions and 4 deletions

View File

@ -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 @@ parsebackq(char *out, struct nodelist **pbqlist,
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

View File

@ -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))

View File

@ -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))