summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2007-12-06 13:33:40 -0700
committerEric Blake <ebb9@byu.net>2008-04-09 11:41:12 -0600
commit32d4bf4d447e0e252c809897b795b57b3bfd74de (patch)
tree90486ae1d3ef1dab338b5066f743fc27cc5c7498
parentf7f45337fa1bfba9512841e8d3d2251359944681 (diff)
downloadm4-32d4bf4d447e0e252c809897b795b57b3bfd74de.tar.gz
Stage21: $@ concatenates builtins, m4wrap takes builtins
-rw-r--r--.cvsignore3
-rw-r--r--.gitignore3
-rw-r--r--GNUmakefile58
-rw-r--r--Makefile.am17
-rw-r--r--cfg.mk37
-rw-r--r--configure.ac15
-rw-r--r--doc/m4.texinfo120
-rw-r--r--m4/gnulib-cache.m44
-rw-r--r--maint.mk (renamed from Makefile.maint)0
-rw-r--r--src/builtin.c26
-rw-r--r--src/debug.c2
-rw-r--r--src/input.c203
-rw-r--r--src/m4.h12
-rw-r--r--src/macro.c86
14 files changed, 306 insertions, 280 deletions
diff --git a/.cvsignore b/.cvsignore
index e24f01a2..493776c5 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1,5 +1,7 @@
*.tar.bz2
*.tar.gz
+.tarball-version
+.version
aclocal.m4
autom4te.cache
build-aux
@@ -15,6 +17,7 @@ configure.lineno
COPYING
depcomp
gendocs.sh
+GNUmakefile
gnupload
INSTALL
install-sh
diff --git a/.gitignore b/.gitignore
index 2b4139a8..fae2fd39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
*.rej
*.tar.bz2
*.tar.gz
+.tarball-version
+.version
aclocal.m4
autom4te.cache
build-aux
@@ -19,6 +21,7 @@ COPYING
CVS
depcomp
gendocs.sh
+GNUmakefile
gnupload
INSTALL
install-sh
diff --git a/GNUmakefile b/GNUmakefile
deleted file mode 100644
index bcdcc6cc..00000000
--- a/GNUmakefile
+++ /dev/null
@@ -1,58 +0,0 @@
-# Having a separate GNUmakefile lets me `include' both Makefile.maint
-# and Makefile.
-# This makefile is used only if you run GNU Make.
-# It is necessary if you want to build targets usually of interest
-# only to the maintainer.
-
-# Copyright (C) 2001, 2003, 2006, 2007 Free Software Foundation, Inc.
-#
-# This file is part of GNU M4.
-#
-# GNU M4 is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# GNU M4 is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-# Systems where /bin/sh is not the default shell need this. The $(shell)
-# command below won't work with e.g. stock DOS/Windows shells.
-ifeq ($(wildcard /bin/s[h]),/bin/sh)
-SHELL = /bin/sh
-else
-# will be used only with the next shell-test line, then overwritten
-# by a configured-in value
-SHELL = sh
-endif
-
-have-Makefile := $(shell test -f Makefile && echo yes)
-
-# If the user runs GNU make but has not yet run ./configure,
-# give them a diagnostic.
-ifeq ($(have-Makefile),yes)
-
-# Make tar archive easier to reproduce.
-export TAR_OPTIONS = --owner=0 --group=0 --numeric-owner
-
-include Makefile
-include $(srcdir)/Makefile.maint
-
-else
-
-all:
- @echo There seems to be no Makefile in this directory. 1>&2
- @echo "You must run ./configure before running \`make'." 1>&2
- @exit 1
-
-endif
-
-# Tell version 3.79 and up of GNU make to not build goals in this
-# directory in parallel. This is necessary in case someone tries to
-# build multiple targets on one command line.
-.NOTPARALLEL:
diff --git a/Makefile.am b/Makefile.am
index f679949e..a67b4159 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
## Makefile.am - template for generating Makefile via Automake
##
-## Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software
-## Foundation, Inc.
+## Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008 Free
+## Software Foundation, Inc.
##
## This file is part of GNU M4.
##
@@ -21,8 +21,8 @@
## Written by Gary V. Vaughan <gary@gnu.org>
SUBDIRS = . examples lib src doc tests checks
-EXTRA_DIST = bootstrap c-boxes.el GNUmakefile Makefile.maint \
- m4/gnulib-cache.m4
+EXTRA_DIST = bootstrap c-boxes.el cfg.mk maint.mk \
+ .version m4/gnulib-cache.m4
DISTCLEANFILES = stamp-h
## maintainer-clean should remove as much as possible that ./bootstrap can
## recreate. In the m4 directory, keep only gnulib-cache.m4.
@@ -33,3 +33,12 @@ MAINTAINERCLEANFILES = COPYING INSTALL Makefile.in aclocal.m4 \
ACLOCAL_AMFLAGS = -I m4
DISTCHECK_CONFIGURE_FLAGS = --enable-changeword
+
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+ echo $(VERSION) > $@-t && mv $@-t $@
+
+# Arrange so that .tarball-version appears only in the distribution
+# tarball, and never in a checked-out repository.
+dist-hook:
+ echo $(VERSION) > $(distdir)/.tarball-version
diff --git a/cfg.mk b/cfg.mk
new file mode 100644
index 00000000..99abe29f
--- /dev/null
+++ b/cfg.mk
@@ -0,0 +1,37 @@
+# Customize maint.mk. -*- makefile -*-
+# Copyright (C) 2003-2008 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Use alpha.gnu.org for alpha and beta releases.
+# Use ftp.gnu.org for major releases.
+gnu_ftp_host-alpha = alpha.gnu.org
+gnu_ftp_host-beta = alpha.gnu.org
+gnu_ftp_host-major = ftp.gnu.org
+gnu_rel_host = $(gnu_ftp_host-$(RELEASE_TYPE))
+
+url_dir_list = ftp://$(gnu_rel_host)/gnu/m4
+
+# The GnuPG ID of the key used to sign the tarballs.
+gpg_key_ID = F4850180
+
+# Tests not to run as part of "make distcheck".
+# Exclude changelog-check here so that there's less churn in ChangeLog
+# files -- otherwise, you'd need to have the upcoming version number
+# at the top of the file for each `make distcheck' run.
+local-checks-to-skip = changelog-check
+
+# The local directory containing the checked-out copy of gnulib used in this
+# release. Used solely to get gnulib's SHA1 for the "announcement" target.
+gnulib_dir = $(srcdir)/../gnulib
diff --git a/configure.ac b/configure.ac
index 630c40b5..ea4e130c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# Configure template for GNU M4. -*-Autoconf-*-
-# Copyright (C) 1991, 1993, 1994, 2004, 2005, 2006, 2007 Free Software
-# Foundation, Inc.
+# Copyright (C) 1991, 1993, 1994, 2004, 2005, 2006, 2007, 2008 Free
+# Software Foundation, Inc.
#
# This file is part of GNU M4.
#
@@ -18,23 +18,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ([2.60])
-AC_INIT([GNU M4], [1.4.10a], [bug-m4@gnu.org])
+AC_INIT([GNU M4], m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+ [bug-m4@gnu.org])
AC_CONFIG_AUX_DIR([build-aux])
-AM_INIT_AUTOMAKE([1.9.6 dist-bzip2 gnu])
+AM_INIT_AUTOMAKE([1.10.1 dist-bzip2 dist-lzma gnu])
m4_pattern_forbid([^M4_[A-Z]])
AC_CONFIG_SRCDIR([src/m4.h])
AC_CONFIG_HEADERS([lib/config.h:lib/config.hin])
-AC_CANONICAL_BUILD
-AC_CANONICAL_HOST
AC_PROG_CC
M4_EARLY
-AC_SYS_LARGEFILE
-AC_CHECK_HEADERS_ONCE([limits.h siginfo.h sys/wait.h])
+AC_CHECK_HEADERS_ONCE([siginfo.h sys/wait.h])
AC_CHECK_TYPES([siginfo_t], [], [],
[[#include <signal.h>
#if HAVE_SIGINFO_H
@@ -52,7 +50,6 @@ AC_CHECK_MEMBERS([stack_t.ss_sp], [], [],
]])
AC_TYPE_SIGNAL
-AC_TYPE_SIZE_T
AC_CHECK_FUNCS_ONCE([sigaction sigaltstack sigstack sigvec strerror])
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 00cff97a..98eba85d 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -394,9 +394,11 @@ addressed some long standing bugs in the venerable 1.4 release. Then in
released 1.4.3 and 1.4.4. And in 2006, Eric Blake joined the team and
prepared patches for the release of 1.4.5, 1.4.6, 1.4.7, and 1.4.8.
More bug fixes were incorporated in 2007, with releases 1.4.9 and
-1.4.10. In 2008, Eric additionally rewrote the scanning engine to
-reduce recursive evaluation from quadratic to linear complexity for
-1.4.11. The 1.4.x branch remains open for bug fixes.
+1.4.10, closing the series with 1.4.11 in 2008.
+
+Additionally, in 2008, Eric rewrote the scanning engine to reduce
+recursive evaluation from quadratic to linear complexity, released as M4
+1.6. The 1.x branch series remains open for bug fixes.
Meanwhile, development has continued on new features for @code{m4}, such
as dynamic module loading and additional builtins. When complete,
@@ -2217,7 +2219,7 @@ defn([l], [r])
Using @code{defn} to generate special tokens for builtin macros will
generate a warning in contexts where a macro name is expected. But in
contexts that operate on text, the builtin token is just silently
-converted to an empty string. As of M4 1.4.11, expansion of user macros
+converted to an empty string. As of M4 1.6, expansion of user macros
will also preserve builtin tokens. However, any use of builtin tokens
outside of the second argument to @code{define} and @code{pushdef} is
generally not portable, since earlier @acronym{GNU} M4 versions, as well
@@ -2278,10 +2280,11 @@ bar
@result{}0
@end example
-Also note that @code{defn} with multiple arguments can only join text
-macros, not builtins. Likewise, when collecting macro arguments, a
-builtin token is preserved only when it occurs in isolation. A future
-version of @acronym{GNU} M4 may lift these restrictions.
+Also note that as of M4 1.6, @code{defn} with multiple arguments can
+join text with builtin tokens. However, when collecting macro
+arguments, a builtin token is preserved only when it occurs in
+isolation. A future version of @acronym{GNU} M4 may lift this
+restriction.
@example
$ @kbd{m4 -d}
@@ -2290,13 +2293,12 @@ define(`a', `A')define(`AA', `b')
traceon(`defn', `define')
@result{}
defn(`a', `divnum', `a')
-@error{}m4:stdin:3: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4trace: -1- defn(`a', `divnum', `a') -> ``A'`A''
+@error{}m4trace: -1- defn(`a', `divnum', `a') -> ``A'<divnum>`A''
@result{}AA
define(`mydivnum', defn(`divnum', `divnum'))mydivnum
-@error{}m4:stdin:4: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4:stdin:4: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4trace: -2- defn(`divnum', `divnum')
+@error{}m4trace: -2- defn(`divnum', `divnum') -> `<divnum><divnum>'
+@error{}m4:stdin:4: Warning: define: cannot concatenate builtin `divnum'
+@error{}m4:stdin:4: Warning: define: cannot concatenate builtin `divnum'
@error{}m4trace: -1- define(`mydivnum', `')
@result{}
traceoff(`defn', `define')
@@ -2314,10 +2316,10 @@ define(`mydivnum', `a'defn(`divnum'))mydivnum
define(`q', ``$@@'')
@result{}
define(`foo', q(`a', defn(`divnum')))foo
-@error{}m4:stdin:10: Warning: define: cannot quote builtin
-@result{}a,
+@error{}m4:stdin:10: Warning: define: cannot concatenate builtins
+@result{}foo
ifdef(`foo', `yes', `no')
-@result{}yes
+@result{}no
@end example
@node Pushdef
@@ -2665,7 +2667,7 @@ ifdef(`no_such_macro', `yes', `no', `extra argument')
@result{}no
@end example
-As of M4 1.4.11, @code{ifdef} transparently handles builtin tokens
+As of M4 1.6, @code{ifdef} transparently handles builtin tokens
generated by @code{defn} (@pxref{Defn}) that occur in either
@var{string}, although a warning is issued for invalid macro names.
@@ -2781,7 +2783,7 @@ ifelse(`foo', `bar', `3', `gnu', `gnats', `6', `7', `8')
@result{}7
@end example
-As of M4 1.4.11, @code{ifelse} transparently handles builtin tokens
+As of M4 1.6, @code{ifelse} transparently handles builtin tokens
generated by @code{defn} (@pxref{Defn}). Because of this, it is always
safe to compare two macro definitions, without worrying whether the
macro might be a builtin.
@@ -2857,8 +2859,8 @@ ifelse(`-01234567890123456789', `-'e(long)`-', `yes', `no')
@result{}no
@end example
-@comment It would be nice to pass builtin tokens through m4wrap, as well
-@comment as allowing concatenation of builtins in ifelse and user macros.
+@comment It would be nice to allow concatenation of builtins without
+@comment using $@ handling.
@example
define(`e', `$@@')define(`q', ``$@@'')define(`u', `$*')
@result{}
@@ -2878,33 +2880,25 @@ cmp(`q(defn(`defn'))', `q(`<defn>')')-fixme
cmp(`q(defn(`defn'))', ``'')-fixme
@error{}m4:stdin:7: Warning: ifelse: cannot quote builtin
@result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))')-fixme
-@error{}m4:stdin:8: Warning: ifelse: cannot quote builtin
-@error{}m4:stdin:8: Warning: ifelse: cannot quote builtin
-@result{}yes-fixme
-cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')')-fixme
-@error{}m4:stdin:9: Warning: ifelse: cannot quote builtin
-@result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', ```1',`2',<defn>'')-fixme
-@error{}m4:stdin:10: Warning: ifelse: cannot quote builtin
-@result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''')-fixme
-@error{}m4:stdin:11: Warning: ifelse: cannot quote builtin
-@result{}yes-fixme
+cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))')
+@result{}yes
+cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')')
+@result{}no
+cmp(`q(`1', `2', defn(`defn'))', ```1',`2',<defn>'')
+@result{}no
+cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''')
+@result{}no
define(`cat', `$1`'ifelse(`$#', `1', `', `$0(shift($@@))')')
@result{}
-cat(`define(`foo',', defn(`divnum'), `)foo')-fixme
-@error{}m4:stdin:13: Warning: ifelse: cannot quote builtin
-@result{}-fixme
-cat(e(`define(`bar',', defn(`divnum'), `)bar'))-fixme
-@error{}m4:stdin:14: Warning: ifelse: cannot quote builtin
-@result{}-fixme
-m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`)-fixme
+cat(`define(`foo',', defn(`divnum'), `)foo')
+@result{}0
+cat(e(`define(`bar',', defn(`divnum'), `)bar'))
+@result{}0
+m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`)
')
-@error{}m4:stdin:15: Warning: m4wrap: cannot quote builtin
@result{}
^D
-@result{}-fixme
+@result{}0
@end example
@end ignore
@@ -3804,7 +3798,7 @@ echo(`1', `long string')
@error{}m4trace: -1- echo(`1', `long s...') -> ``1',`l...'
@result{}1,long string
indir(`echo', defn(`changequote'))
-@error{}m4trace: -2- defn(`change...')
+@error{}m4trace: -2- defn(`change...') -> `<changequote>'
@error{}m4trace: -1- indir(`echo', <changequote>) -> ``<changequote>''
@result{}
@end example
@@ -4601,7 +4595,7 @@ not if @code{m4exit} is used to exit @code{m4}.
It is safe to call @code{m4wrap} from wrapped text, where all the
recursively wrapped text is deferred until the current wrapped text is
-exhausted. As of M4 1.4.11, when @code{m4wrap} is not used recursively,
+exhausted. As of M4 1.6, when @code{m4wrap} is not used recursively,
the saved pieces of text are reread in the same order in which they were
saved (FIFO---first in, first out), as required by @acronym{POSIX}.
@@ -4716,6 +4710,24 @@ m4wrap(`m4wrap(`)')len(abc')
@error{}m4:stdin:1: len: end of file in argument list
@end example
+As of M4 1.6, @code{m4wrap} transparently handles builtin tokens
+generated by @code{defn} (@pxref{Defn}). However, for portability, it
+is better to defer the evaluation of @code{defn} along with the rest of
+the wrapped text, as is done for @code{foo} in the example below, rather
+than computing the builtin token up front, as is done for @code{bar}.
+
+@example
+m4wrap(`define(`foo', defn(`divnum'))foo
+')
+@result{}
+m4wrap(`define(`bar', ')m4wrap(defn(`divnum'))m4wrap(`)bar
+')
+@result{}
+^D
+@result{}0
+@result{}0
+@end example
+
@node File Inclusion
@chapter File inclusion
@@ -4934,6 +4946,20 @@ divert(`1')
f
m4exit
@end example
+
+@comment Catch regression in 1.4.10 with spilled diversions.
+
+@example
+ifdef(`__unix__', ,
+ `errprint(` skipping: syscmd does not have unix semantics
+')m4exit(`77')')dnl
+changequote(`[', `]')dnl
+syscmd([echo 'divert(1)hi
+format(%1000000d, 1)' | m4 | sed 1q])dnl
+@result{}hi
+sysval
+@result{}0
+@end example
@end ignore
Diversions make it possible to generate output in a different order than
@@ -7064,7 +7090,7 @@ of @samp{-} on the command line.
@item
@acronym{POSIX} requires @code{m4wrap} (@pxref{M4wrap}) to act in FIFO
(first-in, first-out) order, and most other implementations obey this.
-However, versions of @acronym{GNU} @code{m4} earlier than 1.4.11 used
+However, versions of @acronym{GNU} @code{m4} earlier than 1.6 used
LIFO order. Furthermore, @acronym{POSIX} states that only the first
argument to @code{m4wrap} is saved for later evaluation, but
@acronym{GNU} @code{m4} saves and processes all arguments, with output
@@ -7526,14 +7552,14 @@ foreachq(`x', ``1', `2', `3', `4'', `x
@result{}4
@end example
-Prior to M4 1.4.11, every instance of @samp{$@@} was rescanned as it was
+Prior to M4 1.6, every instance of @samp{$@@} was rescanned as it was
encountered. Thus, the @file{foreachq3.m4} alternative used much less
memory than @file{foreachq2.m4}, and executed as much as 10% faster,
since each iteration encountered fewer @samp{$@@}. However, the
implementation of rescanning every byte in @samp{$@@} was quadratic in
the number of bytes scanned (for example, making the broken version in
@file{foreachq.m4} cubic, rather than quadratic, in behavior). Once the
-underlying M4 implementation was improved in 1.4.11 to reuse results of
+underlying M4 implementation was improved in 1.6 to reuse results of
previous scans, both styles of @code{foreachq} become linear in the
number of bytes scanned, and the difference in timing is no longer
noticeable; in fact, after the change, the @file{foreachq2.m4} version
diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4
index be74a73c..9f094f51 100644
--- a/m4/gnulib-cache.m4
+++ b/m4/gnulib-cache.m4
@@ -15,11 +15,11 @@
# Specification in the form of a command-line invocation:
-# gnulib-tool --import --dir=. --local-dir=local --lib=libm4 --source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests --no-libtool --macro-prefix=M4 announce-gen assert avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer freadptr freadseek fseeko gendocs getopt git-version-gen gnupload gpl-3.0 intprops memchr2 memmem mkstemp obstack quote regex stdbool stdint stdlib-safer strtod strtol unlocked-io vasnprintf-posix verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix
+# gnulib-tool --import --dir=. --local-dir=local --lib=libm4 --source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests --no-libtool --macro-prefix=M4 announce-gen assert avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer freadptr freadseek fseeko gendocs getopt git-version-gen gnumakefile gnupload gpl-3.0 intprops memchr2 memmem mkstemp obstack quote regex stdbool stdint stdlib-safer strtod strtol unlocked-io vasnprintf-posix verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([local])
-gl_MODULES([announce-gen assert avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer freadptr freadseek fseeko gendocs getopt git-version-gen gnupload gpl-3.0 intprops memchr2 memmem mkstemp obstack quote regex stdbool stdint stdlib-safer strtod strtol unlocked-io vasnprintf-posix verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix])
+gl_MODULES([announce-gen assert avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer freadptr freadseek fseeko gendocs getopt git-version-gen gnumakefile gnupload gpl-3.0 intprops memchr2 memmem mkstemp obstack quote regex stdbool stdint stdlib-safer strtod strtol unlocked-io vasnprintf-posix verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix])
gl_AVOID([])
gl_SOURCE_BASE([lib])
gl_M4_BASE([m4])
diff --git a/Makefile.maint b/maint.mk
index c07aee12..c07aee12 100644
--- a/Makefile.maint
+++ b/maint.mk
diff --git a/src/builtin.c b/src/builtin.c
index c41aa544..497e2b91 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -123,7 +123,7 @@ builtin_tab[] =
{ "indir", true, true, true, m4_indir },
{ "len", false, false, true, m4_len },
{ "m4exit", false, false, false, m4_m4exit },
- { "m4wrap", false, false, true, m4_m4wrap },
+ { "m4wrap", false, true, true, m4_m4wrap },
{ "maketemp", false, false, true, m4_maketemp },
{ "mkstemp", false, false, true, m4_mkstemp },
{ "patsubst", true, false, true, m4_patsubst },
@@ -201,19 +201,25 @@ find_builtin_by_name (const char *name)
/*------------------------------------------------------------------.
| Print a representation of FUNC to OBS. If FLATTEN, output QUOTES |
-| around an empty string instead. |
+| around an empty string instead; else if CHAIN, append the builtin |
+| to the chain; otherwise print the name of FUNC. |
`------------------------------------------------------------------*/
void
func_print (struct obstack *obs, const builtin *func, bool flatten,
- const string_pair *quotes)
+ token_chain **chain, const string_pair *quotes)
{
assert (func);
- if (flatten && quotes)
+ if (flatten)
{
- obstack_grow (obs, quotes->str1, quotes->len1);
- obstack_grow (obs, quotes->str2, quotes->len2);
+ if (quotes)
+ {
+ obstack_grow (obs, quotes->str1, quotes->len1);
+ obstack_grow (obs, quotes->str2, quotes->len2);
+ }
}
- else if (!flatten)
+ else if (chain)
+ append_macro (obs, func->func, NULL, chain);
+ else
{
obstack_1grow (obs, '<');
obstack_grow (obs, func->name, strlen (func->name));
@@ -1018,10 +1024,8 @@ m4_defn (struct obstack *obs, int argc, macro_arguments *argv)
m4_warn (0, me,
_("builtin `%s' requested by frozen file not found"),
ARG (i));
- else if (argc != 2)
- m4_warn (0, me, _("cannot concatenate builtin `%s'"), ARG (i));
else
- push_macro (b);
+ push_macro (obs, b);
break;
default:
@@ -1544,7 +1548,7 @@ m4_errprint (struct obstack *obs, int argc, macro_arguments *argv)
if (bad_argc (ARG (0), argc, 1, -1))
return;
- arg_print (obs, argv, 1, NULL, true, " ", NULL, false);
+ arg_print (obs, argv, 1, NULL, true, NULL, " ", NULL, false);
debug_flush_files ();
len = obstack_object_size (obs);
/* The close_stdin module makes it safe to skip checking the return
diff --git a/src/debug.c b/src/debug.c
index 737ee524..4a4e915d 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -368,7 +368,7 @@ trace_pre (const char *name, int id, macro_arguments *argv)
trace_format ("(");
arg_print (&trace, argv, 1,
(debug_level & DEBUG_TRACE_QUOTE) ? &curr_quote : NULL,
- false, ", ", &len, true);
+ false, NULL, ", ", &len, true);
trace_format (")");
}
diff --git a/src/input.c b/src/input.c
index a6fcc854..cdb7d034 100644
--- a/src/input.c
+++ b/src/input.c
@@ -72,10 +72,10 @@
/* Type of an input block. */
enum input_type
{
- INPUT_STRING, /* String resulting from macro expansion. */
- INPUT_FILE, /* File from command line or include. */
- INPUT_MACRO, /* Builtin resulting from defn. */
- INPUT_CHAIN /* FIFO chain of separate strings and $@ refs. */
+ INPUT_STRING, /* String resulting from macro expansion. */
+ INPUT_FILE, /* File from command line or include. */
+ INPUT_CHAIN, /* FIFO chain of separate strings, builtins, and $@ refs. */
+ INPUT_EOF /* Placeholder at bottom of input stack. */
};
typedef enum input_type input_type;
@@ -103,7 +103,6 @@ struct input_block
bool_bitfield advance : 1; /* Track previous start_of_input_line. */
}
u_f; /* INPUT_FILE */
- builtin_func *func; /* INPUT_MACRO */
struct
{
token_chain *chain; /* Current link in chain. */
@@ -136,15 +135,19 @@ static struct obstack *current_input;
/* Bottom of token_stack, for obstack_free. */
static void *token_bottom;
-/* Pointer to top of current_input. */
+/* Pointer to top of current_input, never NULL. */
static input_block *isp;
-/* Pointer to top of wrapup_stack. */
+/* Pointer to top of wrapup_stack, never NULL. */
static input_block *wsp;
-/* Aux. for handling split push_string (). */
+/* Auxiliary for handling split push_string (), NULL if not pushing
+ text for rescanning. */
static input_block *next;
+/* Marker at the end of the input stack. */
+static input_block input_eof = { NULL, INPUT_EOF, "", 0 };
+
/* Flag for next_char () to increment current_line. */
static bool start_of_input_line;
@@ -268,33 +271,50 @@ push_file (FILE *fp, const char *title, bool close)
isp = i;
}
-/*-----------------------------------------------------------------.
-| push_macro () pushes the builtin macro FUNC on the input stack. |
-| If next is non-NULL, this push invalidates a call to |
-| push_string_init (), whose storage is consequently released. |
-`-----------------------------------------------------------------*/
-
+/*------------------------------------------------------------------.
+| Given an obstack OBS, capture any unfinished text as a link, then |
+| append the builtin FUNC as the next link in the chain that starts |
+| at *START and ends at *END. START may be NULL if *END is |
+| non-NULL. |
+`------------------------------------------------------------------*/
void
-push_macro (builtin_func *func)
+append_macro (struct obstack *obs, builtin_func *func, token_chain **start,
+ token_chain **end)
{
- input_block *i;
-
- if (next != NULL)
- {
- obstack_free (current_input, next);
- next = NULL;
- }
+ token_chain *chain;
assert (func);
- i = (input_block *) obstack_alloc (current_input, sizeof *i);
- i->type = INPUT_MACRO;
- i->file = current_file;
- i->line = current_line;
- input_change = true;
+ make_text_link (obs, start, end);
+ chain = (token_chain *) obstack_alloc (obs, sizeof *chain);
+ if (*end)
+ (*end)->next = chain;
+ else
+ *start = chain;
+ *end = chain;
+ chain->next = NULL;
+ chain->type = CHAIN_FUNC;
+ chain->quote_age = 0;
+ chain->u.func = func;
+}
- i->u.func = func;
- i->prev = isp;
- isp = i;
+/*------------------------------------------------------------------.
+| push_macro () pushes the builtin FUNC onto the obstack OBS, which |
+| is either the input or wrapup stack. |
+`------------------------------------------------------------------*/
+
+void
+push_macro (struct obstack *obs, builtin_func *func)
+{
+ input_block *block = (obs == current_input ? next : wsp);
+ assert (block);
+ if (block->type == INPUT_STRING)
+ {
+ block->type = INPUT_CHAIN;
+ block->u.u_c.chain = block->u.u_c.end = NULL;
+ }
+ else
+ assert (block->type == INPUT_CHAIN);
+ append_macro (obs, func, &block->u.u_c.chain, &block->u.u_c.end);
}
/*--------------------------------------------------------------.
@@ -307,7 +327,7 @@ push_string_init (void)
{
/* Free any memory occupied by completely parsed strings. */
assert (next == NULL);
- while (isp && pop_input (false));
+ while (pop_input (false));
/* Reserve the next location on the obstack. */
next = (input_block *) obstack_alloc (current_input, sizeof *next);
@@ -364,7 +384,18 @@ push_token (token_data *token, int level, bool inuse)
return false;
}
}
- else if (TOKEN_DATA_TYPE (token) != TOKEN_FUNC)
+ else if (TOKEN_DATA_TYPE (token) == TOKEN_FUNC)
+ {
+ if (next->type == INPUT_STRING)
+ {
+ next->type = INPUT_CHAIN;
+ next->u.u_c.chain = next->u.u_c.end = NULL;
+ }
+ append_macro (current_input, TOKEN_DATA_FUNC (token), &next->u.u_c.chain,
+ &next->u.u_c.end);
+ return false;
+ }
+ else
{
/* For composite tokens, if argv is already in use, creating
additional references for long text segments is more
@@ -410,23 +441,15 @@ push_token (token_data *token, int level, bool inuse)
adjust_refcount (level, true);
inuse = true;
}
- else if (TOKEN_DATA_TYPE (token) == TOKEN_FUNC)
- {
- chain = (token_chain *) obstack_alloc (current_input, sizeof *chain);
- if (next->u.u_c.end)
- next->u.u_c.end->next = chain;
- else
- next->u.u_c.chain = chain;
- next->u.u_c.end = chain;
- chain->next = NULL;
- chain->type = CHAIN_FUNC;
- chain->quote_age = 0;
- chain->u.func = TOKEN_DATA_FUNC (token);
- }
while (src_chain)
{
- // TODO support func concatenation
- assert (src_chain->type != CHAIN_FUNC);
+ if (src_chain->type == CHAIN_FUNC)
+ {
+ append_macro (current_input, src_chain->u.func, &next->u.u_c.chain,
+ &next->u.u_c.end);
+ src_chain = src_chain->next;
+ continue;
+ }
if (level == -1)
{
/* Nothing to copy, since link already lives on obstack. */
@@ -529,13 +552,13 @@ push_string_finish (void)
`--------------------------------------------------------------*/
struct obstack *
-push_wrapup_init (void)
+push_wrapup_init (token_chain ***end)
{
input_block *i;
token_chain *chain;
assert (obstack_object_size (wrapup_stack) == 0);
- if (wsp)
+ if (wsp != &input_eof)
{
i = wsp;
assert (i->type == INPUT_CHAIN && i->u.u_c.end
@@ -562,6 +585,7 @@ push_wrapup_init (void)
chain->quote_age = 0;
chain->u.u_l.file = current_file;
chain->u.u_l.line = current_line;
+ *end = &i->u.u_c.end;
return wrapup_stack;
}
@@ -599,12 +623,6 @@ pop_input (bool cleanup)
return false;
break;
- case INPUT_MACRO:
- assert (!isp->u.func || !cleanup);
- if (isp->u.func)
- return false;
- break;
-
case INPUT_CHAIN:
chain = isp->u.u_c.chain;
assert (!chain || !cleanup);
@@ -661,6 +679,9 @@ pop_input (bool cleanup)
output_current_line = -1;
break;
+ case INPUT_EOF:
+ return false;
+
default:
assert (!"pop_input");
abort ();
@@ -687,7 +708,7 @@ pop_wrapup (void)
obstack_free (current_input, NULL);
free (current_input);
- if (wsp == NULL)
+ if (wsp == &input_eof)
{
/* End of the program. Free all memory even though we are about
to exit, since it makes leak detection easier. */
@@ -703,7 +724,7 @@ pop_wrapup (void)
obstack_init (wrapup_stack);
isp = wsp;
- wsp = NULL;
+ wsp = &input_eof;
input_change = true;
return true;
@@ -730,9 +751,6 @@ input_print (struct obstack *obs, const input_block *input)
obstack_grow (obs, input->file, strlen (input->file));
obstack_1grow (obs, '>');
break;
- case INPUT_MACRO:
- func_print (obs, find_builtin_by_addr (input->u.func), false, NULL);
- break;
case INPUT_CHAIN:
chain = input->u.u_c.chain;
while (chain)
@@ -746,14 +764,14 @@ input_print (struct obstack *obs, const input_block *input)
break;
case CHAIN_FUNC:
func_print (obs, find_builtin_by_addr (chain->u.func), false,
- NULL);
+ NULL, NULL);
break;
case CHAIN_ARGV:
assert (!chain->u.u_a.comma);
if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
quote_cache (NULL, chain->quote_age,
chain->u.u_a.quotes),
- chain->u.u_a.flatten, NULL, &maxlen, false))
+ chain->u.u_a.flatten, NULL, NULL, &maxlen, false))
return;
break;
default:
@@ -789,9 +807,7 @@ peek_input (bool allow_argv)
while (1)
{
- if (block == NULL)
- return CHAR_EOF;
-
+ assert (block);
switch (block->type)
{
case INPUT_STRING:
@@ -809,11 +825,6 @@ peek_input (bool allow_argv)
block->u.u_f.end = true;
break;
- case INPUT_MACRO:
- if (block->u.func)
- return CHAR_MACRO;
- break;
-
case INPUT_CHAIN:
chain = block->u.u_c.chain;
while (chain)
@@ -863,6 +874,9 @@ peek_input (bool allow_argv)
}
break;
+ case INPUT_EOF:
+ return CHAR_EOF;
+
default:
assert (!"peek_input");
abort ();
@@ -887,7 +901,7 @@ peek_input (bool allow_argv)
`-------------------------------------------------------------------*/
#define next_char(AQ, AA) \
- (isp && isp->type == INPUT_STRING && isp->u.u_s.len && !input_change \
+ (isp->type == INPUT_STRING && isp->u.u_s.len && !input_change \
? (isp->u.u_s.len--, to_uchar (*isp->u.u_s.str++)) \
: next_char_1 (AQ, AA))
@@ -899,13 +913,7 @@ next_char_1 (bool allow_quote, bool allow_argv)
while (1)
{
- if (isp == NULL)
- {
- current_file = "";
- current_line = 0;
- return CHAR_EOF;
- }
-
+ assert (isp);
if (input_change)
{
current_file = isp->file;
@@ -940,11 +948,6 @@ next_char_1 (bool allow_quote, bool allow_argv)
}
break;
- case INPUT_MACRO:
- if (isp->u.func)
- return CHAR_MACRO;
- break;
-
case INPUT_CHAIN:
chain = isp->u.u_c.chain;
while (chain)
@@ -1013,6 +1016,9 @@ next_char_1 (bool allow_quote, bool allow_argv)
}
break;
+ case INPUT_EOF:
+ return CHAR_EOF;
+
default:
assert (!"next_char_1");
abort ();
@@ -1064,28 +1070,15 @@ init_macro_token (token_data *td)
{
token_chain *chain;
- if (isp->type == INPUT_MACRO)
+ assert (isp->type == INPUT_CHAIN);
+ chain = isp->u.u_c.chain;
+ assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func);
+ if (td)
{
- assert (isp->u.func);
- if (td)
- {
- TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
- TOKEN_DATA_FUNC (td) = isp->u.func;
- }
- isp->u.func = NULL;
- }
- else
- {
- assert (isp->type == INPUT_CHAIN);
- chain = isp->u.u_c.chain;
- assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func);
- if (td)
- {
- TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
- TOKEN_DATA_FUNC (td) = chain->u.func;
- }
- chain->u.func = NULL;
+ TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
+ TOKEN_DATA_FUNC (td) = chain->u.func;
}
+ chain->u.func = NULL;
}
/*-------------------------------------------------------------------.
@@ -1282,8 +1275,8 @@ input_init (void)
obstack_init (&token_stack);
token_bottom = obstack_finish (&token_stack);
- isp = NULL;
- wsp = NULL;
+ isp = &input_eof;
+ wsp = &input_eof;
next = NULL;
start_of_input_line = false;
diff --git a/src/m4.h b/src/m4.h
index a0204c82..cdad61a6 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -393,11 +393,13 @@ void skip_line (const char *);
/* push back input */
void make_text_link (struct obstack *, token_chain **, token_chain **);
void push_file (FILE *, const char *, bool);
-void push_macro (builtin_func *);
+void append_macro (struct obstack *, builtin_func *, token_chain **,
+ token_chain **);
+void push_macro (struct obstack *, builtin_func *);
struct obstack *push_string_init (void);
bool push_token (token_data *, int, bool);
const input_block *push_string_finish (void);
-struct obstack *push_wrapup_init (void);
+struct obstack *push_wrapup_init (token_chain ***);
void push_wrapup_finish (void);
bool pop_wrapup (void);
void input_print (struct obstack *, const input_block *);
@@ -508,7 +510,8 @@ size_t arg_len (macro_arguments *, unsigned int);
builtin_func *arg_func (macro_arguments *, unsigned int);
struct obstack *arg_scratch (void);
bool arg_print (struct obstack *, macro_arguments *, unsigned int,
- const string_pair *, bool, const char *, int *, bool);
+ const string_pair *, bool, token_chain **, const char *,
+ int *, bool);
macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t,
bool, bool);
void push_arg (struct obstack *, macro_arguments *, unsigned int);
@@ -568,7 +571,8 @@ const char *ntoa (int32_t, int);
const builtin *find_builtin_by_addr (builtin_func *);
const builtin *find_builtin_by_name (const char *);
-void func_print (struct obstack *, const builtin *, bool, const string_pair *);
+void func_print (struct obstack *, const builtin *, bool, token_chain **,
+ const string_pair *);
/* File: path.c --- path search for include files. */
diff --git a/src/macro.c b/src/macro.c
index b6d8d3e4..5b99e677 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -582,8 +582,7 @@ collect_arguments (symbol *sym, struct obstack *arguments,
argv->wrapper = args.wrapper;
argv->has_ref = args.has_ref;
argv->has_func = args.has_func;
- // TODO allow funcs without crippling quote age
- if (args.quote_age != quote_age () || args.has_func)
+ if (args.quote_age != quote_age ())
argv->quote_age = 0;
argv->arraylen = args.arraylen;
return argv;
@@ -910,8 +909,6 @@ arg_type (macro_arguments *argv, unsigned int index)
type = TOKEN_TEXT;
if (type != TOKEN_TEXT)
assert (argv->has_func);
- // TODO support TOKEN_COMP meaning concatenation of builtins
- assert (type != TOKEN_COMP);
return type;
}
@@ -936,7 +933,6 @@ arg_text (macro_arguments *argv, unsigned int index)
case TOKEN_TEXT:
return TOKEN_DATA_TEXT (token);
case TOKEN_COMP:
- // TODO - concatenate functions?
chain = token->u.u_c.chain;
obs = arg_scratch ();
while (chain)
@@ -952,7 +948,7 @@ arg_text (macro_arguments *argv, unsigned int index)
quote_cache (NULL, chain->quote_age,
chain->u.u_a.quotes),
argv->flatten || chain->u.u_a.flatten, NULL, NULL,
- false);
+ NULL, false);
break;
default:
assert (!"arg_text");
@@ -983,6 +979,7 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb)
token_chain tmpb;
token_chain *ca = &tmpa;
token_chain *cb = &tmpb;
+ token_chain *chain;
struct obstack *obs = arg_scratch ();
/* Quick tests. */
@@ -1041,30 +1038,34 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb)
{
if (ca->type == CHAIN_ARGV)
{
- tmpa.next = ca->next;
+ tmpa.next = NULL;
tmpa.type = CHAIN_STR;
- // TODO support $@ with funcs
- assert (!ca->u.u_a.has_func || argv->flatten || ca->u.u_a.flatten);
+ tmpa.u.u_s.str = NULL;
+ tmpa.u.u_s.len = 0;
+ chain = &tmpa;
arg_print (obs, ca->u.u_a.argv, ca->u.u_a.index,
quote_cache (NULL, ca->quote_age, ca->u.u_a.quotes),
- argv->flatten || ca->u.u_a.flatten, NULL, NULL, false);
- tmpa.u.u_s.len = obstack_object_size (obs);
- tmpa.u.u_s.str = (char *) obstack_finish (obs);
- ca = &tmpa;
+ argv->flatten || ca->u.u_a.flatten, &chain, NULL, NULL,
+ false);
+ assert (obstack_object_size (obs) == 0 && chain != &tmpa);
+ chain->next = ca->next;
+ ca = tmpa.next;
continue;
}
if (cb->type == CHAIN_ARGV)
{
- tmpb.next = cb->next;
+ tmpb.next = NULL;
tmpb.type = CHAIN_STR;
- // TODO support $@ with funcs
- assert (!cb->u.u_a.has_func || argv->flatten || cb->u.u_a.flatten);
+ tmpb.u.u_s.str = NULL;
+ tmpb.u.u_s.len = 0;
+ chain = &tmpb;
arg_print (obs, cb->u.u_a.argv, cb->u.u_a.index,
quote_cache (NULL, cb->quote_age, cb->u.u_a.quotes),
- argv->flatten || cb->u.u_a.flatten, NULL, NULL, false);
- tmpb.u.u_s.len = obstack_object_size (obs);
- tmpb.u.u_s.str = (char *) obstack_finish (obs);
- cb = &tmpb;
+ argv->flatten || cb->u.u_a.flatten, &chain, NULL, NULL,
+ false);
+ assert (obstack_object_size (obs) == 0 && chain != &tmpb);
+ chain->next = cb->next;
+ cb = tmpb.next;
continue;
}
if (ca->type == CHAIN_FUNC)
@@ -1223,18 +1224,21 @@ arg_scratch (void)
/* Dump a representation of ARGV to the obstack OBS, starting with
argument INDEX. If QUOTES is non-NULL, each argument is displayed
- with those quotes. If FLATTEN, builtins are ignored. Separate
- arguments with SEP, which defaults to a comma. If MAX_LEN is
- non-NULL, truncate the output after *MAX_LEN bytes are output and
- return true; otherwise, return false, and reduce *MAX_LEN by the
- number of bytes output. If QUOTE_EACH, the truncation length is
- reset for each argument, quotes do not count against length, and
- all arguments are printed; otherwise, quotes count against the
- length and trailing arguments may be discarded. */
+ with those quotes. If FLATTEN, builtins are converted to empty
+ quotes; if CHAINP, *CHAINP is updated with macro tokens; otherwise,
+ builtins are represented by their name. Separate arguments with
+ SEP, which defaults to a comma. If MAX_LEN is non-NULL, truncate
+ the output after *MAX_LEN bytes are output and return true;
+ otherwise, return false, and reduce *MAX_LEN by the number of bytes
+ output. If QUOTE_EACH, the truncation length is reset for each
+ argument, quotes do not count against length, and all arguments are
+ printed; otherwise, quotes count against the length and trailing
+ arguments may be discarded. MAX_LEN and CHAINP may not both be
+ specified. */
bool
arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
- const string_pair *quotes, bool flatten, const char *sep,
- int *max_len, bool quote_each)
+ const string_pair *quotes, bool flatten, token_chain **chainp,
+ const char *sep, int *max_len, bool quote_each)
{
int len = max_len ? *max_len : INT_MAX;
unsigned int i;
@@ -1245,6 +1249,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
size_t sep_len;
size_t *plen = quote_each ? NULL : &len;
+ if (chainp)
+ assert (!max_len && *chainp);
if (!sep)
sep = ",";
sep_len = strlen (sep);
@@ -1283,13 +1289,13 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
break;
case CHAIN_FUNC:
func_print (obs, find_builtin_by_addr (chain->u.func),
- flatten, quotes);
+ flatten, chainp, quotes);
break;
case CHAIN_ARGV:
if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
quote_cache (NULL, chain->quote_age,
chain->u.u_a.quotes),
- flatten, NULL, &len, false))
+ flatten, chainp, NULL, &len, false))
done = true;
break;
default:
@@ -1305,7 +1311,7 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
break;
case TOKEN_FUNC:
func_print (obs, find_builtin_by_addr (TOKEN_DATA_FUNC (token)),
- flatten, quotes);
+ flatten, chainp, quotes);
break;
default:
assert (!"arg_print");
@@ -1314,6 +1320,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
}
if (max_len)
*max_len = len;
+ else if (chainp)
+ make_text_link (obs, NULL, chainp);
return false;
}
@@ -1519,11 +1527,12 @@ wrap_args (macro_arguments *argv)
struct obstack *obs;
token_data *token;
token_chain *chain;
+ token_chain **end;
if ((argv->argc == 2 || no_gnu_extensions) && arg_empty (argv, 1))
return;
- obs = push_wrapup_init ();
+ obs = push_wrapup_init (&end);
for (i = 1; i < (no_gnu_extensions ? 2 : argv->argc); i++)
{
if (i != 1)
@@ -1535,8 +1544,8 @@ wrap_args (macro_arguments *argv)
obstack_grow (obs, TOKEN_DATA_TEXT (token), TOKEN_DATA_LEN (token));
break;
case TOKEN_FUNC:
- // TODO allow builtins through m4wrap
- assert (false);
+ append_macro (obs, TOKEN_DATA_FUNC (token), NULL, end);
+ break;
case TOKEN_COMP:
chain = token->u.u_c.chain;
while (chain)
@@ -1547,14 +1556,13 @@ wrap_args (macro_arguments *argv)
obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
break;
case CHAIN_FUNC:
- // TODO allow builtins through m4wrap
- assert (false);
+ append_macro (obs, chain->u.func, NULL, end);
break;
case CHAIN_ARGV:
arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
quote_cache (NULL, chain->quote_age,
chain->u.u_a.quotes),
- chain->u.u_a.flatten, NULL, NULL, false);
+ chain->u.u_a.flatten, end, NULL, NULL, false);
break;
default:
assert (!"wrap_args");