From d9958c50cfba29a617c4c65667e47579e52a4e46 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Sat, 27 Feb 2010 10:39:31 -0700 Subject: [PATCH] Add optional argument to AS_LITERAL_IF. * lib/m4sugar/m4sh.m4 (_AS_LITERAL_IF): Rewrite to generate macro name, without using m4_cond. Allow more control over whitespace. (_AS_LITERAL_IF_, _AS_LITERAL_IF_YES, _AS_LITERAL_IF_NO): New helpers. (AS_LITERAL_IF, _AS_TR_SH, _AS_TR_CPP, _AS_VAR_PUSHDEF): Adjust callers. * lib/autoconf/types.m4 (AC_CHECK_MEMBER): Don't hand space to AS_LITERAL_IF. * lib/autoconf/general.m4 (_AC_INIT_PACKAGE): Likewise. * tests/m4sh.at (AS@&address@hidden): Update test. * doc/autoconf.texi (Polymorphic Variables) : Tighten specification and document extra parameter. * NEWS: Mention the change. Signed-off-by: Eric Blake --- ChangeLog | 15 ++++++++++ NEWS | 4 ++ doc/autoconf.texi | 36 +++++++++++++++++------ lib/autoconf/general.m4 | 3 +- lib/autoconf/types.m4 | 6 ++- lib/m4sugar/m4sh.m4 | 72 +++++++++++++++++++++++++++++++++------------- tests/m4sh.at | 28 +++++++++++++---- 7 files changed, 124 insertions(+), 40 deletions(-) diff --git a/ChangeLog b/ChangeLog index ce31a7e..71b24ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,20 @@ 2010-03-01 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. Allow more control over whitespace. + (_AS_LITERAL_IF_, _AS_LITERAL_IF_YES, _AS_LITERAL_IF_NO): New + helpers. + (AS_LITERAL_IF, _AS_TR_SH, _AS_TR_CPP, _AS_VAR_PUSHDEF): Adjust + callers. + * lib/autoconf/types.m4 (AC_CHECK_MEMBER): Don't hand space to + AS_LITERAL_IF. + * lib/autoconf/general.m4 (_AC_INIT_PACKAGE): Likewise. + * tests/m4sh.at (AS@&address@hidden): Update test. + * doc/autoconf.texi (Polymorphic Variables) : + Tighten specification and document extra parameter. + * NEWS: Mention the change. + Optimize AS_BOX. * lib/m4sugar/m4sh.m4 (AS_BOX): Use less m4 time. (_AS_BOX_LITERAL): Use fewer forks in the common case. diff --git a/NEWS b/NEWS index c11d3a1..ccef43e 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,10 @@ GNU Autoconf NEWS - User visible changes. ** The following macros are now documented: AS_BOX +** The AS_LITERAL_IF macro no longer accepts whitespace as literals, in + order to support the addition of an optional parameter to reduce + script size for some cases of indirect variable references. + * Major changes in Autoconf 2.65 (2009-11-21) [stable] Released by Eric Blake, based on git versions 2.64.*. diff --git a/doc/autoconf.texi b/doc/autoconf.texi index 0c7c981..a568cfe 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -13268,24 +13268,42 @@ Polymorphic Variables undefined if expansion of an indirect variable does not result in a literal variable name. address@hidden AS_LITERAL_IF (@var{expression}, @ovar{if-literal}, @ovar{if-not}) address@hidden AS_LITERAL_IF (@var{expression}, @ovar{if-literal}, @ovar{if-not}, @ + @dvar{if-not-simple, @var{if-not}}) @asindex{LITERAL_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 address@hidden), 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 address@hidden), 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 @address@hidden@}}), in which case address@hidden can be provided. In order to reduce the +time spent deciding whether an expression is literal or a simple +indirection, the implementation is somewhat conservative: the expression +must be a single shell word, 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 @samp{'[$]'} results in @var{if-not}, +because it behaves differently than @samp{"'[$]'"}). While this macro is often used for recognizing shell variable names, it can also be -used in other contexts. +used in other contexts; for example, @code{AC_LIBSOURCE} uses it to +ensure that a direct file name was supplied. + +This example shows how to read the contents of the shell variable address@hidden, using all three 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"], + [eval 'echo "$'"$1"\"])]) +foo=bar bar=hello +MY_ACTION([bar]) +MY_ACTION([`echo bar`]) +MY_ACTION([$foo]) @end example @end defmac diff --git a/lib/autoconf/general.m4 b/lib/autoconf/general.m4 index 15640c8..396ec3d 100644 --- a/lib/autoconf/general.m4 +++ b/lib/autoconf/general.m4 @@ -229,7 +229,8 @@ AU_ALIAS([AC_HELP_STRING], [AS_HELP_STRING]) # _AC_INIT_PACKAGE(PACKAGE-NAME, VERSION, BUG-REPORT, [TARNAME], [URL]) # --------------------------------------------------------------------- m4_define([_AC_INIT_PACKAGE], -[AS_LITERAL_IF([$1], [], [m4_warn([syntax], [AC_INIT: not a literal: $1])]) +[AS_LITERAL_IF(m4_translit([[$1]], [ ], [__]), [], + [m4_warn([syntax], [AC_INIT: not a literal: $1])]) AS_LITERAL_IF([$2], [], [m4_warn([syntax], [AC_INIT: not a literal: $2])]) AS_LITERAL_IF([$3], [], [m4_warn([syntax], [AC_INIT: not a literal: $3])]) m4_ifndef([AC_PACKAGE_NAME], diff --git a/lib/autoconf/types.m4 b/lib/autoconf/types.m4 index 0e0e3f3..0774582 100644 --- a/lib/autoconf/types.m4 +++ b/lib/autoconf/types.m4 @@ -877,8 +877,10 @@ AC_DEFUN([AC_CHECK_MEMBER], [Tries to find if the field MEMBER exists in type AGGR, after including 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 +[AS_LITERAL_IF(m4_translit([[$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 [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 6a43f21..7439ed7 100644 --- a/lib/m4sugar/m4sh.m4 +++ b/lib/m4sugar/m4sh.m4 @@ -1515,17 +1515,30 @@ 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 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. +# 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 +# quotes. 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. 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. # # 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. @@ -1533,21 +1546,39 @@ m4_dquote(m4_dquote(m4_defn([m4_cr_symbols1])))[[))], [0], [-])]) # 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 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 +# 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], address@hidden|@]) == -1)], [0], [], - [m4_index(m4_translit([$1], [[]`,#()]]]dnl -m4_dquote(m4_dquote(m4_defn([m4_cr_symbols2])))[[, [$$$]), [$])], - [-1], [-]), [-], [$2], [$3])]) +[m4_if(m4_index([$1], address@hidden|@]), [-1], [$0_(m4_translit([$1], + [-:%/@{}[]#(),.$2]]]m4_dquote(m4_dquote(m4_defn([m4_cr_symbols2])))[[, + [+++++$$`````]))], [$0_NO])]) + +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_TMPDIR(PREFIX, [DIRECTORY = $TMPDIR [= /tmp]]) @@ -1736,7 +1767,7 @@ 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]], @@ -1768,7 +1799,7 @@ 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], @@ -1970,10 +2001,9 @@ 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 f34e6ba..6e3f8b3 100644 --- a/tests/m4sh.at +++ b/tests/m4sh.at @@ -1040,15 +1040,22 @@ AT_KEYWORDS([m4sh]) 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], [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]) +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], [ERR2], [ok]) 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 ]]) AT_CHECK_M4SH @@ -1060,6 +1067,13 @@ ok 4 ok 5 ok 6 ok 7 +ok 8 +ok 9 +ok 10 +ok 11 +ok 12 +ok 13 +ok 14 ]]) AT_CLEANUP -- 1.6.6.1