+2008-10-21 Eric Blake <ebb9@byu.net>
+
+ Add AS_VAR_APPEND.
+ * lib/m4sugar/m4sh.m4 (_AS_VAR_APPEND_PREPARE)
+ (_AS_VAR_APPEND_WORKS, AS_VAR_APPEND): New macros.
+ (AS_PREPARE, _AS_PREPARE): Emit preparation.
+ * tests/m4sh.at (AS@&t@_VAR_APPEND): New test.
+ * doc/autoconf.texi (Polymorphic Variables) <AS_VAR_APPEND>:
+ Document new macro.
+ <AS_VAR_SET>: Mention ramification of `""` rules.
+ * NEWS: Mention new macro.
+
2008-10-21 Paolo Bonzini <bonzini@gnu.org>
and Eric Blake <ebb9@byu.net>
m4_set_map
** The following documented m4sh macros are new:
- AS_LINENO_PREPARE AS_ME_PREPARE AS_VAR_COPY
+ AS_LINENO_PREPARE AS_ME_PREPARE AS_VAR_APPEND AS_VAR_COPY
** The following m4sh macros are documented now:
AS_ECHO AS_ECHO_N AS_LITERAL_IF AS_UNSET AS_VAR_IF AS_VAR_POPDEF
@end example
@end defmac
+@defmac AS_VAR_APPEND (@var{var}, @var{text})
+@asindex{APPEND}
+Emit shell code to append the shell expansion of @var{text} to the end
+of the current contents of the polymorphic shell variable @var{var},
+taking advantage of shells that provide the @samp{+=} extension for more
+efficient scaling.
+
+For situations where the final contents of @var{var} are relatively
+short (less than 256 bytes), it is more efficient to use the simpler
+code sequence of @code{@var{var}=$@{@var{var}@}@var{text}} (or its
+polymorphic equivalent of @code{AS_VAR_COPY([tmp], [@var{var}])} and
+@code{AS_VAR_SET([@var{var}], ["$tmp"@var{text}])}). But in the case
+when the script will be repeatedly appending text into @code{var},
+issues of scaling start to become apparent. A naive implementation
+requires execution time linear to the length of the current contents of
+@var{var} as well as the length of @var{text} for a single append, for
+an overall quadratic scaling with multiple appends. This macro takes
+advantage of shells which provide the extension
+@code{@var{var}+=@var{text}}, which can provide amortized constant time
+for a single append, for an overall linear scaling with multiple
+appends. Note that unlike @code{AS_VAR_SET}, this macro requires that
+@var{text} be quoted properly to avoid field splitting and file name
+expansion.
+@end defmac
+
@defmac AS_VAR_COPY (@var{dest}, @var{source})
@asindex{VAR_COPY}
Emit shell code to assign the contents of the polymorphic shell variable
@defmac AS_VAR_SET (@var{var}, @ovar{value})
@asindex{VAR_SET}
Emit shell code to assign the contents of the polymorphic shell variable
-@var{var} to the shell expansion of @var{value}.
+@var{var} to the shell expansion of @var{value}. @var{value} is not
+subject to field splitting or file name expansion, so if command
+substitution is used, it may be done with @samp{`""`} rather than using
+an intermediate variable (@pxref{Shell Substitutions}).
@end defmac
@defmac AS_VAR_SET_IF (@var{var}, @ovar{if-set}, @ovar{if-undef})
_AS_TR_CPP_PREPARE
_AS_TR_SH_PREPARE
_AS_UNSET_PREPARE
+_AS_VAR_APPEND_PREPARE
m4_popdef([AS_REQUIRE])dnl
])
AS_REQUIRE([_AS_TR_CPP_PREPARE])
AS_REQUIRE([_AS_TR_SH_PREPARE])
AS_REQUIRE([_AS_UNSET_PREPARE])
+AS_REQUIRE([_AS_VAR_APPEND_PREPARE], [], [M4SH-INIT-FN])
m4_divert_pop[]dnl
])
# when passed through eval, and a polymorphic name is either type.
+# _AS_VAR_APPEND_PREPARE
+# ----------------------
+# Define as_func_append to the optimum definition for the current
+# shell (bash and zsh provide the += assignment operator to avoid
+# quadratic append growth over repeated appends).
+m4_defun([_AS_VAR_APPEND_PREPARE],
+[AS_FUNCTION_DESCRIBE([as_func_append], [VAR VALUE],
+[Append the text in VALUE to the end of the definition contained in
+VAR. Take advantage of any shell optimizations that allow amortized
+linear growth over repeated appends, instead of the typical quadratic
+growth present in naive implementations.])
+AS_IF([_AS_RUN(["AS_ESCAPE([_AS_VAR_APPEND_WORKS])"])],
+[eval 'as_func_append ()
+ {
+ eval $[]1+=\$[]2
+ }'],
+[as_func_append ()
+ {
+ eval $[]1=\$$[]1\$[]2
+ }]) # as_func_append
+])
+
+
+# _AS_VAR_APPEND_WORKS
+# --------------------
+# Output a shell test to discover whether += works.
+m4_define([_AS_VAR_APPEND_WORKS],
+[as_var=1; as_var+=2; test x$as_var = x12])
+
+
+# AS_VAR_APPEND(VAR, VALUE)
+# -------------------------
+# Append the shell expansion of VALUE to the end of the existing
+# contents of the polymorphic shell variable VAR, taking advantage of
+# any shell optimizations that allow repeated appends to result in
+# amortized linear scaling rather than quadratic behavior. This macro
+# is not worth the overhead unless the expected final size of the
+# contents of VAR outweigh the typical VALUE size of repeated appends.
+# Note that unlike AS_VAR_SET, VALUE must be properly quoted to avoid
+# field splitting and file name expansion.
+m4_define([AS_VAR_APPEND],
+[_AS_DETECT_SUGGESTED([_AS_VAR_APPEND_WORKS])dnl
+AS_REQUIRE([_AS_VAR_APPEND_PREPARE], [], [M4SH-INIT-FN])dnl
+as_func_append $1 $2])
+
+
# AS_VAR_COPY(DEST, SOURCE)
# -------------------------
# Set the polymorphic shell variable DEST to the contents of the polymorphic
# AS_VAR_SET(VARIABLE, VALUE)
# ---------------------------
# Set the contents of the polymorphic shell VARIABLE to the shell
-# expansion of VALUE.
+# expansion of VALUE. VALUE is immune to field splitting and file
+# name expansion.
m4_define([AS_VAR_SET],
[AS_LITERAL_IF([$1],
[$1=$2],
## AS_VAR_*. ##
## ---------- ##
-AT_SETUP([AS@&t@_VAR])
+AT_SETUP([AS@&t@_VAR basics])
AT_KEYWORDS([m4sh AS@&t@_VAR_COPY AS@&t@_VAR_SET AS@&t@_VAR_GET])
AT_KEYWORDS([AS@&t@_VAR_TEST_SET AS@&t@_VAR_SET_IF AS@&t@_VAR_IF])
AT_KEYWORDS([AS@&t@_VAR_PUSHDEF AS@&t@_VAR_POPDEF])
AT_CLEANUP
+## --------------- ##
+## AS_VAR_APPEND. ##
+## --------------- ##
+
+AT_SETUP([AS@&t@_VAR_APPEND])
+AT_KEYWORDS([m4sh AS@&t@_VAR])
+
+AT_DATA_M4SH([script.as], [[dnl
+AS_INIT
+# Literals.
+AS_VAR_APPEND([foo], ["hello, "])
+AS_VAR_APPEND([foo], [world])
+echo "$foo"
+# Indirects via shell vars.
+num=1
+AS_VAR_APPEND([foo$num], ['hello, '])
+AS_VAR_APPEND([foo$num], [`echo "world"`])
+echo "$foo1"
+# Indirects via command substitution.
+h=hello w=', world'
+AS_VAR_APPEND([`echo foo2`], [${h}])
+AS_VAR_APPEND([`echo foo2`], ["$w"])
+echo "$foo2"
+]])
+
+AT_CHECK_M4SH
+AT_CHECK([./script], [],
+[[hello, world
+hello, world
+hello, world
+]])
+
+AT_CLEANUP
+
+
## ----------------- ##
## AS_INIT cleanup. ##
## ----------------- ##