Also, @code{m4_append_uniq} warns if @var{separator} is not empty, but
occurs within @var{string}, since that can lead to duplicates.
+Note that @code{m4_append} can scale @dfn{linearithmically} (ie., O(n
+log n) in complexity notation), depending on the quality of the
+underlying M4 implementation, while @code{m4_append_uniq} has an
+inherent quadratic scaling factor. If an algorithm can tolerate
+duplicates in the final string, use the former for speed.
+
@example
m4_define([active], [ACTIVE])dnl
m4_append([sentence], [This is an])dnl
@code{m4_strip}.
@end defmac
+@defmac m4_prepend (@var{macro-name}, @var{string}, @ovar{separator})
+@defmacx m4_prepend_uniq (@var{macro-name}, @var{string}, @ovar{separator} @
+ @ovar{if-uniq}, @ovar{if-duplicate})
+@defmacx m4_append_uniq_w (@var{macro-name}, @var{strings})
+@msindex{prepend}
+@msindex{prepend_uniq}
+@msindex{prepend_uniq_w}
+These macros were introduced in Autoconf 2.63; they are like
+@code{m4_append}, @code{m4_append_uniq}, and @code{m4_append_uniq_w}
+respectively, except that the @var{string} argument is added at the
+beginning instead of the end of the definition of @code{macro-name}.
+
+Also note that unlike @code{m4_append}, @code{m4_prepend} has quadratic
+rather than linearithmic scaling behavior. Thus, if the order of list
+elements does not matter, it is better to append.
+@end defmac
+
@defmac m4_re_escape (@var{string})
@msindex{re_escape}
Backslash-escape all characters in @var{string} that are active in
# m4_append(MACRO-NAME, STRING, [SEPARATOR])
-# ------------------------------------------
+# m4_prepend(MACRO-NAME, STRING, [SEPARATOR])
+# -------------------------------------------
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR`'STRING'
-# at the end. It is valid to use this macro with MACRO-NAME undefined,
+# at the end for m4_append, or `STRING`'SEPARATOR' at the beginning for
+# m4_prepend. It is valid to use this macro with MACRO-NAME undefined,
# in which case no SEPARATOR is added. Be aware that the criterion is
# `not being defined', and not `not being empty'.
#
# => one, two, three
# => [one],[two],[three]
#
+# Note that m4_append can benefit from amortized O(n log n) m4 behavior, if
+# the underlying m4 implementation is smart enough to avoid copying existing
+# contents when enlarging a macro's definition into any pre-allocated storage
+# (m4 1.4.x unfortunately does not implement this optimization). m4_prepend
+# is inherently O(n^2), since pre-allocated storage only occurs at the end
+# of a macro, so the existing contents must always be moved.
+#
# Use m4_builtin to avoid overhead of m4_defn.
m4_define([m4_append],
[m4_define([$1],
m4_ifdef([$1], [m4_builtin([defn], [$1])[$3]])[$2])])
+m4_define([m4_prepend],
+[m4_define([$1],
+ [$2]m4_ifdef([$1], [[$3]m4_builtin([defn], [$1])]))])
# m4_append_uniq(MACRO-NAME, STRING, [SEPARATOR], [IF-UNIQ], [IF-DUP])
-# --------------------------------------------------------------------
-# Like `m4_append', but append only if not yet present. Additionally,
-# expand IF-UNIQ if STRING was appended, or IF-DUP if STRING was already
-# present. Also, warn if SEPARATOR is not empty and occurs within STRING,
-# as the algorithm no longer guarantees uniqueness.
+# m4_prepend_uniq(MACRO-NAME, STRING, [SEPARATOR], [IF-UNIQ], [IF-DUP])
+# ---------------------------------------------------------------------
+# Like `m4_append'/`m4_prepend', but add STRING only if not yet present.
+# Additionally, expand IF-UNIQ if STRING was appended, or IF-DUP if STRING
+# was already present. Also, warn if SEPARATOR is not empty and occurs
+# within STRING, as the algorithm no longer guarantees uniqueness.
+#
+# Note that while m4_append can be O(n log n) (depending on whether the
+# underlying M4 implementation), m4_append_uniq is inherently O(n^2)
+# because each append operation searches the entire string.
m4_define([m4_append_uniq],
-[m4_ifval([$3], [m4_if(m4_index([$2], [$3]), [-1], [],
+[_m4_grow_uniq([m4_append], $@)])
+m4_define([m4_prepend_uniq],
+[_m4_grow_uniq([m4_prepend], $@)])
+
+# _m4_grow_uniq(HOW, MACRO-NAME, STRING, [SEP], [IF-UNIQ], [IF-DUP])
+# ------------------------------------------------------------------
+# Shared implementation of m4_append_uniq and m4_prepend_uniq. HOW is
+# used to distinguish where the STRING will be added.
+m4_define([_m4_grow_uniq],
+[m4_ifval([$4], [m4_if(m4_index([$3], [$4]), [-1], [],
[m4_warn([syntax],
- [$0: `$2' contains `$3'])])])_$0($@)])
-m4_define([_m4_append_uniq],
-[m4_ifdef([$1],
- [m4_if(m4_index([$3]m4_builtin([defn], [$1])[$3], [$3$2$3]), [-1],
- [m4_append([$1], [$2], [$3])$4], [$5])],
- [m4_append([$1], [$2], [$3])$4])])
+ [$1_uniq: `$3' contains `$4'])])])]$0_1($@))
+m4_define([_m4_grow_uniq_1],
+[m4_ifdef([$2],
+ [m4_if(m4_index([$4]m4_builtin([defn], [$2])[$4], [$4$3$4]), [-1],
+ [$1([$2], [$3], [$4])$5], [$6])],
+ [m4_define([$2], [$3])$5])])
# m4_append_uniq_w(MACRO-NAME, STRINGS)
-# -------------------------------------
+# m4_prepend_uniq_w(MACRO-NAME, STRINGS)
+# --------------------------------------
# For each of the words in the whitespace separated list STRINGS, append
# only the unique strings to the definition of MACRO-NAME.
#
# Avoid overhead of m4_defn by using m4_builtin.
m4_define([m4_append_uniq_w],
[m4_foreach_w([m4_Word], [$2],
- [_m4_append_uniq([$1], m4_builtin([defn], [m4_Word]), [ ])])])
+ [_m4_grow_uniq_1([m4_append], [$1],
+ m4_builtin([defn], [m4_Word]), [ ])])])
+m4_define([m4_prepend_uniq_w],
+[m4_foreach_w([m4_Word], [$2],
+ [_m4_grow_uniq_1([m4_prepend], [$1],
+ m4_builtin([defn], [m4_Word]), [ ])])])
# m4_text_wrap(STRING, [PREFIX], [FIRST-PREFIX], [WIDTH])
# m4_PACKAGE_STRING
# m4_PACKAGE_BUGREPORT
# --------------------
-m4_include([m4sugar/version.m4])
+# If m4sugar/version.m4 is present, then define version strings. This
+# file is optional, provided by Autoconf but absent in Bison.
+m4_sinclude([m4sugar/version.m4])
# m4_version_prereq(VERSION, [IF-OK], [IF-NOT = FAIL])
AT_CLEANUP
+## ------------ ##
+## m4_prepend. ##
+## ------------ ##
+
+AT_SETUP([m4@&t@_prepend])
+
+AT_CHECK_M4SUGAR_TEXT(
+[[m4_define([active], [ACTIVE])dnl
+m4_prepend([sentence], [This is an])dnl
+m4_prepend([sentence], [ active ])dnl
+m4_prepend([sentence], [symbol.])dnl
+sentence
+m4_undefine([active])dnl
+sentence
+m4_define([active], [ACTIVE])dnl
+m4_prepend([hooks], [m4_define([act1], [act2])])dnl
+m4_prepend([hooks], [m4_define([act2], [active])])dnl
+m4_undefine([active])dnl
+act1
+hooks
+act1
+dnl Test for bug fixed in 2.62 when separator is active.
+m4_define([a], [A])dnl
+m4_prepend_uniq([foo], [-], [a])dnl
+m4_prepend_uniq([foo], [-], [a])dnl
+m4_prepend_uniq([bar], [-], [a])dnl
+m4_prepend_uniq([bar], [~], [a])dnl
+m4_prepend_uniq([bar], [-], [a])dnl
+m4_defn([foo])
+m4_defn([bar])
+foo
+bar
+m4_prepend_uniq([blah], [one], [, ], [new], [existing])
+m4_prepend_uniq([blah], [two], [, ], [new], [existing])
+m4_prepend_uniq([blah], [two], [, ], [new], [existing])
+m4_prepend_uniq([blah], [three], [, ], [new], [existing])
+m4_prepend([blah], [two], [, ])dnl
+blah
+m4_dquote(blah)
+m4_prepend([list], [one], [[, ]])dnl
+m4_prepend([list], [two], [[, ]])dnl
+m4_prepend([list], [three], [[, ]])dnl
+list
+m4_dquote(list)
+m4_prepend_uniq_w([numbers], [1 1 2])dnl
+m4_prepend_uniq_w([numbers], [ 2 3 ])dnl
+numbers
+]],
+[[symbol. ACTIVE This is an
+symbol. active This is an
+act1
+
+active
+-
+~a-
+-
+~A-
+new
+new
+existing
+new
+two, three, two, one
+[two],[three],[two],[one]
+three, two, one
+[three, two, one]
+3 2 1
+]])
+
+AT_DATA_M4SUGAR([script.4s],
+[[m4_prepend_uniq([str], [a], [ ])
+m4_prepend_uniq([str], [a b], [ ])
+m4_divert([0])dnl
+str
+]])
+
+AT_CHECK_M4SUGAR([-o-], 0, [[a b a
+]], [[script.4s:2: warning: m4@&t@_prepend_uniq: `a b' contains ` '
+]])
+
+AT_CLEANUP
+
+
## --------- ##
## m4_join. ##
## --------- ##