In particular, see http://austingroupbugs.net/view.php?id=221
and http://austingroupbugs.net/view.php?id=255.
* doc/autoconf.texi (Shell Substitutions) <${var-value}>: New
subsection.
<${var=literal}>: Tweak wording.  Add mention of an ambiguity
allowed by POSIX.
* tests/torture.at (Substitute and define special characters):
Make test more robust; here, the outer "" is in a here-doc, and
does not violate the quoting rules of thumb just documented.

Signed-off-by: Eric Blake <[email protected]>
---

I think this clears up some of the issues I ran into with
assignment quoting.  Any comments before I push it?

 ChangeLog         |   11 +++++
 doc/autoconf.texi |  130 +++++++++++++++++++++++++++++++++++++++++++++--------
 tests/torture.at  |    4 +-
 3 files changed, 124 insertions(+), 21 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index fa252b3..0978d88 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2010-08-25  Eric Blake  <[email protected]>

+       docs: mention another issue with variable expansion
+       In particular, see http://austingroupbugs.net/view.php?id=221
+       and http://austingroupbugs.net/view.php?id=255.
+       * doc/autoconf.texi (Shell Substitutions) <${var-value}>: New
+       subsection.
+       <${var=literal}>: Tweak wording.  Add mention of an ambiguity
+       allowed by POSIX.
+       * tests/torture.at (Substitute and define special characters):
+       Make test more robust; here, the outer "" is in a here-doc, and
+       does not violate the quoting rules of thumb just documented.
+
        m4sh: revert incorrect mix of "${a='b'}"
        * bin/autoconf.as: Revert leak of literal '' into assignment.
        * tests/tools.at (autom4te preselections): Likewise.
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index 28637c5..5fcc39e 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -15396,27 +15396,81 @@ Shell Substitutions
 colon for any shell substitution, and complain and die.
 Similarly for $...@{@var{var}:=...@var{value}@}, 
$...@{@var{var}:?...@var{value}@}, etc.

