2008-11-20 Eric Blake <ebb9@byu.net>
+ Improve m4_expand robustness, part 2.
+ * lib/m4sugar/m4sugar.m4 (m4_expand): Support unterminated
+ comments, by wrapping old implementation...
+ (_m4_expand): ...as this, and renaming old core...
+ (_m4_expand_): ...to this.
+ (m4_text_box): Use lighter-weight _m4_expand.
+ * lib/m4sugar/m4sh.m4 (_AS_DETECT_EXPAND)
+ (_AS_DETECT_BETTER_SHELL, AS_FUNCTION_DESCRIBE): Likewise.
+ * lib/autotest/general.m4 (AT_KEYWORDS): Likewise.
+ * tests/m4sugar.at (m4@&t@_expand): Enhance test.
+ * tests/autotest.at (AT_CHECK_AT_TITLE_CHAR): Likewise.
+ * doc/autoconf.texi (Evaluation Macros) <m4_expand>: Mention new
+ functionality.
+
Improve m4_expand robustness, part 1.
* lib/m4sugar/m4sugar.m4 (_m4_expand): Tolerate unquoted
unbalanced `)'.
Return the expansion of @var{arg} as a quoted string. Whereas
@code{m4_quote} is designed to collect expanded text into a single
argument, @code{m4_expand} is designed to perform one level of expansion
-on quoted text. The distinction is in the treatment of whitespace
+on quoted text. One distinction is in the treatment of whitespace
following a comma in the original @var{arg}. Any time multiple
arguments are collected into one with @code{m4_quote}, the M4 argument
collection rules discard the whitespace. However, with @code{m4_expand},
whitespace is preserved, even after the expansion of macros contained in
-@var{arg}.
+@var{arg}. Additionally, @code{m4_expand} is able to expand text that
+would involve an unterminated comment, whereas expanding that same text
+as the argument to @code{m4_quote} runs into difficulty in finding the
+end of the argument.
@example
m4_define([active], [ACT, IVE])dnl
@result{}ACT, IVE,ACT, IVE
m4_expand([active2, active2])
@result{}ACT, IVE, ACT, IVE
+m4_expand([# m4_echo])
+@result{}# m4_echo
+m4_quote(# m4_echo)
+)
+@result{}# m4_echo)
+@result{}
@end example
Note that @code{m4_expand} cannot handle an @var{arg} that expands to
literal unbalanced quotes, but that quadrigraphs can be used when
-unbalanced output is necessary. Likewise, unbalanced parentheses must
+unbalanced output is necessary. Likewise, unbalanced parentheses should
be supplied with double quoting or a quadrigraph.
@example
# Since the -k option is case-insensitive, the list is stored in lower case
# to avoid duplicates that differ only by case.
_AT_DEFINE_SETUP([AT_KEYWORDS],
-[m4_append_uniq_w([AT_keywords], m4_tolower(m4_dquote(m4_expand([$1]))))])
+[m4_append_uniq_w([AT_keywords], m4_tolower(m4_dquote(_m4_expand([$1
+]))))])
# AT_CAPTURE_FILE(FILE)
# we must piece-meal the assignment of VAR such that $LINENO expansion
# occurs in a single line.
m4_define([_AS_DETECT_EXPAND],
-[$1="m4_bpatsubst(m4_dquote(AS_ESCAPE(m4_expand(m4_set_contents([$2], [
+[$1="m4_bpatsubst(m4_dquote(AS_ESCAPE(_m4_expand(m4_set_contents([$2], [
])))), [\\\$LINENO\(.*\)$], [";$1=$$1$LINENO;$1=$$1"\1])"])
[m4_set_map([_AS_DETECT_SUGGESTED_BODY], [_AS_DETECT_SUGGESTED_PRUNE])]dnl
[m4_pushdef([AS_EXIT], [exit m4_default([$1], 1)])]dnl
[if test "x$CONFIG_SHELL" = x; then
- as_bourne_compatible="AS_ESCAPE(m4_expand([_AS_BOURNE_COMPATIBLE]))"
+ as_bourne_compatible="AS_ESCAPE(_m4_expand([_AS_BOURNE_COMPATIBLE]))"
_AS_DETECT_EXPAND([as_required], [_AS_DETECT_REQUIRED_BODY])
_AS_DETECT_EXPAND([as_suggested], [_AS_DETECT_SUGGESTED_BODY])
AS_IF([_AS_RUN(["$as_required"])],
m4_define([AS_FUNCTION_DESCRIBE],
[@%:@ $1[]m4_ifval([$2], [ $2])
@%:@ m4_translit(m4_format([%*s],
- m4_qlen(m4_expand([$1[]m4_ifval([$2], [ $2])])), []),
- [ ], [-])
+ m4_decr(m4_qlen(_m4_expand([$1[]m4_ifval([$2], [ $2])
+]))), []), [ ], [-])
m4_text_wrap([$3], [@%:@ ], [], [$4])])
# m4_expand(ARG)
-# --------------
+# _m4_expand(ARG)
+# ---------------
# Return the expansion of ARG as a single string. Unlike
# m4_quote($1), this preserves whitespace following single-quoted
# commas that appear within ARG. It also deals with shell case
# something with balanced quotes (use quadrigraphs to get around
# this), and should not contain the unlikely delimiters -=<{( or
# )}>=-. It is possible to have unbalanced quoted `(' or `)', as well
-# as unbalanced unquoted `)'.
+# as unbalanced unquoted `)'. m4_expand can handle unterminated
+# comments or dnl on the final line, at the expense of speed, while
+# _m4_expand is faster but must be given a terminated expansion.
#
# Exploit that extra unquoted () will group unquoted commas and the
# following whitespace. m4_bpatsubst can't handle newlines inside $1,
# this time with one more `(' in the second argument and in the
# open-quote delimiter. We must also ignore the slop from the
# previous try. The final macro is thus half line-noise, half art.
-m4_define([m4_expand], [_$0([$1], [(], -=<{($1)}>=-, [}>=-])])
+m4_define([m4_expand], [m4_chomp(_$0([$1
+]))])
+
+m4_define([_m4_expand], [$0_([$1], [(], -=<{($1)}>=-, [}>=-])])
-m4_define([_m4_expand],
+m4_define([_m4_expand_],
[m4_if([$4], [}>=-],
[m4_changequote([-=<{$2], [)}>=-])$3m4_changequote([, ])],
[$0([$1], [($2], -=<{($2$1)}>=-, [}>=-])m4_ignore$2])])
# will post-process.
m4_define([m4_text_box],
[m4_pushdef([m4_Border],
- m4_translit(m4_format([%*s], m4_qlen(m4_expand([$1])), []),
- [ ], m4_default_quoted([$2], [-])))dnl
-[##] m4_Border [##]
+ m4_translit(m4_format([%*s], m4_decr(m4_qlen(_m4_expand([$1
+]))), []), [ ], m4_default_quoted([$2], [-])))]dnl
+[[##] m4_Border [##]
[##] $1 [##]
[##] m4_Border [##]_m4_popdef([m4_Border])])
AT_CHECK_AT_TITLE_CHAR([Brackets], [[[]]], [[]])
AT_CHECK_AT_TITLE_CHAR([Left bracket], [@<@&t@:@], [@<:@])
AT_CHECK_AT_TITLE_CHAR([Right bracket], [@:@&t@>@], [@:>@])
-AT_CHECK_AT_TITLE_CHAR([Pound], [[#]], [#])
-AT_CHECK_AT_TITLE_CHAR([Quoted comma],[[,]], [,])
-AT_CHECK_AT_TITLE_CHAR([Comma], [,], [,])
+AT_CHECK_AT_TITLE_CHAR([Quoted pound], [[#]], [#])
+AT_CHECK_AT_TITLE_CHAR([Pound], [#])
+AT_CHECK_AT_TITLE_CHAR([Quoted comma], [[,]], [,])
+AT_CHECK_AT_TITLE_CHAR([Comma], [,])
dnl this test also hits quadrigraphs for ()
AT_CHECK_AT_TITLE_CHAR([Parentheses], [(@{:@)@:}@], [(())])
AT_CHECK_AT_TITLE_CHAR([Left paren], [[(]], [(])
case d in
*) echo active, ;;
esac])
+dnl unterminated comment/dnl
+m4_expand([active # active])
+m4_expand([a
+dnl])
+m4_expand([a
+-dnl])
]],
[[#active
ACTIVE
case d in
*) echo ACTIVE, ;;
esac
+ACTIVE # active
+a
+a
+-
]])
AT_CLEANUP