According to Eric Blake on 3/1/2010 5:32 PM:
>>>> Autoconf could have optimized this to
>>>>
>>>>   if eval "test \"\${$as_gl_Symbol+set}\" = set"; then
>>>>
> 
> I'm game.  But I think this patch series is a little more robust.  It
> shaves more than 1k size off of coreutils' configure.  The number of
> reduced processes is in the noise.
> 

Here's an updated version of patch 2/3 of the previous post; I realized
that ${a+b} should not be treated as a simple indirection, and added some
documentation.  Patch 1/3 and 3/3 are unchanged.

-- 
Don't work too hard, make some time for fun as well!

Eric Blake             [email protected]
From d9958c50cfba29a617c4c65667e47579e52a4e46 Mon Sep 17 00:00:00 2001
From: Eric Blake <[email protected]>
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@&t...@_literal_if): Update test.
* doc/autoconf.texi (Polymorphic Variables) <AS_LITERAL_IF>:
Tighten specification and document extra parameter.
* NEWS: Mention the change.

Signed-off-by: Eric Blake <[email protected]>
---
 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  <[email protected]>

+       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@&t...@_literal_if): Update test.
+       * doc/autoconf.texi (Polymorphic Variables) <AS_LITERAL_IF>:
+       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.

-...@defmac AS_LITERAL_IF (@var{expression}, @ovar{if-literal}, @ovar{if-not})
+...@defmac 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
-...@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
+...@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 @cod...@{var@}}), in which case
+...@var{if-not-simple} 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
+...@code{bar}, 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], [...@s|@]) == -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], [...@s|@]), [-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

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to