A while ago, I started a thread about ideas of cleaning up AS_ESCAPE, and it even got a favorable review from Paolo. However, it ran into a snag because the optimal semantics (expanding the string before adding escapes) doesn't work with the fact that there are existing clients of AS_ESCAPE that call AC_REQUIRE, and this does not work inside m4_expand. http://lists.gnu.org/archive/html/autoconf-patches/2008-11/msg00185.html
However, there are still some simpler cleanups that can be done, whether or not I figure out how to make m4_expand and m4_require play nicely. In particular, we can take advantage of my recent patch to m4 to be faster on m4_translit when the set of characters to be changed is small: http://lists.gnu.org/archive/html/m4-patches/2009-02/msg00017.html In my test runs, I saw about 0.5% improvement in execution time when running autoconf on coreutils. This slightly changes the semantics of AS_ESCAPE, but a google code search found most clients of AS_ESCAPE used the default second argument, and of those few that didn't, they were still compliant with the additional restrictions added by this patch. When documenting AS_ESCAPE, I also noticed that a few macros recently documented in the manual hadn't shown up in the NEWS. From: Eric Blake <[email protected]> Date: Thu, 19 Feb 2009 09:53:35 -0700 Subject: [PATCH 1/2] Mention recently documented macros. * NEWS: Update list of new documentation. Signed-off-by: Eric Blake <[email protected]> --- ChangeLog | 5 +++++ NEWS | 13 +++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index d766102..79b6740 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2009-02-19 Eric Blake <[email protected]> + + Mention recently documented macros. + * NEWS: Update list of new documentation. + 2009-02-14 Ralf Wildenhues <[email protected]> Add index for config.site. diff --git a/NEWS b/NEWS index 94b310e..a1f0a60 100644 --- a/NEWS +++ b/NEWS @@ -55,10 +55,11 @@ GNU Autoconf NEWS - User visible changes. `autoreconf -I dir' option. ** The following documented m4sugar macros are new: - m4_chomp m4_curry m4_default_quoted m4_esyscmd_s m4_map_args - m4_map_args_pair m4_map_args_sep m4_map_args_w m4_set_map - m4_set_map_sep m4_stack_foreach m4_stack_foreach_lifo - m4_stack_foreach_sep m4_stack_foreach_sep_lifo + m4_chomp m4_chomp_all m4_cleardivert m4_curry m4_default_quoted + m4_esyscmd_s m4_map_args m4_map_args_pair m4_map_args_sep + m4_map_args_w m4_set_map m4_set_map_sep m4_stack_foreach + m4_stack_foreach_lifo m4_stack_foreach_sep + m4_stack_foreach_sep_lifo ** The following m4sugar macros are documented now, but in some cases with slightly different semantics than what the previous @@ -73,8 +74,8 @@ GNU Autoconf NEWS - User visible changes. unbalanced `(' where single-quoting used to be sufficient. ** The following documented m4sh macros are new: - AS_LINENO_PREPARE AS_ME_PREPARE AS_SET_STATUS AS_VAR_APPEND - AS_VAR_ARITH AS_VAR_COPY + AS_INIT_GENERATED AS_LINENO_PREPARE AS_ME_PREPARE AS_SET_STATUS + AS_VAR_APPEND AS_VAR_ARITH AS_VAR_COPY ** The following m4sh macros are documented now, but in some cases with slightly different semantics than what the previous -- 1.6.1.2 >From 4da1abd28bae9bbe54e49159929f33628476fc9a Mon Sep 17 00:00:00 2001 From: Eric Blake <[email protected]> Date: Thu, 19 Feb 2009 14:32:36 -0700 Subject: [PATCH 2/2] Use m4_translit more efficiently in AS_ESCAPE. * lib/m4sugar/m4sh.m4 (_AS_ESCAPE): Alter API to take first byte of set separately from rest. (AS_ESCAPE, _AS_QUOTE_MODERN, AS_TR_SH, AS_VAR_GET): Adjust callers. * lib/autoconf/autoheader.m4 (AH_VERBATIM): Avoid duplicate characters in translit request. * doc/autoconf.texi (Common Shell Constructs) <AS_ESCAPE>: Document the macro. * NEWS: Likewise. Signed-off-by: Eric Blake <[email protected]> --- ChangeLog | 11 ++++++++++ NEWS | 4 +- doc/autoconf.texi | 45 ++++++++++++++++++++++++++++++++++++++++++++ lib/autoconf/autoheader.m4 | 4 +- lib/m4sugar/m4sh.m4 | 34 ++++++++++++++++++++++++-------- 5 files changed, 85 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 79b6740..c68d1b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2009-02-19 Eric Blake <[email protected]> + Use m4_translit more efficiently in AS_ESCAPE. + * lib/m4sugar/m4sh.m4 (_AS_ESCAPE): Alter API to take first byte + of set separately from rest. + (AS_ESCAPE, _AS_QUOTE_MODERN, AS_TR_SH, AS_VAR_GET): Adjust + callers. + * lib/autoconf/autoheader.m4 (AH_VERBATIM): Avoid duplicate + characters in translit request. + * doc/autoconf.texi (Common Shell Constructs) <AS_ESCAPE>: + Document the macro. + * NEWS: Likewise. + Mention recently documented macros. * NEWS: Update list of new documentation. diff --git a/NEWS b/NEWS index a1f0a60..34b4380 100644 --- a/NEWS +++ b/NEWS @@ -80,8 +80,8 @@ GNU Autoconf NEWS - User visible changes. ** The following m4sh macros are documented now, but in some cases with slightly different semantics than what the previous undocumented version had: - AS_ECHO AS_ECHO_N AS_EXIT AS_LITERAL_IF AS_UNSET AS_VAR_IF - AS_VAR_POPDEF AS_VAR_PUSHDEF AS_VAR_SET AS_VAR_SET_IF + AS_ECHO AS_ECHO_N AS_ESCAPE AS_EXIT AS_LITERAL_IF AS_UNSET + AS_VAR_IF AS_VAR_POPDEF AS_VAR_PUSHDEF AS_VAR_SET AS_VAR_SET_IF AS_VAR_TEST_SET AS_VERSION_COMPARE ** The m4sh macros AS_IF and AS_CASE can now be used in shell lists. diff --git a/doc/autoconf.texi b/doc/autoconf.texi index 9588301..dcf591d 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -12338,6 +12338,51 @@ Common Shell Constructs Redirections can be placed outside the macro invocation. @end defmac +...@defmac AS_ESCAPE (@var{string}, @dvar{chars, `\"$}) +...@asindex{escape} +Expands to @var{string}, with any characters in @var{chars} escaped with +a backslash (@samp{\}). @var{chars} should be at most four bytes long, +and only contain characters from the set @samp{`\"$}; however, +characters may be safely listed more than once in @var{chars} for the +sake of syntax highlighting editors. The current implementation expands +...@var{string} after adding escapes; if @var{string} contains macro calls +that have text that needs quoting, you can use +...@code{as_escape(m4_dquote(m4_expand([string])))}. + +The default for @var{chars} (@samp{\"$`}) is the set of characters +needing escapes when @var{string} will be used literally within double +quotes. One common variant is the set of characters to protect when +...@var{string} will be used literally within back-ticks or an unquoted +here-doc (@samp{\$`}). Another common variant is @samp{""}, which can +be used to form a double-quoted string containing the same expansions +that would have occurred if @var{string} were expanded in an unquoted +here-doc; however, when using this variant, care must be taken that +...@var{string} does not use double quotes within complex variable +expansions (such as @sam...@{foo-`echo "hi"`...@}}) that would be broken +with improper escapes. + +This macro is often used with @code{AS_ECHO}. For example, this snippet +will produce shell code that outputs the four lines @samp{"$foo" = +"bar"}, @samp{macro}, @samp{a, b}, and @samp{a, \b}: + +...@example +foo=bar +AS_ECHO(["AS_ESCAPE(["$foo" = ])AS_ESCAPE(["$foo"], [""])"]) +m4_define([macro], [a, [\b]]) +AS_ECHO(["AS_ESCAPE([[macro]])"]) +AS_ECHO(["AS_ESCAPE([macro])"]) +AS_ECHO(["AS_ESCAPE(m4_dquote(m4_expand([macro])))"]) +...@end example + +...@comment Should we add AS_ESCAPE_SINGLE? If we do, we can optimize in +...@comment the case of @var{string} that does not contain '. +To escape a string that will be placed within single quotes, use: + +...@example +m4_bpatsubst([...@var{string}]], ['], ['\\'']) +...@end example +...@end defmac + @defmac AS_EXIT (@dvar{status, $?}) @asindex{EXIT} Emit code to exit the shell with @var{status}, defaulting to @samp{$?}. diff --git a/lib/autoconf/autoheader.m4 b/lib/autoconf/autoheader.m4 index dfc5f77..8e109cc 100644 --- a/lib/autoconf/autoheader.m4 +++ b/lib/autoconf/autoheader.m4 @@ -2,7 +2,7 @@ # Interface with autoheader. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2008 Free Software Foundation, Inc. +# 2002, 2008, 2009 Free Software Foundation, Inc. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -68,7 +68,7 @@ m4_define([AH_OUTPUT], []) # Quote for Perl '' strings, which are those used by Autoheader. m4_define([AH_VERBATIM], [AS_LITERAL_IF([$1], - [AH_OUTPUT([$1], AS_ESCAPE([[$2]], [\\'']))])]) + [AH_OUTPUT([$1], AS_ESCAPE([[$2]], [\']))])]) # AH_TEMPLATE(KEY, DESCRIPTION) diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4 index 2e17a9d..e033063 100644 --- a/lib/m4sugar/m4sh.m4 +++ b/lib/m4sugar/m4sh.m4 @@ -680,18 +680,33 @@ m4_define([AS_MESSAGE_LOG_FD]) m4_define([AS_ORIGINAL_STDIN_FD], [0]) -# AS_ESCAPE(STRING, [CHARS = $"`\]) +# AS_ESCAPE(STRING, [CHARS = `\"$]) # --------------------------------- -# Escape the CHARS in STRING. +# Add backslash escaping to the CHARS in STRING. In an effort to +# optimize use of this macro inside double-quoted shell constructs, +# the behavior is intentionally undefined if CHARS is longer than 4 +# bytes, or contains bytes outside of the set [`\"$]. However, +# repeated bytes within the set are permissible (AS_ESCAPE([$1], [""]) +# being a common way to be nice to syntax highlighting). # # Avoid the m4_bpatsubst if there are no interesting characters to escape. # _AS_ESCAPE bypasses argument defaulting. m4_define([AS_ESCAPE], -[_$0([$1], m4_default([$2], [\"$`]))]) +[_$0([$1], m4_if([$2], [], [[`], [\"$]], [m4_substr([$2], [0], [1]), [$2]]))]) + +# _AS_ESCAPE(STRING, KEY, SET) +# ---------------------------- +# Backslash-escape all instances of the singly byte KEY or up to four +# bytes in SET occurring in STRING. Although a character can occur +# multiple times, optimum efficiency occurs when KEY and SET are +# distinct, and when SET does not exceed two bytes. These particular +# semantics allow for the fewest number of parses of STRING, as well +# as taking advantage of newer m4's optimizations when m4_translit is +# passed SET of size 2 or smaller. m4_define([_AS_ESCAPE], -[m4_if(m4_len([$1]), - m4_len(m4_translit([[$1]], [$2])), - [$1], [m4_bpatsubst([$1], [[$2]], [\\\&])])]) +[m4_if(m4_index(m4_translit([[$1]], [$3], [$2$2$2$2]), [$2]), [-1], + [$0_], [m4_bpatsubst])([$1], [[$2$3]], [\\\&])]) +m4_define([_AS_ESCAPE_], [$1]) # _AS_QUOTE(STRING) @@ -722,7 +737,7 @@ m4_define([_AS_QUOTE], [_AS_QUOTE_MODERN])([$1])]) m4_define([_AS_QUOTE_MODERN], -[_AS_ESCAPE([$1], [`""])]) +[_AS_ESCAPE([$1], [`], [""])]) m4_define([_AS_QUOTE_OLD], [m4_warn([obsolete], @@ -1725,7 +1740,8 @@ m4_defun_init([AS_TR_SH], m4_dquote(m4_dquote(m4_defn([m4_cr_not_symbols2])))[[, [pp[]]]]dnl m4_dquote(m4_dquote(m4_for(,1,255,,[[_]])))[[)], - [`AS_ECHO(["_AS_ESCAPE(m4_dquote(m4_expand([$1])), [\`])"]) | $as_tr_sh`])]) + [`AS_ECHO(["_AS_ESCAPE(m4_dquote(m4_expand([$1])), + [`], [\])"]) | $as_tr_sh`])]) # _AS_TR_CPP_PREPARE @@ -1883,7 +1899,7 @@ m4_define([AS_VAR_COPY], m4_define([AS_VAR_GET], [AS_LITERAL_IF([$1], [$$1], - [`eval 'as_val=${'_AS_ESCAPE([[$1]], [\`])'};AS_ECHO(["$as_val"])'`])]) + [`eval 'as_val=${'_AS_ESCAPE([[$1]], [`], [\])'};AS_ECHO(["$as_val"])'`])]) # AS_VAR_IF(VARIABLE, VALUE, IF-TRUE, IF-FALSE) -- 1.6.1.2
