diff --git a/ChangeLog b/ChangeLog index dc2fa006..d835b3fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,19 @@ 2010-07-02 Eric Blake + Add optional argument to AS_LITERAL_IF. + * lib/m4sugar/m4sh.m4 (_AS_LITERAL_IF): Rewrite to generate macro + name, without using m4_cond. + (_AS_LITERAL_IF_, _AS_LITERAL_IF_YES, _AS_LITERAL_IF_NO): New + helpers. + (AS_LITERAL_IF, AS_LITERAL_WORD_IF, _AS_TR_SH, _AS_TR_CPP) + (_AS_VAR_PUSHDEF): Adjust callers. + * lib/autoconf/types.m4 (AC_CHECK_ALIGNOF): Relax restrictions on + invalid bytes, since this allows inline struct layouts. + (_AC_CHECK_ALIGNOF): New helper macro. + * tests/m4sh.at (AS@&t@_LITERAL_IF): Update test. + * doc/autoconf.texi (Polymorphic Variables) : + Update documentation. + Use AS_LITERAL_WORD_IF as appropriate. * lib/autoconf/autoheader.m4 (AH_VERBATIM): Use new macro. * lib/autoconf/general.m4 (AC_REQUIRE_AUX_FILE, AC_CACHE_VAL) diff --git a/doc/autoconf.texi b/doc/autoconf.texi index ab7ed934..e6d4751a 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -13298,15 +13298,20 @@ names and output shell code appropriate for both use cases. Behavior is undefined if expansion of an indirect variable does not result in a 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}) +@defmac AS_LITERAL_IF (@var{expression}, @ovar{if-literal}, @ovar{if-not}, @ + @dvar{if-simple-ref, @var{if-not}}) +@defmacx AS_LITERAL_WORD_IF (@var{expression}, @ovar{if-literal}, @ + @ovar{if-not}, @dvar{if-simple-ref, @var{if-not}}) @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. -@code{AS_LITERAL_WORD_IF} only expands +@code{`expr`}), then @var{if-not} is expanded. Sometimes, it is +possible to output optimized code if @var{expression} consists only of +shell variable expansions (such as @code{$@{var@}}), in which case +@var{if-simple-ref} can be provided; but defaulting to @var{if-not} +should always be safe. @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}. @@ -13327,7 +13332,7 @@ 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 +@code{bar}, exercising all three arguments to @code{AS_LITERAL_IF}. It results in a script that will output the line @samp{hello} three times. @example @@ -13335,7 +13340,8 @@ AC_DEFUN([MY_ACTION], [AS_LITERAL_IF([$1], [echo "$$1"], [AS_VAR_COPY([tmp], [$1]) - echo "$tmp"])]) + echo "$tmp"], + [eval 'echo "$'"$1"\"])]) foo=bar bar=hello MY_ACTION([bar]) MY_ACTION([`echo bar`]) diff --git a/lib/autoconf/types.m4 b/lib/autoconf/types.m4 index 646b1dcc..ee07148d 100644 --- a/lib/autoconf/types.m4 +++ b/lib/autoconf/types.m4 @@ -785,25 +785,32 @@ AC_DEFINE_UNQUOTED(AS_TR_CPP(sizeof_$1), $AS_TR_SH([ac_cv_sizeof_$1]), # AC_CHECK_ALIGNOF(TYPE, [INCLUDES = DEFAULT-INCLUDES]) # ----------------------------------------------------- +# TYPE can include braces and semicolon, which AS_TR_CPP and AS_TR_SH +# (correctly) recognize as potential shell metacharacters. So we +# have to flatten problematic characters ourselves to guarantee that +# AC_DEFINE_UNQUOTED will see a literal. AC_DEFUN([AC_CHECK_ALIGNOF], -[AS_LITERAL_IF([$1], [], - [m4_fatal([$0: requires literal arguments])])]dnl +[m4_if(m4_index(m4_translit([[$1]], [`\"], [$]), [$]), [-1], [], + [m4_fatal([$0: requires literal arguments])])]dnl +[_$0([$1], [$2], m4_translit([[$1]], [{;}], [___]))]) + +m4_define([_AC_CHECK_ALIGNOF], [# The cast to long int works around a bug in the HP C Compiler, # see AC_CHECK_SIZEOF for more information. -_AC_CACHE_CHECK_INT([alignment of $1], [AS_TR_SH([ac_cv_alignof_$1])], +_AC_CACHE_CHECK_INT([alignment of $1], [AS_TR_SH([ac_cv_alignof_$3])], [(long int) offsetof (ac__type_alignof_, y)], [AC_INCLUDES_DEFAULT([$2]) #ifndef offsetof # define offsetof(type, member) ((char *) &((type *) 0)->member - (char *) 0) #endif typedef struct { char x; $1 y; } ac__type_alignof_;], - [if test "$AS_TR_SH([ac_cv_type_$1])" = yes; then + [if test "$AS_TR_SH([ac_cv_type_$3])" = yes; then AC_MSG_FAILURE([cannot compute alignment of $1], 77) else - AS_TR_SH([ac_cv_alignof_$1])=0 + AS_TR_SH([ac_cv_alignof_$3])=0 fi]) -AC_DEFINE_UNQUOTED(AS_TR_CPP(alignof_$1), $AS_TR_SH([ac_cv_alignof_$1]), +AC_DEFINE_UNQUOTED(AS_TR_CPP(alignof_$3), $AS_TR_SH([ac_cv_alignof_$3]), [The normal alignment of `$1', in bytes.]) ])# AC_CHECK_ALIGNOF @@ -878,7 +885,8 @@ AC_DEFUN([AC_CHECK_MEMBER], INCLUDES, setting cache variable VAR accordingly.])], [_$0_BODY])]dnl [AS_LITERAL_IF([$1], [], [m4_fatal([$0: requires literal arguments])])]dnl -[m4_if(m4_index([$1], [.]), -1, [m4_fatal([$0: Did not see any dot in `$1'])])]dnl +[m4_if(m4_index([$1], [.]), [-1], + [m4_fatal([$0: Did not see any dot in `$1'])])]dnl [AS_VAR_PUSHDEF([ac_Member], [ac_cv_member_$1])]dnl [ac_fn_[]_AC_LANG_ABBREV[]_check_member "$LINENO" ]dnl [m4_bpatsubst([$1], [^\([^.]*\)\.\(.*\)], ["\1" "\2"]) "ac_Member" ]dnl diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4 index b3c6006a..d218eb29 100644 --- a/lib/m4sugar/m4sh.m4 +++ b/lib/m4sugar/m4sh.m4 @@ -1517,10 +1517,14 @@ m4_dquote(m4_dquote(m4_defn([m4_cr_symbols2])))[[)) > 0)], [1], [], m4_dquote(m4_dquote(m4_defn([m4_cr_symbols1])))[[))], [0], [-])]) -# AS_LITERAL_IF(EXPRESSION, IF-LITERAL, IF-NOT-LITERAL) +# AS_LITERAL_IF(EXPRESSION, IF-LITERAL, IF-NOT-LITERAL, +# [IF-SIMPLE-REF = IF-NOT-LITERAL]) # ----------------------------------------------------- # If EXPRESSION has no shell indirections ($var or `expr`), expand -# IF-LITERAL, else IF-NOT-LITERAL. +# IF-LITERAL, else IF-NOT-LITERAL. In some cases, IF-NOT-LITERAL +# must be complex to safely deal with ``, while a simpler +# expression IF-SIMPLE-REF can be used if the indirection +# involves only shell variable expansion (as in ${varname}). # # EXPRESSION is treated as a literal if it results in the same # interpretation whether it is unquoted or contained within double @@ -1528,8 +1532,9 @@ m4_dquote(m4_dquote(m4_defn([m4_cr_symbols1])))[[))], [0], [-])]) # 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 +# literal, because it has different globbing. Note, however, that +# while `${a+b}' is neither a literal nor a simple ref, `a+b' is a +# literal. 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. # @@ -1548,30 +1553,44 @@ m4_dquote(m4_dquote(m4_defn([m4_cr_symbols1])))[[))], [0], [-])]) # 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. +# characters are normalized into `$' if they can form simple refs +# (${a}), `+' if they can potentially form literals (a+b), ``' if they +# can interfere with m4 parsing, or left alone otherwise. If both `$' +# and `+' are left, it is treated as a complex reference (${a+b}), +# even though it could technically be a simple reference (${a}+b). # _AS_LITERAL_IF_ only has to check for an empty string after removing -# the normalized characters. +# one of the two normalized characters. # # Rather than expand m4_defn every time AS_LITERAL_IF is expanded, we -# inline its expansion up front. +# inline its expansion up front. _AS_LITERAL_IF expands to the name +# of a macro that takes three arguments: IF-SIMPLE-REF, +# IF-NOT-LITERAL, IF-LITERAL. It also takes an optional argument of +# any additional characters to allow as literals (useful for AS_TR_SH +# and AS_TR_CPP to perform inline conversion of whitespace to _). The +# order of the arguments allows reuse of m4_default. m4_define([AS_LITERAL_IF], -[_$0(m4_expand([$1]), [$2], [$3])]) +[_$0(m4_expand([$1]), [ ][ +])([$4], [$3], [$2])]) m4_define([_AS_LITERAL_IF], -[m4_if(m4_cond([m4_eval(m4_index([$1], [@S|@]) == -1)], [0], [], - [m4_index(m4_translit([$1], [[]`'\"$4,#()]]]dnl -m4_dquote(m4_dquote(m4_defn([m4_cr_symbols2])))[[, [$$$$$$$5]), [$])], - [-1], [-]), [-], [$2], [$3])]) +[m4_if(m4_index([$1], [@S|@]), [-1], [$0_(m4_translit([$1], + [-:%/@{}[]#(),.$2]]]m4_dquote(m4_dquote(m4_defn([m4_cr_symbols2])))[[, + [+++++$$`````]))], [$0_NO])]) -# AS_LITERAL_WORD_IF(EXPRESSION, IF-LITERAL, IF-NOT-LITERAL) +m4_define([_AS_LITERAL_IF_], +[m4_if(m4_translit([$1], [+]), [], [$0YES], + m4_translit([$1], [$]), [], [m4_default], [$0NO])]) + +m4_define([_AS_LITERAL_IF_YES], [$3]) +m4_define([_AS_LITERAL_IF_NO], [$2]) + +# AS_LITERAL_WORD_IF(EXPRESSION, IF-LITERAL, IF-NOT-LITERAL, +# [IF-SIMPLE-REF = 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_LITERAL_IF(m4_expand([$1]))([$4], [$3], [$2])]) # AS_TMPDIR(PREFIX, [DIRECTORY = $TMPDIR [= /tmp]]) @@ -1760,7 +1779,8 @@ m4_defun_init([AS_TR_SH], [_$0(m4_expand([$1]))]) m4_define([_AS_TR_SH], -[_AS_LITERAL_IF([$1], [$0_LITERAL], [$0_INDIR])([$1])]) +[_AS_LITERAL_IF([$1], [ ][ +])([], [$0_INDIR], [$0_LITERAL])([$1])]) m4_define([_AS_TR_SH_LITERAL], [m4_translit([[$1]], @@ -1792,7 +1812,8 @@ m4_defun_init([AS_TR_CPP], [_$0(m4_expand([$1]))]) m4_define([_AS_TR_CPP], -[_AS_LITERAL_IF([$1], [$0_LITERAL], [$0_INDIR])([$1])]) +[_AS_LITERAL_IF([$1], [ ][ +])([], [$0_INDIR], [$0_LITERAL])([$1])]) m4_define([_AS_TR_CPP_LITERAL], [m4_translit([$1], @@ -1994,10 +2015,10 @@ m4_defun_init([AS_VAR_PUSHDEF], [_$0([$1], m4_expand([$2]))]) m4_define([_AS_VAR_PUSHDEF], -[_AS_LITERAL_IF([$2], - [m4_pushdef([$1], [_AS_TR_SH_LITERAL([$2])])], - [as_$1=_AS_TR_SH_INDIR([$2]) -m4_pushdef([$1], [$as_[$1]])])]) +[_AS_LITERAL_IF([$2], [ ][ +])([], [as_$1=_AS_TR_SH_INDIR([$2]) +m4_pushdef([$1], [$as_[$1]])], +[m4_pushdef([$1], [_AS_TR_SH_LITERAL([$2])])])]) # AS_VAR_SET(VARIABLE, VALUE) diff --git a/tests/m4sh.at b/tests/m4sh.at index 5b3a55db..1e6eb596 100644 --- a/tests/m4sh.at +++ b/tests/m4sh.at @@ -1085,8 +1085,8 @@ echo AS_LITERAL_IF([lit], [ok], [ERR]) 1 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$it], [ERR1], [ERR2], [ok]) 5 +echo AS_LITERAL_IF([l${it}], [ERR1], [ERR2], [ok]) 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]) @@ -1094,7 +1094,7 @@ 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]) 11 -echo AS_LITERAL_IF([mac], [ERR1], [ok], [fixme]) 12 +echo AS_LITERAL_IF([mac], [ERR1], [ERR2], [ok]) 12 m4_define([mac], [l``it]) echo AS_LITERAL_IF([mac], [ERR], [ok]) 13 echo AS_LITERAL_IF([mac], [ERR1], [ok], [ERR2]) 14