2010-07-02 Eric Blake <eblake@redhat.com>
+ Add AS_LITERAL_WORD_IF.
+ * lib/m4sugar/m4sh.m4 (_AS_LITERAL_IF): Also reject shell quoting
+ characters as non-literal, and provide way to reject space.
+ (AS_LITERAL_WORD_IF): New macro.
+ * doc/autoconf.texi (Polymorphic Variables) <AS_LITERAL_IF>:
+ Document new macro. Fix example to match reality.
+ * NEWS: Document change and new macro.
+ * tests/m4sh.at (AS@&t@_LITERAL_IF): Update test.
+
Optimize AC_DEFINE.
* lib/autoconf/general.m4 (_AC_DEFINE_Q): Avoid overhead of
AS_LITERAL_IF.
AT_ARG_OPTION has been changed in that the negative of a long option
--OPTION is now --no-OPTION rather than --noOPTION.
+** The macro AS_LITERAL_IF is slightly more conservative; text
+ containing shell quotes are no longer treated as literals.
+ Furthermore, a new macro, AS_LITERAL_WORD_IF, adds an additional
+ level of checking that no whitespace occurs in literals.
+
* Major changes in Autoconf 2.65 (2009-11-21) [stable]
Released by Eric Blake, based on git versions 2.64.*.
literal variable name.
@defmac AS_LITERAL_IF (@var{expression}, @ovar{if-literal}, @ovar{if-not})
+@defmacx AS_LITERAL_WORD_IF (@var{expression}, @ovar{if-literal})
@asindex{LITERAL_IF}
+@asindex{LITERAL_WORD_IF}
If the expansion of @var{expression} is definitely a shell literal,
expand @var{if-literal}. If the expansion of @var{expression} looks
like it might contain shell indirections (such as @code{$var} or
-@code{`expr`}), then @var{if-not} is expanded. In order to reduce the
-time spent deciding whether an expression is literal, the implementation
-is somewhat conservative (for example, @samp{'[$]'} is a single-quoted
-shell literal, but causes @var{if-not} to be expanded). While this
-macro is often used for recognizing shell variable names, it can also be
-used in other contexts.
+@code{`expr`}), then @var{if-not} is expanded.
+@code{AS_LITERAL_WORD_IF} only expands
+@var{if-literal} if @var{expression} looks like a single shell word,
+containing no whitespace; while @code{AS_LITERAL_IF} allows whitespace
+in @var{expression}.
+
+In order to reduce the time spent recognizing whether an
+@var{expression} qualifies as a literal or a simple indirection, the
+implementation is somewhat conservative: @var{expression} must be a
+single shell word (possibly after stripping whitespace), consisting only
+of bytes that would have the same meaning whether unquoted or enclosed
+in double quotes (for example, @samp{a.b} results in @var{if-literal},
+even though it is not a valid shell variable name; while both @samp{'a'}
+and @samp{[$]} result in @var{if-not}, because they behave differently
+than @samp{"'a'"} and @samp{"[$]"}). This macro can be used in contexts
+for recognizing portable file names (such as in the implementation of
+@code{AC_LIBSOURCE}), or coupled with some transliterations for forming
+valid variable names (such as in the implementation of @code{AS_TR_SH},
+which uses an additional @code{m4_translit} to convert @samp{.} to
+@samp{_}).
+
+This example shows how to read the contents of the shell variable
+@code{bar}, exercising both arguments to @code{AS_LITERAL_IF}. It
+results in a script that will output the line @samp{hello} three times.
@example
AC_DEFUN([MY_ACTION],
[AS_LITERAL_IF([$1],
-[echo "$1"],
-[AS_VAR_COPY([tmp], [$1])
-echo "$tmp"])])
+ [echo "$$1"],
+ [AS_VAR_COPY([tmp], [$1])
+ echo "$tmp"])])
+foo=bar bar=hello
+MY_ACTION([bar])
+MY_ACTION([`echo bar`])
+MY_ACTION([$foo])
@end example
@end defmac
# AS_LITERAL_IF(EXPRESSION, IF-LITERAL, IF-NOT-LITERAL)
# -----------------------------------------------------
-# If EXPRESSION has shell indirections ($var or `expr`), expand
+# If EXPRESSION has no shell indirections ($var or `expr`), expand
# IF-LITERAL, else IF-NOT-LITERAL.
-# This is an *approximation*: for instance EXPRESSION = `\$' is
-# definitely a literal, but will not be recognized as such.
+#
+# EXPRESSION is treated as a literal if it results in the same
+# interpretation whether it is unquoted or contained within double
+# quotes, with the exception that whitespace is ignored (on the
+# assumption that it will be flattened to _). Therefore, neither `\$'
+# nor `a''b' is a literal, since both backslash and single quotes have
+# different quoting behavior in the two contexts; and `a*' is not a
+# literal, because it has different globbing.
+# This macro is an *approximation*: it is possible that
+# there are some EXPRESSIONs which the shell would treat as literals,
+# but which this macro does not recognize.
#
# Why do we reject EXPRESSION expanding with `[' or `]' as a literal?
# Because AS_TR_SH is MUCH faster if it can use m4_translit on literals
# instead of m4_bpatsubst; but m4_translit is much tougher to do safely
-# if `[' is translated.
+# if `[' is translated. That, and file globbing matters.
#
# Note that the quadrigraph @S|@ can result in non-literals, but outright
# rejecting all @ would make AC_INIT complain on its bug report address.
# We used to use m4_bmatch(m4_quote($1), [[`$]], [$3], [$2]), but
# profiling shows that it is faster to use m4_translit.
#
-# Because the translit is stripping quotes, it must also neutralize anything
-# that might be in a macro name, as well as comments, commas, or unbalanced
-# parentheses. All the problem characters are unified so that a single
-# m4_index can scan the result.
+# Because the translit is stripping quotes, it must also neutralize
+# anything that might be in a macro name, as well as comments, commas,
+# or unbalanced parentheses. Valid shell variable characters and
+# unambiguous literal characters are deleted (`a.b'), and remaining
+# characters are normalized into `$' if they are special to the
+# shell or to m4 parsing, and left alone otherwise.
+# _AS_LITERAL_IF_ only has to check for an empty string after removing
+# the normalized characters.
#
# Rather than expand m4_defn every time AS_LITERAL_IF is expanded, we
# inline its expansion up front.
m4_define([_AS_LITERAL_IF],
[m4_if(m4_cond([m4_eval(m4_index([$1], [@S|@]) == -1)], [0], [],
- [m4_index(m4_translit([$1], [[]`,#()]]]dnl
-m4_dquote(m4_dquote(m4_defn([m4_cr_symbols2])))[[, [$$$]), [$])],
+ [m4_index(m4_translit([$1], [[]`'\"$4,#()]]]dnl
+m4_dquote(m4_dquote(m4_defn([m4_cr_symbols2])))[[, [$$$$$$$5]), [$])],
[-1], [-]), [-], [$2], [$3])])
+# AS_LITERAL_WORD_IF(EXPRESSION, IF-LITERAL, IF-NOT-LITERAL)
+# ----------------------------------------------------------
+# Like AS_LITERAL_IF, except that spaces and tabs in EXPRESSION
+# are treated as non-literal.
+m4_define([AS_LITERAL_WORD_IF],
+[_AS_LITERAL_IF(m4_expand([$1]), [$2], [$3], [ ][
+], [$$$])])
+
+
# AS_TMPDIR(PREFIX, [DIRECTORY = $TMPDIR [= /tmp]])
# -------------------------------------------------
## --------------- ##
AT_SETUP([AS@&t@_LITERAL_IF])
-AT_KEYWORDS([m4sh])
+AT_KEYWORDS([m4sh AS@&t@_LITERAL_WORD_IF])
AT_DATA_M4SH([script.as], [[dnl
AS_INIT
echo AS_LITERAL_IF([lit], [ok], [ERR]) 1
-echo AS_LITERAL_IF([l$it], [ERR], [ok]) 2
-echo AS_LITERAL_IF([l`case a in b) ;; esac`it], [ERR], [ok]) 3
-m4_define([mac], [lit])
-echo AS_LITERAL_IF([mac], [ok], [ERR]) 4
-echo AS_LITERAL_IF([mac($, ``)], [ok], [ERR]) 5
+echo AS_LITERAL_IF([l-/.it], [ok], [ERR]) 2
+echo AS_LITERAL_IF([l''it], [ERR], [ok]) 3
+echo AS_LITERAL_IF([l$it], [ERR], [ok]) 4
+echo AS_LITERAL_IF([l$it], [ERR1], [ok], [fixme]) 5
+echo AS_LITERAL_IF([l${it}], [ERR1], [ok], [fixme]) 6
+echo AS_LITERAL_IF([l`case a in b) ;; esac`it], [ERR], [ok]) 7
+echo AS_LITERAL_IF([l`case a in b) ;; esac`it], [ERR1], [ok], [ERR2]) 8
+m4_define([mac], [l-/.it])
+echo AS_LITERAL_IF([mac], [ok], [ERR]) 9
+echo AS_LITERAL_IF([mac($, ``)], [ok], [ERR]) 10
m4_define([mac], [l$it])
-echo AS_LITERAL_IF([mac], [ERR], [ok]) 6
+echo AS_LITERAL_IF([mac], [ERR], [ok]) 11
+echo AS_LITERAL_IF([mac], [ERR1], [ok], [fixme]) 12
m4_define([mac], [l``it])
-echo AS_LITERAL_IF([mac], [ERR], [ok]) 7
+echo AS_LITERAL_IF([mac], [ERR], [ok]) 13
+echo AS_LITERAL_IF([mac], [ERR1], [ok], [ERR2]) 14
+echo AS_LITERAL_IF([ a ][
+b], [ok], [ok]) 15
+echo AS_LITERAL_WORD_IF([ a ][
+b], [ERR], [ok]) 16
]])
AT_CHECK_M4SH
ok 5
ok 6
ok 7
+ok 8
+ok 9
+ok 10
+ok 11
+ok 12
+ok 13
+ok 14
+ok 15
+ok 16
]])
AT_CLEANUP