From cd9b7c590e884c33b060f56579414b207ec83a86 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. (AS_LITERAL_IF_W): New macro to keep old semantics. * lib/autoconf/types.m4 (AC_CHECK_ALIGNOF): Relax restrictions on invalid bytes, since this allows inline struct layouts. (_AC_CHECK_ALIGNOF): New helper macro. (AC_CHECK_SIZEOF, AC_CHECK_MEMBER): Use new macro to preserve handling of space. * lib/autoconf/general.m4 (_AC_INIT_PACKAGE): Likewise. (_AC_DEFINE_Q): No need to use AS_LITERAL_IF. * 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 | 22 +++++++++++- NEWS | 6 +++ doc/autoconf.texi | 45 ++++++++++++++++++----- lib/autoconf/general.m4 | 6 ++-- lib/autoconf/types.m4 | 26 +++++++++----- lib/m4sugar/m4sh.m4 | 88 +++++++++++++++++++++++++++++++++++------------ tests/m4sh.at | 28 +++++++++++---- 7 files changed, 169 insertions(+), 52 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4b38b7a..679c64a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,24 @@ -2010-03-02 Eric Blake +2010-03-03 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. + (AS_LITERAL_IF_W): New macro to keep old semantics. + * lib/autoconf/types.m4 (AC_CHECK_ALIGNOF): Relax restrictions on + invalid bytes, since this allows inline struct layouts. + (_AC_CHECK_ALIGNOF): New helper macro. + (AC_CHECK_SIZEOF, AC_CHECK_MEMBER): Use new macro to preserve + handling of space. + * lib/autoconf/general.m4 (_AC_INIT_PACKAGE): Likewise. + (_AC_DEFINE_Q): No need to use AS_LITERAL_IF. + * 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. diff --git a/NEWS b/NEWS index c11d3a1..d273126 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,12 @@ 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. The + older semantics of ignoring space, tab, and newline is exposed + via the new macro AS_LITERAL_IF_W. + * 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 b43f7bd..67db60f 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -13274,24 +13274,49 @@ 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}}) address@hidden AS_LITERAL_IF_W (@var{expression}, @ovar{if-literal}, @ovar{if-not}, @ + @dvar{if-not-simple, @var{if-not}}) @asindex{LITERAL_IF} address@hidden 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 -macro is often used for recognizing shell variable names, it can also be -used in other contexts. 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. @code{AS_LITERAL_IF_W} is like address@hidden except that whitespace in @var{expression} is +ignored. + +In order to reduce the time spent recognizing an @var{expression} that +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 @samp{'[$]'} results in address@hidden, 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; 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..d6a6ca0 100644 --- a/lib/autoconf/general.m4 +++ b/lib/autoconf/general.m4 @@ -229,7 +229,7 @@ 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_W([$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], @@ -2117,8 +2117,8 @@ m4_define([_AC_DEFINE_Q], [AC_DEFINE_TRACE(AC_name)]dnl [m4_cond([m4_index([$3], [ ])], [-1], [], - [AS_LITERAL_IF([$3], [m4_bregexp([[$3]], [[^\\] -], [-])])], [], [], + [m4_bregexp([[$3]], [[^\\] +], [-])], [], [], [m4_warn([syntax], [AC_DEFINE]m4_if([$1], [_AC_DEFINE], [], [[_UNQUOTED]])[: `$3' is not a valid preprocessor define value])])]dnl [m4_ifval([$4], [AH_TEMPLATE(AC_name, [$4]) diff --git a/lib/autoconf/types.m4 b/lib/autoconf/types.m4 index 0e0e3f3..9bbdcb4 100644 --- a/lib/autoconf/types.m4 +++ b/lib/autoconf/types.m4 @@ -763,7 +763,7 @@ Remove this warning and the `AC_CACHE_CHECK' when you adjust the code.]) # AC_CHECK_SIZEOF(TYPE, [IGNORED], [INCLUDES = DEFAULT-INCLUDES]) # --------------------------------------------------------------- AC_DEFUN([AC_CHECK_SIZEOF], -[AS_LITERAL_IF([$1], [], +[AS_LITERAL_IF_W([$1], [], [m4_fatal([$0: requires literal arguments])])]dnl [# The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects @@ -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 @@ -877,8 +884,9 @@ 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_W([$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 58ed1fb..105d8a5 100644 --- a/lib/m4sugar/m4sh.m4 +++ b/lib/m4sugar/m4sh.m4 @@ -1514,17 +1514,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 EXPRESSION has no shell indirections ($var or `expr`), expand +# 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. @@ -1532,21 +1545,50 @@ 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_LITERAL_IF_W(EXPRESSION, IF-LITERAL, IF-NOT-LITERAL, +# [IF-SIMPLE-REF = IF-NOT-LITERAL]) +# ------------------------------------------------------- +# Like AS_LITERAL_IF, except that spaces and tabs in EXPRESSION +# are ignored (on the assumption that they will be stripped or +# converted to _ in subsequent use). +m4_define([AS_LITERAL_IF_W], +[_AS_LITERAL_IF(m4_expand([$1]), [ ][ +])([$4], [$3], [$2])]) + # AS_TMPDIR(PREFIX, [DIRECTORY = $TMPDIR [= /tmp]]) @@ -1735,7 +1777,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]], @@ -1767,7 +1810,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], @@ -1969,10 +2013,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 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