-...@item $...@{@var{va...@var{literal}@}
-...@cindex $...@{@var{va...@var{literal}@}
+...@item $...@{@var{va...@var{value}@}
+...@cindex $...@{@var{va...@var{value}@}
+When using @sam...@{@var{va...@var{value}@}} or
+...@samp{$@{...@var{var}-@var{val...@}} for providing alternate substitutions,
+...@var{value} must either be a single shell word or be quoted.  Solaris
+...@command{/bin/sh} complains otherwise.
+
+...@example
+$ @kbd{/bin/sh -c 'echo $...@{a-b c...@}'}
+/bin/sh: bad substitution
+$ @kbd{/bin/sh -c 'echo $...@{a-'\''b c'\''@}'}
+b c
+$ @kbd{/bin/sh -c 'echo "$...@{a-b c...@}"'}
+b c
+...@end example
+
+According to Posix, if an expansion occurs inside double quotes, then
+the use of unquoted double quotes within @var{value} is unspecified, and
+any single quotes become literal characters; in that case, escaping must
+be done with backslash.
+
+...@example
+$ @kbd{/bin/sh -c 'echo "$...@{a-"b  c"@}"'}
+/bin/sh: bad substitution
+$ @kbd{ksh -c 'echo "$...@{a-"b  c"@}"'}
+b c
+$ @kbd{bash -c 'echo "$...@{a-"b  c"@}"'}
+b  c
+$ @kbd{/bin/sh -c 'echo $...@{a-'\''b  c'\''@}'}
+b  c
+$ @kbd{/bin/sh -c 'echo "$...@{a-'\''b  c'\''@}"'}
+'b  c'
+$ @kbd{/bin/sh -c 'echo "$...@{a-\"b  c\"@}"'}
+"b  c"
+$ @kbd{/bin/sh -c 'echo "$...@{a-b  c...@}"'}
+b  c
+...@end example
+
+Perhaps the easiest way to work around quoting issues in a manner
+portable to all shells is to place the results in a temporary variable,
+then use @samp{$tmp} as the @var{value}, rather than trying to inline
+the expression needing quoting.
+
+...@example
+$ @kbd{/bin/sh -c 'tmp="a  b\"'\''@}\\"; echo "$...@{a-$tmp@}"'}
+b  c"'@}\
+$ @kbd{ksh -c 'tmp="a  b\"'\''@}\\"; echo "$...@{a-$tmp@}"'}
+b  c"'@}\
+$ @kbd{bash -c 'tmp="a  b\"'\''@}\\"; echo "$...@{a-$tmp@}"'}
+b  c"'@}\
+...@end example
+
+...@item $...@{@var{va...@var{value}@}
+...@cindex $...@{@var{va...@var{value}@}
 When using @sam...@{@var{va...@var{value}@}} to assign a default value
 to @var{var}, remember that even though the assignment to @var{var} does
 not undergo file name expansion, the result of the variable expansion
-does.  In particular, when using @command{:} followed by unquoted
-variable expansion for the side effect of setting a default value, if
-either @samp{value} or the prior contents of @samp{$var} contains
-globbing characters, the shell has to spend time performing file name
+does unless the expansion occurred within double quotes.  In particular,
+when using @command{:} followed by unquoted variable expansion for the
+side effect of setting a default value, if the final value of
+...@samp{$var} contains any globbing characters (either from @var{value} or
+from prior contents), the shell has to spend time performing file name
 expansion and field splitting even though those results will not be
-used.  Therefore, it is a good idea to use double quotes when performing
-default initialization.
+used.  Therefore, it is a good idea to consider double quotes when performing
+default initialization; while remembering how this impacts any quoting
+characters appering in @var{value}.

 @example
-$ time bash -c ': "$...@{a=/usr/bin/*...@}"; echo "$a"'
+$ @kbd{time bash -c ': "$...@{a=/usr/bin/*...@}"; echo "$a"'}
 /usr/bin/*

 real   0m0.005s
 user   0m0.002s
 sys    0m0.003s
-$ time bash -c ': $...@{a=/usr/bin/*...@}; echo "$a"'
+$ @kbd{time bash -c ': $...@{a=/usr/bin/*...@}; echo "$a"'}
 /usr/bin/*

 real   0m0.039s
@@ -15424,20 +15478,41 @@ Shell Substitutions
 sys    0m0.009s
 @end example

-Use quotes if @var{literal} contains more than one shell word:
+As with @samp{+} and @samp{-}, you must use quotes when using @samp{=}
+if the @var{value} contains more than one shell word; either single
+quotes for just the @var{value}, or double quotes around the entire
+expansion:

 @example
-: "$...@{var='Some words'@}"
+$ @kbd{: $...@{var1='Some words'@}}
+$ @kbd{: "$...@{var2=like t...@}"}
+$ @kbd{echo $var1 $var2}
+Some words like this
 @end example

 @noindent
-otherwise some shells, such as on Digital Unix V 5.0, die because
-of a ``bad substitution''.
+otherwise some shells, such as Solaris @command{/bin/sh} or on Digital
+Unix V 5.0, die because of a ``bad substitution''.  Meanwhile, Posix
+requires that with @samp{=}, quote removal happens prior to the
+assignment, and the expansion be the final contents of @var{var} without
+quoting (and thus subject to field splitting), in contrast to the
+behavior with @samp{-} passing the quoting through to the final
+expansion.  However, @command{bash} 4.1 does not obey this rule.
+
+...@example
+$ @kbd{ksh -c 'echo $...@{var-a\ \ b...@}'}
+a  b
+$ @kbd{ksh -c 'echo $...@{var=a\ \ b...@}'}
+a b
+$ @kbd{bash -c 'echo $...@{var=a\ \ b...@}'}
+a  b
+...@end example

 @sp 1

-Solaris @command{/bin/sh} has a frightening bug in its interpretation
-of this.  Imagine you need set a variable to a string containing
+...@cindex $...@{@var{va...@var{literal}@}
+Solaris @command{/bin/sh} has a frightening bug in its handling of
+assignments.  Imagine you need set a variable to a string containing
 @sa...@}}.  This @sa...@}} character confuses Solaris @command{/bin/sh}
 when the affected variable was already set.  This bug can be exercised
 by running:
@@ -15467,7 +15542,7 @@ Shell Substitutions

 @example
 default="yu,yaa"
-: "$...@{var="$default"@}"
+: "$...@{var=$default@}"
 @end example

 @noindent
@@ -15493,7 +15568,7 @@ Shell Substitutions

 @example
 default="a b c"
-: "$...@{list="$default"@}"
+: $...@{list="$default"@}
 for c in $list; do
   echo $c
 done
@@ -15524,6 +15599,23 @@ Shell Substitutions
 test "$...@{var+set@}" = set || v...@var{@{va...@}}
 @end example

+Finally, Posix states that when mixing @sam...@{a=b@}} with regular
+commands, it is unspecified whether the assignments affect the parent
+shell environment.  It is best to perform assignments independently from
+commands, to avoid the problems demonstrated in this example:
+
+...@example
+$ @kbd{bash -c 'x= y...@{x:=...@} sh -c "echo +\$x+\$y+";echo -$x-'}
++b+b+
+-b-
+$ @kbd{/bin/sh -c 'x= y...@{x:=...@} sh -c "echo +\$x+\$y+";echo -$x-'}
+++b+
+--
+$ @kbd{ksh -c 'x= y...@{x:=...@} sh -c "echo +\$x+\$y+";echo -$x-'}
++b+b+
+--
+...@end example
+
 @item $...@{#@var{v...@}
 @itemx $...@{@var{va...@var{word}@}
 @itemx $...@{@var{var...@var{word}@}
@@ -15755,7 +15847,7 @@ Assignments
 (i.e., it's not a list), then use:

 @example
-: "$...@{var="$default"@}"
+: $...@{var="$default"@}
 @end example

 @item
diff --git a/tests/torture.at b/tests/torture.at
index a8a2aa1..6855da4 100644
--- a/tests/torture.at
+++ b/tests/torture.at
@@ -909,7 +909,7 @@ AC_DEFINE_UNQUOTED([unq1], [$baz], [unquoted, test 1])
 AC_DEFINE_UNQUOTED([unq2], [\$baz], [unquoted, test 2])
 AC_DEFINE_UNQUOTED([unq3], ["$baz"], [unquoted, test 3])
 AC_DEFINE_UNQUOTED([unq4], [${baz+set}], [unquoted, test 4])
-AC_DEFINE_UNQUOTED([unq5], ["${baz+`echo "a b"`}"], [unquoted, test 5])
+AC_DEFINE_UNQUOTED([unq5], ["${baz+`echo "a "' b'`}"], [unquoted, test 5])
 AC_DEFINE_UNQUOTED([unq6], [`echo hi`], [unquoted, test 6])
 AC_DEFINE_UNQUOTED([unq7], ['\\"'], [unquoted, test 7])
 AC_PROG_AWK
@@ -943,7 +943,7 @@ x...@file@
 #define unq2 $baz
 #define unq3 "bla"
 #define unq4 set
-#define unq5 "a b"
+#define unq5 "a  b"
 #define unq6 hi
 #define unq7 '\"'
 ]])
-- 
1.7.2.2


Reply via email to