summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am28
-rw-r--r--src/Makefile.in1351
-rw-r--r--src/builtin.c2257
-rw-r--r--src/debug.c444
-rw-r--r--src/eval.c855
-rw-r--r--src/format.c385
-rw-r--r--src/freeze.c398
-rw-r--r--src/input.c1156
-rw-r--r--src/m4.c688
-rw-r--r--src/m4.h487
-rw-r--r--src/macro.c391
-rw-r--r--src/output.c1017
-rw-r--r--src/path.c205
-rw-r--r--src/symtab.c401
14 files changed, 10063 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..b9b809d
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,28 @@
+## Makefile.am - template for generating Makefile via Automake
+##
+## Copyright (C) 2006-2011 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/>.
+##
+## This file written by Eric Blake <ebb9@byu.net>
+
+AUTOMAKE_OPTIONS = nostdinc
+AM_CPPFLAGS = -I$(top_srcdir)/lib -I../lib
+AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
+bin_PROGRAMS = m4
+m4_SOURCES = m4.h m4.c builtin.c debug.c eval.c format.c freeze.c input.c \
+macro.c output.c path.c symtab.c
+m4_LDADD = ../lib/libm4.a $(LIBM4_LIBDEPS) $(LIBCSTACK) $(LIBTHREAD)
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..502d651
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,1351 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = m4$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/00gnulib.m4 \
+ $(top_srcdir)/m4/alloca.m4 $(top_srcdir)/m4/ansi-c++.m4 \
+ $(top_srcdir)/m4/assert.m4 $(top_srcdir)/m4/autobuild.m4 \
+ $(top_srcdir)/m4/btowc.m4 $(top_srcdir)/m4/c-stack.m4 \
+ $(top_srcdir)/m4/canonicalize.m4 $(top_srcdir)/m4/cloexec.m4 \
+ $(top_srcdir)/m4/close-stream.m4 $(top_srcdir)/m4/close.m4 \
+ $(top_srcdir)/m4/closein.m4 $(top_srcdir)/m4/closeout.m4 \
+ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/config-h.m4 \
+ $(top_srcdir)/m4/configmake.m4 $(top_srcdir)/m4/dirname.m4 \
+ $(top_srcdir)/m4/double-slash-root.m4 $(top_srcdir)/m4/dup2.m4 \
+ $(top_srcdir)/m4/eealloc.m4 $(top_srcdir)/m4/environ.m4 \
+ $(top_srcdir)/m4/errno_h.m4 $(top_srcdir)/m4/error.m4 \
+ $(top_srcdir)/m4/execute.m4 $(top_srcdir)/m4/exponentd.m4 \
+ $(top_srcdir)/m4/exponentf.m4 $(top_srcdir)/m4/exponentl.m4 \
+ $(top_srcdir)/m4/extensions.m4 \
+ $(top_srcdir)/m4/fatal-signal.m4 $(top_srcdir)/m4/fclose.m4 \
+ $(top_srcdir)/m4/fcntl-o.m4 $(top_srcdir)/m4/fcntl.m4 \
+ $(top_srcdir)/m4/fcntl_h.m4 $(top_srcdir)/m4/fflush.m4 \
+ $(top_srcdir)/m4/filenamecat.m4 $(top_srcdir)/m4/float_h.m4 \
+ $(top_srcdir)/m4/fopen.m4 $(top_srcdir)/m4/fpending.m4 \
+ $(top_srcdir)/m4/fpieee.m4 $(top_srcdir)/m4/fpurge.m4 \
+ $(top_srcdir)/m4/freading.m4 $(top_srcdir)/m4/frexp.m4 \
+ $(top_srcdir)/m4/frexpl.m4 $(top_srcdir)/m4/fseeko.m4 \
+ $(top_srcdir)/m4/ftell.m4 $(top_srcdir)/m4/ftello.m4 \
+ $(top_srcdir)/m4/getdtablesize.m4 $(top_srcdir)/m4/getopt.m4 \
+ $(top_srcdir)/m4/getpagesize.m4 \
+ $(top_srcdir)/m4/gettimeofday.m4 $(top_srcdir)/m4/gl_list.m4 \
+ $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/gnulib-common.m4 \
+ $(top_srcdir)/m4/gnulib-comp.m4 \
+ $(top_srcdir)/m4/include_next.m4 $(top_srcdir)/m4/inline.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intmax_t.m4 \
+ $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/isnand.m4 \
+ $(top_srcdir)/m4/isnanf.m4 $(top_srcdir)/m4/isnanl.m4 \
+ $(top_srcdir)/m4/langinfo_h.m4 $(top_srcdir)/m4/lcmessage.m4 \
+ $(top_srcdir)/m4/ldexp.m4 $(top_srcdir)/m4/ldexpl.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libsigsegv.m4 \
+ $(top_srcdir)/m4/link.m4 $(top_srcdir)/m4/localcharset.m4 \
+ $(top_srcdir)/m4/locale-fr.m4 $(top_srcdir)/m4/locale-ja.m4 \
+ $(top_srcdir)/m4/locale-tr.m4 $(top_srcdir)/m4/locale-zh.m4 \
+ $(top_srcdir)/m4/locale_h.m4 $(top_srcdir)/m4/localename.m4 \
+ $(top_srcdir)/m4/lock.m4 $(top_srcdir)/m4/longlong.m4 \
+ $(top_srcdir)/m4/lseek.m4 $(top_srcdir)/m4/lstat.m4 \
+ $(top_srcdir)/m4/malloc.m4 $(top_srcdir)/m4/malloca.m4 \
+ $(top_srcdir)/m4/manywarnings.m4 $(top_srcdir)/m4/math_h.m4 \
+ $(top_srcdir)/m4/mbrtowc.m4 $(top_srcdir)/m4/mbsinit.m4 \
+ $(top_srcdir)/m4/mbstate_t.m4 $(top_srcdir)/m4/mbtowc.m4 \
+ $(top_srcdir)/m4/memchr.m4 $(top_srcdir)/m4/mkdtemp.m4 \
+ $(top_srcdir)/m4/mkstemp.m4 $(top_srcdir)/m4/mmap-anon.m4 \
+ $(top_srcdir)/m4/mode_t.m4 $(top_srcdir)/m4/multiarch.m4 \
+ $(top_srcdir)/m4/nl_langinfo.m4 $(top_srcdir)/m4/nocrash.m4 \
+ $(top_srcdir)/m4/open.m4 $(top_srcdir)/m4/pathmax.m4 \
+ $(top_srcdir)/m4/pipe2.m4 $(top_srcdir)/m4/posix_spawn.m4 \
+ $(top_srcdir)/m4/printf-frexp.m4 \
+ $(top_srcdir)/m4/printf-frexpl.m4 $(top_srcdir)/m4/printf.m4 \
+ $(top_srcdir)/m4/putenv.m4 $(top_srcdir)/m4/quotearg.m4 \
+ $(top_srcdir)/m4/rawmemchr.m4 $(top_srcdir)/m4/readlink.m4 \
+ $(top_srcdir)/m4/regex.m4 $(top_srcdir)/m4/rename.m4 \
+ $(top_srcdir)/m4/rmdir.m4 $(top_srcdir)/m4/sched_h.m4 \
+ $(top_srcdir)/m4/setenv.m4 $(top_srcdir)/m4/setlocale.m4 \
+ $(top_srcdir)/m4/sig_atomic_t.m4 $(top_srcdir)/m4/sigaction.m4 \
+ $(top_srcdir)/m4/signal_h.m4 \
+ $(top_srcdir)/m4/signalblocking.m4 $(top_srcdir)/m4/signbit.m4 \
+ $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/snprintf.m4 \
+ $(top_srcdir)/m4/spawn-pipe.m4 $(top_srcdir)/m4/spawn_h.m4 \
+ $(top_srcdir)/m4/ssize_t.m4 $(top_srcdir)/m4/stat.m4 \
+ $(top_srcdir)/m4/stdarg.m4 $(top_srcdir)/m4/stdbool.m4 \
+ $(top_srcdir)/m4/stddef_h.m4 $(top_srcdir)/m4/stdint.m4 \
+ $(top_srcdir)/m4/stdint_h.m4 $(top_srcdir)/m4/stdio-safer.m4 \
+ $(top_srcdir)/m4/stdio_h.m4 $(top_srcdir)/m4/stdlib-safer.m4 \
+ $(top_srcdir)/m4/stdlib_h.m4 $(top_srcdir)/m4/strchrnul.m4 \
+ $(top_srcdir)/m4/strdup.m4 $(top_srcdir)/m4/strerror.m4 \
+ $(top_srcdir)/m4/string_h.m4 $(top_srcdir)/m4/strndup.m4 \
+ $(top_srcdir)/m4/strnlen.m4 $(top_srcdir)/m4/strsignal.m4 \
+ $(top_srcdir)/m4/strstr.m4 $(top_srcdir)/m4/strtod.m4 \
+ $(top_srcdir)/m4/strtol.m4 $(top_srcdir)/m4/symlink.m4 \
+ $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/sys_time_h.m4 \
+ $(top_srcdir)/m4/sys_wait_h.m4 $(top_srcdir)/m4/tempname.m4 \
+ $(top_srcdir)/m4/threadlib.m4 $(top_srcdir)/m4/time_h.m4 \
+ $(top_srcdir)/m4/tls.m4 $(top_srcdir)/m4/tmpdir.m4 \
+ $(top_srcdir)/m4/ungetc.m4 $(top_srcdir)/m4/unistd-safer.m4 \
+ $(top_srcdir)/m4/unistd_h.m4 $(top_srcdir)/m4/unlocked-io.m4 \
+ $(top_srcdir)/m4/vasnprintf.m4 \
+ $(top_srcdir)/m4/vasprintf-posix.m4 \
+ $(top_srcdir)/m4/vasprintf.m4 $(top_srcdir)/m4/version-etc.m4 \
+ $(top_srcdir)/m4/wait-process.m4 $(top_srcdir)/m4/waitpid.m4 \
+ $(top_srcdir)/m4/warnings.m4 $(top_srcdir)/m4/wchar_h.m4 \
+ $(top_srcdir)/m4/wchar_t.m4 $(top_srcdir)/m4/wcrtomb.m4 \
+ $(top_srcdir)/m4/wctob.m4 $(top_srcdir)/m4/wctomb.m4 \
+ $(top_srcdir)/m4/wctype_h.m4 $(top_srcdir)/m4/wint_t.m4 \
+ $(top_srcdir)/m4/xalloc.m4 $(top_srcdir)/m4/xsize.m4 \
+ $(top_srcdir)/m4/xstrndup.m4 $(top_srcdir)/m4/xvasprintf.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/lib/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_m4_OBJECTS = m4.$(OBJEXT) builtin.$(OBJEXT) debug.$(OBJEXT) \
+ eval.$(OBJEXT) format.$(OBJEXT) freeze.$(OBJEXT) \
+ input.$(OBJEXT) macro.$(OBJEXT) output.$(OBJEXT) \
+ path.$(OBJEXT) symtab.$(OBJEXT)
+m4_OBJECTS = $(am_m4_OBJECTS)
+am__DEPENDENCIES_1 =
+m4_DEPENDENCIES = ../lib/libm4.a $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(m4_SOURCES)
+DIST_SOURCES = $(m4_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+pkglibexecdir = @pkglibexecdir@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALLOCA_H = @ALLOCA_H@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
+BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
+BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
+BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
+BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONFIG_INCLUDE = @CONFIG_INCLUDE@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXX_CHOICE = @CXX_CHOICE@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@
+EMULTIHOP_VALUE = @EMULTIHOP_VALUE@
+ENOLINK_HIDDEN = @ENOLINK_HIDDEN@
+ENOLINK_VALUE = @ENOLINK_VALUE@
+EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@
+EOVERFLOW_VALUE = @EOVERFLOW_VALUE@
+ERRNO_H = @ERRNO_H@
+EXEEXT = @EXEEXT@
+FLOAT_H = @FLOAT_H@
+GETOPT_H = @GETOPT_H@
+GLIBC21 = @GLIBC21@
+GNULIB_ACOSL = @GNULIB_ACOSL@
+GNULIB_ASINL = @GNULIB_ASINL@
+GNULIB_ATANL = @GNULIB_ATANL@
+GNULIB_ATOLL = @GNULIB_ATOLL@
+GNULIB_BTOWC = @GNULIB_BTOWC@
+GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@
+GNULIB_CANONICALIZE_FILE_NAME = @GNULIB_CANONICALIZE_FILE_NAME@
+GNULIB_CEIL = @GNULIB_CEIL@
+GNULIB_CEILF = @GNULIB_CEILF@
+GNULIB_CEILL = @GNULIB_CEILL@
+GNULIB_CHOWN = @GNULIB_CHOWN@
+GNULIB_CLOSE = @GNULIB_CLOSE@
+GNULIB_COSL = @GNULIB_COSL@
+GNULIB_DPRINTF = @GNULIB_DPRINTF@
+GNULIB_DUP2 = @GNULIB_DUP2@
+GNULIB_DUP3 = @GNULIB_DUP3@
+GNULIB_DUPLOCALE = @GNULIB_DUPLOCALE@
+GNULIB_ENVIRON = @GNULIB_ENVIRON@
+GNULIB_EUIDACCESS = @GNULIB_EUIDACCESS@
+GNULIB_EXPL = @GNULIB_EXPL@
+GNULIB_FACCESSAT = @GNULIB_FACCESSAT@
+GNULIB_FCHDIR = @GNULIB_FCHDIR@
+GNULIB_FCHMODAT = @GNULIB_FCHMODAT@
+GNULIB_FCHOWNAT = @GNULIB_FCHOWNAT@
+GNULIB_FCLOSE = @GNULIB_FCLOSE@
+GNULIB_FCNTL = @GNULIB_FCNTL@
+GNULIB_FFLUSH = @GNULIB_FFLUSH@
+GNULIB_FLOOR = @GNULIB_FLOOR@
+GNULIB_FLOORF = @GNULIB_FLOORF@
+GNULIB_FLOORL = @GNULIB_FLOORL@
+GNULIB_FOPEN = @GNULIB_FOPEN@
+GNULIB_FPRINTF = @GNULIB_FPRINTF@
+GNULIB_FPRINTF_POSIX = @GNULIB_FPRINTF_POSIX@
+GNULIB_FPURGE = @GNULIB_FPURGE@
+GNULIB_FPUTC = @GNULIB_FPUTC@
+GNULIB_FPUTS = @GNULIB_FPUTS@
+GNULIB_FREOPEN = @GNULIB_FREOPEN@
+GNULIB_FREXP = @GNULIB_FREXP@
+GNULIB_FREXPL = @GNULIB_FREXPL@
+GNULIB_FSEEK = @GNULIB_FSEEK@
+GNULIB_FSEEKO = @GNULIB_FSEEKO@
+GNULIB_FSTATAT = @GNULIB_FSTATAT@
+GNULIB_FSYNC = @GNULIB_FSYNC@
+GNULIB_FTELL = @GNULIB_FTELL@
+GNULIB_FTELLO = @GNULIB_FTELLO@
+GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@
+GNULIB_FUTIMENS = @GNULIB_FUTIMENS@
+GNULIB_FWRITE = @GNULIB_FWRITE@
+GNULIB_GETCWD = @GNULIB_GETCWD@
+GNULIB_GETDELIM = @GNULIB_GETDELIM@
+GNULIB_GETDOMAINNAME = @GNULIB_GETDOMAINNAME@
+GNULIB_GETDTABLESIZE = @GNULIB_GETDTABLESIZE@
+GNULIB_GETGROUPS = @GNULIB_GETGROUPS@
+GNULIB_GETHOSTNAME = @GNULIB_GETHOSTNAME@
+GNULIB_GETLINE = @GNULIB_GETLINE@
+GNULIB_GETLOADAVG = @GNULIB_GETLOADAVG@
+GNULIB_GETLOGIN = @GNULIB_GETLOGIN@
+GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@
+GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@
+GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@
+GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@
+GNULIB_GETUSERSHELL = @GNULIB_GETUSERSHELL@
+GNULIB_GRANTPT = @GNULIB_GRANTPT@
+GNULIB_ISFINITE = @GNULIB_ISFINITE@
+GNULIB_ISINF = @GNULIB_ISINF@
+GNULIB_ISNAN = @GNULIB_ISNAN@
+GNULIB_ISNAND = @GNULIB_ISNAND@
+GNULIB_ISNANF = @GNULIB_ISNANF@
+GNULIB_ISNANL = @GNULIB_ISNANL@
+GNULIB_ISWBLANK = @GNULIB_ISWBLANK@
+GNULIB_ISWCTYPE = @GNULIB_ISWCTYPE@
+GNULIB_LCHMOD = @GNULIB_LCHMOD@
+GNULIB_LCHOWN = @GNULIB_LCHOWN@
+GNULIB_LDEXPL = @GNULIB_LDEXPL@
+GNULIB_LINK = @GNULIB_LINK@
+GNULIB_LINKAT = @GNULIB_LINKAT@
+GNULIB_LOGB = @GNULIB_LOGB@
+GNULIB_LOGL = @GNULIB_LOGL@
+GNULIB_LSEEK = @GNULIB_LSEEK@
+GNULIB_LSTAT = @GNULIB_LSTAT@
+GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@
+GNULIB_MBRLEN = @GNULIB_MBRLEN@
+GNULIB_MBRTOWC = @GNULIB_MBRTOWC@
+GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@
+GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@
+GNULIB_MBSCHR = @GNULIB_MBSCHR@
+GNULIB_MBSCSPN = @GNULIB_MBSCSPN@
+GNULIB_MBSINIT = @GNULIB_MBSINIT@
+GNULIB_MBSLEN = @GNULIB_MBSLEN@
+GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@
+GNULIB_MBSNLEN = @GNULIB_MBSNLEN@
+GNULIB_MBSNRTOWCS = @GNULIB_MBSNRTOWCS@
+GNULIB_MBSPBRK = @GNULIB_MBSPBRK@
+GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@
+GNULIB_MBSRCHR = @GNULIB_MBSRCHR@
+GNULIB_MBSRTOWCS = @GNULIB_MBSRTOWCS@
+GNULIB_MBSSEP = @GNULIB_MBSSEP@
+GNULIB_MBSSPN = @GNULIB_MBSSPN@
+GNULIB_MBSSTR = @GNULIB_MBSSTR@
+GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@
+GNULIB_MBTOWC = @GNULIB_MBTOWC@
+GNULIB_MEMCHR = @GNULIB_MEMCHR@
+GNULIB_MEMMEM = @GNULIB_MEMMEM@
+GNULIB_MEMPCPY = @GNULIB_MEMPCPY@
+GNULIB_MEMRCHR = @GNULIB_MEMRCHR@
+GNULIB_MKDIRAT = @GNULIB_MKDIRAT@
+GNULIB_MKDTEMP = @GNULIB_MKDTEMP@
+GNULIB_MKFIFO = @GNULIB_MKFIFO@
+GNULIB_MKFIFOAT = @GNULIB_MKFIFOAT@
+GNULIB_MKNOD = @GNULIB_MKNOD@
+GNULIB_MKNODAT = @GNULIB_MKNODAT@
+GNULIB_MKOSTEMP = @GNULIB_MKOSTEMP@
+GNULIB_MKOSTEMPS = @GNULIB_MKOSTEMPS@
+GNULIB_MKSTEMP = @GNULIB_MKSTEMP@
+GNULIB_MKSTEMPS = @GNULIB_MKSTEMPS@
+GNULIB_MKTIME = @GNULIB_MKTIME@
+GNULIB_NANOSLEEP = @GNULIB_NANOSLEEP@
+GNULIB_NL_LANGINFO = @GNULIB_NL_LANGINFO@
+GNULIB_OBSTACK_PRINTF = @GNULIB_OBSTACK_PRINTF@
+GNULIB_OBSTACK_PRINTF_POSIX = @GNULIB_OBSTACK_PRINTF_POSIX@
+GNULIB_OPEN = @GNULIB_OPEN@
+GNULIB_OPENAT = @GNULIB_OPENAT@
+GNULIB_PERROR = @GNULIB_PERROR@
+GNULIB_PIPE = @GNULIB_PIPE@
+GNULIB_PIPE2 = @GNULIB_PIPE2@
+GNULIB_POPEN = @GNULIB_POPEN@
+GNULIB_POSIX_SPAWN = @GNULIB_POSIX_SPAWN@
+GNULIB_POSIX_SPAWNATTR_DESTROY = @GNULIB_POSIX_SPAWNATTR_DESTROY@
+GNULIB_POSIX_SPAWNATTR_GETFLAGS = @GNULIB_POSIX_SPAWNATTR_GETFLAGS@
+GNULIB_POSIX_SPAWNATTR_GETPGROUP = @GNULIB_POSIX_SPAWNATTR_GETPGROUP@
+GNULIB_POSIX_SPAWNATTR_GETSCHEDPARAM = @GNULIB_POSIX_SPAWNATTR_GETSCHEDPARAM@
+GNULIB_POSIX_SPAWNATTR_GETSCHEDPOLICY = @GNULIB_POSIX_SPAWNATTR_GETSCHEDPOLICY@
+GNULIB_POSIX_SPAWNATTR_GETSIGDEFAULT = @GNULIB_POSIX_SPAWNATTR_GETSIGDEFAULT@
+GNULIB_POSIX_SPAWNATTR_GETSIGMASK = @GNULIB_POSIX_SPAWNATTR_GETSIGMASK@
+GNULIB_POSIX_SPAWNATTR_INIT = @GNULIB_POSIX_SPAWNATTR_INIT@
+GNULIB_POSIX_SPAWNATTR_SETFLAGS = @GNULIB_POSIX_SPAWNATTR_SETFLAGS@
+GNULIB_POSIX_SPAWNATTR_SETPGROUP = @GNULIB_POSIX_SPAWNATTR_SETPGROUP@
+GNULIB_POSIX_SPAWNATTR_SETSCHEDPARAM = @GNULIB_POSIX_SPAWNATTR_SETSCHEDPARAM@
+GNULIB_POSIX_SPAWNATTR_SETSCHEDPOLICY = @GNULIB_POSIX_SPAWNATTR_SETSCHEDPOLICY@
+GNULIB_POSIX_SPAWNATTR_SETSIGDEFAULT = @GNULIB_POSIX_SPAWNATTR_SETSIGDEFAULT@
+GNULIB_POSIX_SPAWNATTR_SETSIGMASK = @GNULIB_POSIX_SPAWNATTR_SETSIGMASK@
+GNULIB_POSIX_SPAWNP = @GNULIB_POSIX_SPAWNP@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2 = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_DESTROY = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_DESTROY@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_INIT = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_INIT@
+GNULIB_PREAD = @GNULIB_PREAD@
+GNULIB_PRINTF = @GNULIB_PRINTF@
+GNULIB_PRINTF_POSIX = @GNULIB_PRINTF_POSIX@
+GNULIB_PTSNAME = @GNULIB_PTSNAME@
+GNULIB_PUTC = @GNULIB_PUTC@
+GNULIB_PUTCHAR = @GNULIB_PUTCHAR@
+GNULIB_PUTENV = @GNULIB_PUTENV@
+GNULIB_PUTS = @GNULIB_PUTS@
+GNULIB_PWRITE = @GNULIB_PWRITE@
+GNULIB_RANDOM_R = @GNULIB_RANDOM_R@
+GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@
+GNULIB_READLINK = @GNULIB_READLINK@
+GNULIB_READLINKAT = @GNULIB_READLINKAT@
+GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@
+GNULIB_REALPATH = @GNULIB_REALPATH@
+GNULIB_REMOVE = @GNULIB_REMOVE@
+GNULIB_RENAME = @GNULIB_RENAME@
+GNULIB_RENAMEAT = @GNULIB_RENAMEAT@
+GNULIB_RMDIR = @GNULIB_RMDIR@
+GNULIB_ROUND = @GNULIB_ROUND@
+GNULIB_ROUNDF = @GNULIB_ROUNDF@
+GNULIB_ROUNDL = @GNULIB_ROUNDL@
+GNULIB_RPMATCH = @GNULIB_RPMATCH@
+GNULIB_SETENV = @GNULIB_SETENV@
+GNULIB_SETLOCALE = @GNULIB_SETLOCALE@
+GNULIB_SIGACTION = @GNULIB_SIGACTION@
+GNULIB_SIGNAL_H_SIGPIPE = @GNULIB_SIGNAL_H_SIGPIPE@
+GNULIB_SIGNBIT = @GNULIB_SIGNBIT@
+GNULIB_SIGPROCMASK = @GNULIB_SIGPROCMASK@
+GNULIB_SINL = @GNULIB_SINL@
+GNULIB_SLEEP = @GNULIB_SLEEP@
+GNULIB_SNPRINTF = @GNULIB_SNPRINTF@
+GNULIB_SPRINTF_POSIX = @GNULIB_SPRINTF_POSIX@
+GNULIB_SQRTL = @GNULIB_SQRTL@
+GNULIB_STAT = @GNULIB_STAT@
+GNULIB_STDIO_H_SIGPIPE = @GNULIB_STDIO_H_SIGPIPE@
+GNULIB_STPCPY = @GNULIB_STPCPY@
+GNULIB_STPNCPY = @GNULIB_STPNCPY@
+GNULIB_STRCASESTR = @GNULIB_STRCASESTR@
+GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@
+GNULIB_STRDUP = @GNULIB_STRDUP@
+GNULIB_STRERROR = @GNULIB_STRERROR@
+GNULIB_STRERROR_R = @GNULIB_STRERROR_R@
+GNULIB_STRNCAT = @GNULIB_STRNCAT@
+GNULIB_STRNDUP = @GNULIB_STRNDUP@
+GNULIB_STRNLEN = @GNULIB_STRNLEN@
+GNULIB_STRPBRK = @GNULIB_STRPBRK@
+GNULIB_STRPTIME = @GNULIB_STRPTIME@
+GNULIB_STRSEP = @GNULIB_STRSEP@
+GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@
+GNULIB_STRSTR = @GNULIB_STRSTR@
+GNULIB_STRTOD = @GNULIB_STRTOD@
+GNULIB_STRTOK_R = @GNULIB_STRTOK_R@
+GNULIB_STRTOLL = @GNULIB_STRTOLL@
+GNULIB_STRTOULL = @GNULIB_STRTOULL@
+GNULIB_STRVERSCMP = @GNULIB_STRVERSCMP@
+GNULIB_SYMLINK = @GNULIB_SYMLINK@
+GNULIB_SYMLINKAT = @GNULIB_SYMLINKAT@
+GNULIB_SYSTEM_POSIX = @GNULIB_SYSTEM_POSIX@
+GNULIB_TANL = @GNULIB_TANL@
+GNULIB_TIMEGM = @GNULIB_TIMEGM@
+GNULIB_TIME_R = @GNULIB_TIME_R@
+GNULIB_TMPFILE = @GNULIB_TMPFILE@
+GNULIB_TOWCTRANS = @GNULIB_TOWCTRANS@
+GNULIB_TRUNC = @GNULIB_TRUNC@
+GNULIB_TRUNCF = @GNULIB_TRUNCF@
+GNULIB_TRUNCL = @GNULIB_TRUNCL@
+GNULIB_TTYNAME_R = @GNULIB_TTYNAME_R@
+GNULIB_UNISTD_H_GETOPT = @GNULIB_UNISTD_H_GETOPT@
+GNULIB_UNISTD_H_SIGPIPE = @GNULIB_UNISTD_H_SIGPIPE@
+GNULIB_UNLINK = @GNULIB_UNLINK@
+GNULIB_UNLINKAT = @GNULIB_UNLINKAT@
+GNULIB_UNLOCKPT = @GNULIB_UNLOCKPT@
+GNULIB_UNSETENV = @GNULIB_UNSETENV@
+GNULIB_USLEEP = @GNULIB_USLEEP@
+GNULIB_UTIMENSAT = @GNULIB_UTIMENSAT@
+GNULIB_VASPRINTF = @GNULIB_VASPRINTF@
+GNULIB_VDPRINTF = @GNULIB_VDPRINTF@
+GNULIB_VFPRINTF = @GNULIB_VFPRINTF@
+GNULIB_VFPRINTF_POSIX = @GNULIB_VFPRINTF_POSIX@
+GNULIB_VPRINTF = @GNULIB_VPRINTF@
+GNULIB_VPRINTF_POSIX = @GNULIB_VPRINTF_POSIX@
+GNULIB_VSNPRINTF = @GNULIB_VSNPRINTF@
+GNULIB_VSPRINTF_POSIX = @GNULIB_VSPRINTF_POSIX@
+GNULIB_WAITPID = @GNULIB_WAITPID@
+GNULIB_WCPCPY = @GNULIB_WCPCPY@
+GNULIB_WCPNCPY = @GNULIB_WCPNCPY@
+GNULIB_WCRTOMB = @GNULIB_WCRTOMB@
+GNULIB_WCSCASECMP = @GNULIB_WCSCASECMP@
+GNULIB_WCSCAT = @GNULIB_WCSCAT@
+GNULIB_WCSCHR = @GNULIB_WCSCHR@
+GNULIB_WCSCMP = @GNULIB_WCSCMP@
+GNULIB_WCSCOLL = @GNULIB_WCSCOLL@
+GNULIB_WCSCPY = @GNULIB_WCSCPY@
+GNULIB_WCSCSPN = @GNULIB_WCSCSPN@
+GNULIB_WCSDUP = @GNULIB_WCSDUP@
+GNULIB_WCSLEN = @GNULIB_WCSLEN@
+GNULIB_WCSNCASECMP = @GNULIB_WCSNCASECMP@
+GNULIB_WCSNCAT = @GNULIB_WCSNCAT@
+GNULIB_WCSNCMP = @GNULIB_WCSNCMP@
+GNULIB_WCSNCPY = @GNULIB_WCSNCPY@
+GNULIB_WCSNLEN = @GNULIB_WCSNLEN@
+GNULIB_WCSNRTOMBS = @GNULIB_WCSNRTOMBS@
+GNULIB_WCSPBRK = @GNULIB_WCSPBRK@
+GNULIB_WCSRCHR = @GNULIB_WCSRCHR@
+GNULIB_WCSRTOMBS = @GNULIB_WCSRTOMBS@
+GNULIB_WCSSPN = @GNULIB_WCSSPN@
+GNULIB_WCSSTR = @GNULIB_WCSSTR@
+GNULIB_WCSTOK = @GNULIB_WCSTOK@
+GNULIB_WCSWIDTH = @GNULIB_WCSWIDTH@
+GNULIB_WCSXFRM = @GNULIB_WCSXFRM@
+GNULIB_WCTOB = @GNULIB_WCTOB@
+GNULIB_WCTOMB = @GNULIB_WCTOMB@
+GNULIB_WCTRANS = @GNULIB_WCTRANS@
+GNULIB_WCTYPE = @GNULIB_WCTYPE@
+GNULIB_WCWIDTH = @GNULIB_WCWIDTH@
+GNULIB_WMEMCHR = @GNULIB_WMEMCHR@
+GNULIB_WMEMCMP = @GNULIB_WMEMCMP@
+GNULIB_WMEMCPY = @GNULIB_WMEMCPY@
+GNULIB_WMEMMOVE = @GNULIB_WMEMMOVE@
+GNULIB_WMEMSET = @GNULIB_WMEMSET@
+GNULIB_WRITE = @GNULIB_WRITE@
+GNULIB__EXIT = @GNULIB__EXIT@
+GREP = @GREP@
+HAVE_ACOSL = @HAVE_ACOSL@
+HAVE_ASINL = @HAVE_ASINL@
+HAVE_ATANL = @HAVE_ATANL@
+HAVE_ATOLL = @HAVE_ATOLL@
+HAVE_BTOWC = @HAVE_BTOWC@
+HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@
+HAVE_CHOWN = @HAVE_CHOWN@
+HAVE_COSL = @HAVE_COSL@
+HAVE_DECL_ACOSL = @HAVE_DECL_ACOSL@
+HAVE_DECL_ASINL = @HAVE_DECL_ASINL@
+HAVE_DECL_ATANL = @HAVE_DECL_ATANL@
+HAVE_DECL_CEILF = @HAVE_DECL_CEILF@
+HAVE_DECL_CEILL = @HAVE_DECL_CEILL@
+HAVE_DECL_COSL = @HAVE_DECL_COSL@
+HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@
+HAVE_DECL_EXPL = @HAVE_DECL_EXPL@
+HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@
+HAVE_DECL_FLOORF = @HAVE_DECL_FLOORF@
+HAVE_DECL_FLOORL = @HAVE_DECL_FLOORL@
+HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@
+HAVE_DECL_FREXPL = @HAVE_DECL_FREXPL@
+HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@
+HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@
+HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@
+HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@
+HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@
+HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@
+HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@
+HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@
+HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@
+HAVE_DECL_LDEXPL = @HAVE_DECL_LDEXPL@
+HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@
+HAVE_DECL_LOGB = @HAVE_DECL_LOGB@
+HAVE_DECL_LOGL = @HAVE_DECL_LOGL@
+HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@
+HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@
+HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@
+HAVE_DECL_ROUND = @HAVE_DECL_ROUND@
+HAVE_DECL_ROUNDF = @HAVE_DECL_ROUNDF@
+HAVE_DECL_ROUNDL = @HAVE_DECL_ROUNDL@
+HAVE_DECL_SETENV = @HAVE_DECL_SETENV@
+HAVE_DECL_SINL = @HAVE_DECL_SINL@
+HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@
+HAVE_DECL_SQRTL = @HAVE_DECL_SQRTL@
+HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@
+HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@
+HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@
+HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@
+HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@
+HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@
+HAVE_DECL_TANL = @HAVE_DECL_TANL@
+HAVE_DECL_TRUNC = @HAVE_DECL_TRUNC@
+HAVE_DECL_TRUNCF = @HAVE_DECL_TRUNCF@
+HAVE_DECL_TRUNCL = @HAVE_DECL_TRUNCL@
+HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@
+HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@
+HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@
+HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@
+HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@
+HAVE_DPRINTF = @HAVE_DPRINTF@
+HAVE_DUP2 = @HAVE_DUP2@
+HAVE_DUP3 = @HAVE_DUP3@
+HAVE_DUPLOCALE = @HAVE_DUPLOCALE@
+HAVE_EUIDACCESS = @HAVE_EUIDACCESS@
+HAVE_EXPL = @HAVE_EXPL@
+HAVE_FACCESSAT = @HAVE_FACCESSAT@
+HAVE_FCHDIR = @HAVE_FCHDIR@
+HAVE_FCHMODAT = @HAVE_FCHMODAT@
+HAVE_FCHOWNAT = @HAVE_FCHOWNAT@
+HAVE_FCNTL = @HAVE_FCNTL@
+HAVE_FEATURES_H = @HAVE_FEATURES_H@
+HAVE_FSEEKO = @HAVE_FSEEKO@
+HAVE_FSTATAT = @HAVE_FSTATAT@
+HAVE_FSYNC = @HAVE_FSYNC@
+HAVE_FTELLO = @HAVE_FTELLO@
+HAVE_FTRUNCATE = @HAVE_FTRUNCATE@
+HAVE_FUTIMENS = @HAVE_FUTIMENS@
+HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@
+HAVE_GETGROUPS = @HAVE_GETGROUPS@
+HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@
+HAVE_GETLOGIN = @HAVE_GETLOGIN@
+HAVE_GETOPT_H = @HAVE_GETOPT_H@
+HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@
+HAVE_GETSUBOPT = @HAVE_GETSUBOPT@
+HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@
+HAVE_GRANTPT = @HAVE_GRANTPT@
+HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
+HAVE_ISNAND = @HAVE_ISNAND@
+HAVE_ISNANF = @HAVE_ISNANF@
+HAVE_ISNANL = @HAVE_ISNANL@
+HAVE_ISWBLANK = @HAVE_ISWBLANK@
+HAVE_ISWCNTRL = @HAVE_ISWCNTRL@
+HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@
+HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@
+HAVE_LANGINFO_H = @HAVE_LANGINFO_H@
+HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@
+HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@
+HAVE_LCHMOD = @HAVE_LCHMOD@
+HAVE_LCHOWN = @HAVE_LCHOWN@
+HAVE_LIBSIGSEGV = @HAVE_LIBSIGSEGV@
+HAVE_LINK = @HAVE_LINK@
+HAVE_LINKAT = @HAVE_LINKAT@
+HAVE_LOGL = @HAVE_LOGL@
+HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@
+HAVE_LSTAT = @HAVE_LSTAT@
+HAVE_MBRLEN = @HAVE_MBRLEN@
+HAVE_MBRTOWC = @HAVE_MBRTOWC@
+HAVE_MBSINIT = @HAVE_MBSINIT@
+HAVE_MBSLEN = @HAVE_MBSLEN@
+HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@
+HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@
+HAVE_MEMCHR = @HAVE_MEMCHR@
+HAVE_MEMPCPY = @HAVE_MEMPCPY@
+HAVE_MKDIRAT = @HAVE_MKDIRAT@
+HAVE_MKDTEMP = @HAVE_MKDTEMP@
+HAVE_MKFIFO = @HAVE_MKFIFO@
+HAVE_MKFIFOAT = @HAVE_MKFIFOAT@
+HAVE_MKNOD = @HAVE_MKNOD@
+HAVE_MKNODAT = @HAVE_MKNODAT@
+HAVE_MKOSTEMP = @HAVE_MKOSTEMP@
+HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@
+HAVE_MKSTEMP = @HAVE_MKSTEMP@
+HAVE_MKSTEMPS = @HAVE_MKSTEMPS@
+HAVE_NANOSLEEP = @HAVE_NANOSLEEP@
+HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@
+HAVE_OPENAT = @HAVE_OPENAT@
+HAVE_OS_H = @HAVE_OS_H@
+HAVE_PIPE = @HAVE_PIPE@
+HAVE_PIPE2 = @HAVE_PIPE2@
+HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@
+HAVE_POSIX_SPAWN = @HAVE_POSIX_SPAWN@
+HAVE_POSIX_SPAWNATTR_T = @HAVE_POSIX_SPAWNATTR_T@
+HAVE_POSIX_SPAWN_FILE_ACTIONS_T = @HAVE_POSIX_SPAWN_FILE_ACTIONS_T@
+HAVE_PREAD = @HAVE_PREAD@
+HAVE_PTSNAME = @HAVE_PTSNAME@
+HAVE_PWRITE = @HAVE_PWRITE@
+HAVE_RANDOM_H = @HAVE_RANDOM_H@
+HAVE_RANDOM_R = @HAVE_RANDOM_R@
+HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@
+HAVE_READLINK = @HAVE_READLINK@
+HAVE_READLINKAT = @HAVE_READLINKAT@
+HAVE_REALPATH = @HAVE_REALPATH@
+HAVE_RENAMEAT = @HAVE_RENAMEAT@
+HAVE_RPMATCH = @HAVE_RPMATCH@
+HAVE_SCHED_H = @HAVE_SCHED_H@
+HAVE_SETENV = @HAVE_SETENV@
+HAVE_SIGACTION = @HAVE_SIGACTION@
+HAVE_SIGINFO_T = @HAVE_SIGINFO_T@
+HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
+HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
+HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
+HAVE_SIGSET_T = @HAVE_SIGSET_T@
+HAVE_SINL = @HAVE_SINL@
+HAVE_SLEEP = @HAVE_SLEEP@
+HAVE_SPAWN_H = @HAVE_SPAWN_H@
+HAVE_SQRTL = @HAVE_SQRTL@
+HAVE_STDINT_H = @HAVE_STDINT_H@
+HAVE_STPCPY = @HAVE_STPCPY@
+HAVE_STPNCPY = @HAVE_STPNCPY@
+HAVE_STRCASESTR = @HAVE_STRCASESTR@
+HAVE_STRCHRNUL = @HAVE_STRCHRNUL@
+HAVE_STRPBRK = @HAVE_STRPBRK@
+HAVE_STRPTIME = @HAVE_STRPTIME@
+HAVE_STRSEP = @HAVE_STRSEP@
+HAVE_STRTOD = @HAVE_STRTOD@
+HAVE_STRTOLL = @HAVE_STRTOLL@
+HAVE_STRTOULL = @HAVE_STRTOULL@
+HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@
+HAVE_STRUCT_SCHED_PARAM = @HAVE_STRUCT_SCHED_PARAM@
+HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@
+HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@
+HAVE_STRVERSCMP = @HAVE_STRVERSCMP@
+HAVE_SYMLINK = @HAVE_SYMLINK@
+HAVE_SYMLINKAT = @HAVE_SYMLINKAT@
+HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@
+HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@
+HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@
+HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@
+HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@
+HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
+HAVE_TANL = @HAVE_TANL@
+HAVE_TIMEGM = @HAVE_TIMEGM@
+HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@
+HAVE_UNISTD_H = @HAVE_UNISTD_H@
+HAVE_UNLINKAT = @HAVE_UNLINKAT@
+HAVE_UNLOCKPT = @HAVE_UNLOCKPT@
+HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@
+HAVE_USLEEP = @HAVE_USLEEP@
+HAVE_UTIMENSAT = @HAVE_UTIMENSAT@
+HAVE_VASPRINTF = @HAVE_VASPRINTF@
+HAVE_VDPRINTF = @HAVE_VDPRINTF@
+HAVE_WCHAR_H = @HAVE_WCHAR_H@
+HAVE_WCHAR_T = @HAVE_WCHAR_T@
+HAVE_WCPCPY = @HAVE_WCPCPY@
+HAVE_WCPNCPY = @HAVE_WCPNCPY@
+HAVE_WCRTOMB = @HAVE_WCRTOMB@
+HAVE_WCSCASECMP = @HAVE_WCSCASECMP@
+HAVE_WCSCAT = @HAVE_WCSCAT@
+HAVE_WCSCHR = @HAVE_WCSCHR@
+HAVE_WCSCMP = @HAVE_WCSCMP@
+HAVE_WCSCOLL = @HAVE_WCSCOLL@
+HAVE_WCSCPY = @HAVE_WCSCPY@
+HAVE_WCSCSPN = @HAVE_WCSCSPN@
+HAVE_WCSDUP = @HAVE_WCSDUP@
+HAVE_WCSLEN = @HAVE_WCSLEN@
+HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@
+HAVE_WCSNCAT = @HAVE_WCSNCAT@
+HAVE_WCSNCMP = @HAVE_WCSNCMP@
+HAVE_WCSNCPY = @HAVE_WCSNCPY@
+HAVE_WCSNLEN = @HAVE_WCSNLEN@
+HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@
+HAVE_WCSPBRK = @HAVE_WCSPBRK@
+HAVE_WCSRCHR = @HAVE_WCSRCHR@
+HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@
+HAVE_WCSSPN = @HAVE_WCSSPN@
+HAVE_WCSSTR = @HAVE_WCSSTR@
+HAVE_WCSTOK = @HAVE_WCSTOK@
+HAVE_WCSWIDTH = @HAVE_WCSWIDTH@
+HAVE_WCSXFRM = @HAVE_WCSXFRM@
+HAVE_WCTRANS_T = @HAVE_WCTRANS_T@
+HAVE_WCTYPE_H = @HAVE_WCTYPE_H@
+HAVE_WCTYPE_T = @HAVE_WCTYPE_T@
+HAVE_WINT_T = @HAVE_WINT_T@
+HAVE_WMEMCHR = @HAVE_WMEMCHR@
+HAVE_WMEMCMP = @HAVE_WMEMCMP@
+HAVE_WMEMCPY = @HAVE_WMEMCPY@
+HAVE_WMEMMOVE = @HAVE_WMEMMOVE@
+HAVE_WMEMSET = @HAVE_WMEMSET@
+HAVE_XLOCALE_H = @HAVE_XLOCALE_H@
+HAVE__BOOL = @HAVE__BOOL@
+HAVE__EXIT = @HAVE__EXIT@
+INCLUDE_NEXT = @INCLUDE_NEXT@
+INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBCSTACK = @LIBCSTACK@
+LIBINTL = @LIBINTL@
+LIBM4_LIBDEPS = @LIBM4_LIBDEPS@
+LIBM4_LTLIBDEPS = @LIBM4_LTLIBDEPS@
+LIBMULTITHREAD = @LIBMULTITHREAD@
+LIBOBJS = @LIBOBJS@
+LIBPTH = @LIBPTH@
+LIBPTH_PREFIX = @LIBPTH_PREFIX@
+LIBS = @LIBS@
+LIBSIGSEGV = @LIBSIGSEGV@
+LIBSIGSEGV_PREFIX = @LIBSIGSEGV_PREFIX@
+LIBTESTS_LIBDEPS = @LIBTESTS_LIBDEPS@
+LIBTHREAD = @LIBTHREAD@
+LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@
+LOCALE_FR = @LOCALE_FR@
+LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@
+LOCALE_JA = @LOCALE_JA@
+LOCALE_TR_UTF8 = @LOCALE_TR_UTF8@
+LOCALE_ZH_CN = @LOCALE_ZH_CN@
+LTLIBCSTACK = @LTLIBCSTACK@
+LTLIBINTL = @LTLIBINTL@
+LTLIBMULTITHREAD = @LTLIBMULTITHREAD@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBPTH = @LTLIBPTH@
+LTLIBSIGSEGV = @LTLIBSIGSEGV@
+LTLIBTHREAD = @LTLIBTHREAD@
+M4_LIBOBJS = @M4_LIBOBJS@
+M4_LTLIBOBJS = @M4_LTLIBOBJS@
+M4tests_LIBOBJS = @M4tests_LIBOBJS@
+M4tests_LTLIBOBJS = @M4tests_LTLIBOBJS@
+M4tests_WITNESS = @M4tests_WITNESS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@
+NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@
+NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@
+NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@
+NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@
+NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@
+NEXT_AS_FIRST_DIRECTIVE_MATH_H = @NEXT_AS_FIRST_DIRECTIVE_MATH_H@
+NEXT_AS_FIRST_DIRECTIVE_SCHED_H = @NEXT_AS_FIRST_DIRECTIVE_SCHED_H@
+NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@
+NEXT_AS_FIRST_DIRECTIVE_SPAWN_H = @NEXT_AS_FIRST_DIRECTIVE_SPAWN_H@
+NEXT_AS_FIRST_DIRECTIVE_STDARG_H = @NEXT_AS_FIRST_DIRECTIVE_STDARG_H@
+NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@
+NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@
+NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@
+NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@
+NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_WAIT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_WAIT_H@
+NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@
+NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@
+NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@
+NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H@
+NEXT_ERRNO_H = @NEXT_ERRNO_H@
+NEXT_FCNTL_H = @NEXT_FCNTL_H@
+NEXT_FLOAT_H = @NEXT_FLOAT_H@
+NEXT_GETOPT_H = @NEXT_GETOPT_H@
+NEXT_LANGINFO_H = @NEXT_LANGINFO_H@
+NEXT_LOCALE_H = @NEXT_LOCALE_H@
+NEXT_MATH_H = @NEXT_MATH_H@
+NEXT_SCHED_H = @NEXT_SCHED_H@
+NEXT_SIGNAL_H = @NEXT_SIGNAL_H@
+NEXT_SPAWN_H = @NEXT_SPAWN_H@
+NEXT_STDARG_H = @NEXT_STDARG_H@
+NEXT_STDDEF_H = @NEXT_STDDEF_H@
+NEXT_STDINT_H = @NEXT_STDINT_H@
+NEXT_STDIO_H = @NEXT_STDIO_H@
+NEXT_STDLIB_H = @NEXT_STDLIB_H@
+NEXT_STRING_H = @NEXT_STRING_H@
+NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@
+NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@
+NEXT_SYS_WAIT_H = @NEXT_SYS_WAIT_H@
+NEXT_TIME_H = @NEXT_TIME_H@
+NEXT_UNISTD_H = @NEXT_UNISTD_H@
+NEXT_WCHAR_H = @NEXT_WCHAR_H@
+NEXT_WCTYPE_H = @NEXT_WCTYPE_H@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PRAGMA_COLUMNS = @PRAGMA_COLUMNS@
+PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@
+PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@
+PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
+RANLIB = @RANLIB@
+REPLACE_BTOWC = @REPLACE_BTOWC@
+REPLACE_CALLOC = @REPLACE_CALLOC@
+REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@
+REPLACE_CEIL = @REPLACE_CEIL@
+REPLACE_CEILF = @REPLACE_CEILF@
+REPLACE_CEILL = @REPLACE_CEILL@
+REPLACE_CHOWN = @REPLACE_CHOWN@
+REPLACE_CLOSE = @REPLACE_CLOSE@
+REPLACE_DPRINTF = @REPLACE_DPRINTF@
+REPLACE_DUP = @REPLACE_DUP@
+REPLACE_DUP2 = @REPLACE_DUP2@
+REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@
+REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@
+REPLACE_FCLOSE = @REPLACE_FCLOSE@
+REPLACE_FCNTL = @REPLACE_FCNTL@
+REPLACE_FFLUSH = @REPLACE_FFLUSH@
+REPLACE_FLOOR = @REPLACE_FLOOR@
+REPLACE_FLOORF = @REPLACE_FLOORF@
+REPLACE_FLOORL = @REPLACE_FLOORL@
+REPLACE_FOPEN = @REPLACE_FOPEN@
+REPLACE_FPRINTF = @REPLACE_FPRINTF@
+REPLACE_FPURGE = @REPLACE_FPURGE@
+REPLACE_FREOPEN = @REPLACE_FREOPEN@
+REPLACE_FREXP = @REPLACE_FREXP@
+REPLACE_FREXPL = @REPLACE_FREXPL@
+REPLACE_FSEEK = @REPLACE_FSEEK@
+REPLACE_FSEEKO = @REPLACE_FSEEKO@
+REPLACE_FSTAT = @REPLACE_FSTAT@
+REPLACE_FSTATAT = @REPLACE_FSTATAT@
+REPLACE_FTELL = @REPLACE_FTELL@
+REPLACE_FTELLO = @REPLACE_FTELLO@
+REPLACE_FUTIMENS = @REPLACE_FUTIMENS@
+REPLACE_GETCWD = @REPLACE_GETCWD@
+REPLACE_GETDELIM = @REPLACE_GETDELIM@
+REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@
+REPLACE_GETGROUPS = @REPLACE_GETGROUPS@
+REPLACE_GETLINE = @REPLACE_GETLINE@
+REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@
+REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@
+REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@
+REPLACE_HUGE_VAL = @REPLACE_HUGE_VAL@
+REPLACE_ISFINITE = @REPLACE_ISFINITE@
+REPLACE_ISINF = @REPLACE_ISINF@
+REPLACE_ISNAN = @REPLACE_ISNAN@
+REPLACE_ISWBLANK = @REPLACE_ISWBLANK@
+REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@
+REPLACE_LCHOWN = @REPLACE_LCHOWN@
+REPLACE_LDEXPL = @REPLACE_LDEXPL@
+REPLACE_LINK = @REPLACE_LINK@
+REPLACE_LINKAT = @REPLACE_LINKAT@
+REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@
+REPLACE_LSEEK = @REPLACE_LSEEK@
+REPLACE_LSTAT = @REPLACE_LSTAT@
+REPLACE_MALLOC = @REPLACE_MALLOC@
+REPLACE_MBRLEN = @REPLACE_MBRLEN@
+REPLACE_MBRTOWC = @REPLACE_MBRTOWC@
+REPLACE_MBSINIT = @REPLACE_MBSINIT@
+REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@
+REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@
+REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@
+REPLACE_MBTOWC = @REPLACE_MBTOWC@
+REPLACE_MEMCHR = @REPLACE_MEMCHR@
+REPLACE_MEMMEM = @REPLACE_MEMMEM@
+REPLACE_MKDIR = @REPLACE_MKDIR@
+REPLACE_MKFIFO = @REPLACE_MKFIFO@
+REPLACE_MKNOD = @REPLACE_MKNOD@
+REPLACE_MKSTEMP = @REPLACE_MKSTEMP@
+REPLACE_MKTIME = @REPLACE_MKTIME@
+REPLACE_NAN = @REPLACE_NAN@
+REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@
+REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@
+REPLACE_NULL = @REPLACE_NULL@
+REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@
+REPLACE_OPEN = @REPLACE_OPEN@
+REPLACE_OPENAT = @REPLACE_OPENAT@
+REPLACE_PERROR = @REPLACE_PERROR@
+REPLACE_POPEN = @REPLACE_POPEN@
+REPLACE_POSIX_SPAWN = @REPLACE_POSIX_SPAWN@
+REPLACE_PREAD = @REPLACE_PREAD@
+REPLACE_PRINTF = @REPLACE_PRINTF@
+REPLACE_PUTENV = @REPLACE_PUTENV@
+REPLACE_PWRITE = @REPLACE_PWRITE@
+REPLACE_READLINK = @REPLACE_READLINK@
+REPLACE_REALLOC = @REPLACE_REALLOC@
+REPLACE_REALPATH = @REPLACE_REALPATH@
+REPLACE_REMOVE = @REPLACE_REMOVE@
+REPLACE_RENAME = @REPLACE_RENAME@
+REPLACE_RENAMEAT = @REPLACE_RENAMEAT@
+REPLACE_RMDIR = @REPLACE_RMDIR@
+REPLACE_ROUND = @REPLACE_ROUND@
+REPLACE_ROUNDF = @REPLACE_ROUNDF@
+REPLACE_ROUNDL = @REPLACE_ROUNDL@
+REPLACE_SETENV = @REPLACE_SETENV@
+REPLACE_SETLOCALE = @REPLACE_SETLOCALE@
+REPLACE_SIGNBIT = @REPLACE_SIGNBIT@
+REPLACE_SIGNBIT_USING_GCC = @REPLACE_SIGNBIT_USING_GCC@
+REPLACE_SLEEP = @REPLACE_SLEEP@
+REPLACE_SNPRINTF = @REPLACE_SNPRINTF@
+REPLACE_SPRINTF = @REPLACE_SPRINTF@
+REPLACE_STAT = @REPLACE_STAT@
+REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@
+REPLACE_STPNCPY = @REPLACE_STPNCPY@
+REPLACE_STRCASESTR = @REPLACE_STRCASESTR@
+REPLACE_STRDUP = @REPLACE_STRDUP@
+REPLACE_STRERROR = @REPLACE_STRERROR@
+REPLACE_STRERROR_R = @REPLACE_STRERROR_R@
+REPLACE_STRNCAT = @REPLACE_STRNCAT@
+REPLACE_STRNDUP = @REPLACE_STRNDUP@
+REPLACE_STRNLEN = @REPLACE_STRNLEN@
+REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@
+REPLACE_STRSTR = @REPLACE_STRSTR@
+REPLACE_STRTOD = @REPLACE_STRTOD@
+REPLACE_STRTOK_R = @REPLACE_STRTOK_R@
+REPLACE_SYMLINK = @REPLACE_SYMLINK@
+REPLACE_TIMEGM = @REPLACE_TIMEGM@
+REPLACE_TMPFILE = @REPLACE_TMPFILE@
+REPLACE_TRUNC = @REPLACE_TRUNC@
+REPLACE_TRUNCF = @REPLACE_TRUNCF@
+REPLACE_TRUNCL = @REPLACE_TRUNCL@
+REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@
+REPLACE_UNLINK = @REPLACE_UNLINK@
+REPLACE_UNLINKAT = @REPLACE_UNLINKAT@
+REPLACE_UNSETENV = @REPLACE_UNSETENV@
+REPLACE_USLEEP = @REPLACE_USLEEP@
+REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@
+REPLACE_VASPRINTF = @REPLACE_VASPRINTF@
+REPLACE_VDPRINTF = @REPLACE_VDPRINTF@
+REPLACE_VFPRINTF = @REPLACE_VFPRINTF@
+REPLACE_VPRINTF = @REPLACE_VPRINTF@
+REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@
+REPLACE_VSPRINTF = @REPLACE_VSPRINTF@
+REPLACE_WCRTOMB = @REPLACE_WCRTOMB@
+REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@
+REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@
+REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@
+REPLACE_WCTOB = @REPLACE_WCTOB@
+REPLACE_WCTOMB = @REPLACE_WCTOMB@
+REPLACE_WCWIDTH = @REPLACE_WCWIDTH@
+REPLACE_WRITE = @REPLACE_WRITE@
+SCHED_H = @SCHED_H@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
+SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
+STDARG_H = @STDARG_H@
+STDBOOL_H = @STDBOOL_H@
+STDDEF_H = @STDDEF_H@
+STDINT_H = @STDINT_H@
+STRIP = @STRIP@
+SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@
+TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@
+UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@
+UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@
+UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
+WERROR_CFLAGS = @WERROR_CFLAGS@
+WINT_T_SUFFIX = @WINT_T_SUFFIX@
+abs_aux_dir = @abs_aux_dir@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+lispdir = @lispdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = nostdinc
+AM_CPPFLAGS = -I$(top_srcdir)/lib -I../lib
+AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
+m4_SOURCES = m4.h m4.c builtin.c debug.c eval.c format.c freeze.c input.c \
+macro.c output.c path.c symtab.c
+
+m4_LDADD = ../lib/libm4.a $(LIBM4_LIBDEPS) $(LIBCSTACK) $(LIBTHREAD)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+m4$(EXEEXT): $(m4_OBJECTS) $(m4_DEPENDENCIES)
+ @rm -f m4$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(m4_OBJECTS) $(m4_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/builtin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/format.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freeze.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m4.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macro.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/path.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/builtin.c b/src/builtin.c
new file mode 100644
index 0000000..632ef79
--- /dev/null
+++ b/src/builtin.c
@@ -0,0 +1,2257 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2000, 2004, 2006-2011 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/>.
+*/
+
+/* Code for all builtin macros, initialization of symbol table, and
+ expansion of user defined macros. */
+
+#include "m4.h"
+
+#include "execute.h"
+#include "memchr2.h"
+#include "regex.h"
+#include "spawn-pipe.h"
+#include "wait-process.h"
+
+#define ARG(i) (argc > (i) ? TOKEN_DATA_TEXT (argv[i]) : "")
+
+/* Initialization of builtin and predefined macros. The table
+ "builtin_tab" is both used for initialization, and by the "builtin"
+ builtin. */
+
+#define DECLARE(name) \
+ static void name (struct obstack *, int, token_data **)
+
+DECLARE (m4___file__);
+DECLARE (m4___line__);
+DECLARE (m4___program__);
+DECLARE (m4_builtin);
+DECLARE (m4_changecom);
+DECLARE (m4_changequote);
+#ifdef ENABLE_CHANGEWORD
+DECLARE (m4_changeword);
+#endif
+DECLARE (m4_debugmode);
+DECLARE (m4_debugfile);
+DECLARE (m4_decr);
+DECLARE (m4_define);
+DECLARE (m4_defn);
+DECLARE (m4_divert);
+DECLARE (m4_divnum);
+DECLARE (m4_dnl);
+DECLARE (m4_dumpdef);
+DECLARE (m4_errprint);
+DECLARE (m4_esyscmd);
+DECLARE (m4_eval);
+DECLARE (m4_format);
+DECLARE (m4_ifdef);
+DECLARE (m4_ifelse);
+DECLARE (m4_include);
+DECLARE (m4_incr);
+DECLARE (m4_index);
+DECLARE (m4_indir);
+DECLARE (m4_len);
+DECLARE (m4_m4exit);
+DECLARE (m4_m4wrap);
+DECLARE (m4_maketemp);
+DECLARE (m4_mkstemp);
+DECLARE (m4_patsubst);
+DECLARE (m4_popdef);
+DECLARE (m4_pushdef);
+DECLARE (m4_regexp);
+DECLARE (m4_shift);
+DECLARE (m4_sinclude);
+DECLARE (m4_substr);
+DECLARE (m4_syscmd);
+DECLARE (m4_sysval);
+DECLARE (m4_traceoff);
+DECLARE (m4_traceon);
+DECLARE (m4_translit);
+DECLARE (m4_undefine);
+DECLARE (m4_undivert);
+
+#undef DECLARE
+
+static builtin const builtin_tab[] =
+{
+
+ /* name GNUext macros blind function */
+
+ { "__file__", true, false, false, m4___file__ },
+ { "__line__", true, false, false, m4___line__ },
+ { "__program__", true, false, false, m4___program__ },
+ { "builtin", true, true, true, m4_builtin },
+ { "changecom", false, false, false, m4_changecom },
+ { "changequote", false, false, false, m4_changequote },
+#ifdef ENABLE_CHANGEWORD
+ { "changeword", true, false, true, m4_changeword },
+#endif
+ { "debugmode", true, false, false, m4_debugmode },
+ { "debugfile", true, false, false, m4_debugfile },
+ { "decr", false, false, true, m4_decr },
+ { "define", false, true, true, m4_define },
+ { "defn", false, false, true, m4_defn },
+ { "divert", false, false, false, m4_divert },
+ { "divnum", false, false, false, m4_divnum },
+ { "dnl", false, false, false, m4_dnl },
+ { "dumpdef", false, false, false, m4_dumpdef },
+ { "errprint", false, false, true, m4_errprint },
+ { "esyscmd", true, false, true, m4_esyscmd },
+ { "eval", false, false, true, m4_eval },
+ { "format", true, false, true, m4_format },
+ { "ifdef", false, false, true, m4_ifdef },
+ { "ifelse", false, false, true, m4_ifelse },
+ { "include", false, false, true, m4_include },
+ { "incr", false, false, true, m4_incr },
+ { "index", false, false, true, m4_index },
+ { "indir", true, true, true, m4_indir },
+ { "len", false, false, true, m4_len },
+ { "m4exit", false, false, false, m4_m4exit },
+ { "m4wrap", false, false, true, m4_m4wrap },
+ { "maketemp", false, false, true, m4_maketemp },
+ { "mkstemp", false, false, true, m4_mkstemp },
+ { "patsubst", true, false, true, m4_patsubst },
+ { "popdef", false, false, true, m4_popdef },
+ { "pushdef", false, true, true, m4_pushdef },
+ { "regexp", true, false, true, m4_regexp },
+ { "shift", false, false, true, m4_shift },
+ { "sinclude", false, false, true, m4_sinclude },
+ { "substr", false, false, true, m4_substr },
+ { "syscmd", false, false, true, m4_syscmd },
+ { "sysval", false, false, false, m4_sysval },
+ { "traceoff", false, false, false, m4_traceoff },
+ { "traceon", false, false, false, m4_traceon },
+ { "translit", false, false, true, m4_translit },
+ { "undefine", false, false, true, m4_undefine },
+ { "undivert", false, false, false, m4_undivert },
+
+ { 0, false, false, false, 0 },
+
+ /* placeholder is intentionally stuck after the table end delimiter,
+ so that we can easily find it, while not treating it as a real
+ builtin. */
+ { "placeholder", true, false, false, m4_placeholder },
+};
+
+static predefined const predefined_tab[] =
+{
+#if UNIX
+ { "unix", "__unix__", "" },
+#endif
+#if W32_NATIVE
+ { "windows", "__windows__", "" },
+#endif
+#if OS2
+ { "os2", "__os2__", "" },
+#endif
+#if !UNIX && !W32_NATIVE && !OS2
+# warning Platform macro not provided
+#endif
+ { NULL, "__gnu__", "" },
+
+ { NULL, NULL, NULL },
+};
+
+/*----------------------------------------.
+| Find the builtin, which lives on ADDR. |
+`----------------------------------------*/
+
+const builtin *
+find_builtin_by_addr (builtin_func *func)
+{
+ const builtin *bp;
+
+ for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
+ if (bp->func == func)
+ return bp;
+ if (func == m4_placeholder)
+ return bp + 1;
+ return NULL;
+}
+
+/*----------------------------------------------------------.
+| Find the builtin, which has NAME. On failure, return the |
+| placeholder builtin. |
+`----------------------------------------------------------*/
+
+const builtin *
+find_builtin_by_name (const char *name)
+{
+ const builtin *bp;
+
+ for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
+ if (STREQ (bp->name, name))
+ return bp;
+ return bp + 1;
+}
+
+/*----------------------------------------------------------------.
+| Install a builtin macro with name NAME, bound to the C function |
+| given in BP. MODE is SYMBOL_INSERT or SYMBOL_PUSHDEF. |
+`----------------------------------------------------------------*/
+
+void
+define_builtin (const char *name, const builtin *bp, symbol_lookup mode)
+{
+ symbol *sym;
+
+ sym = lookup_symbol (name, mode);
+ SYMBOL_TYPE (sym) = TOKEN_FUNC;
+ SYMBOL_MACRO_ARGS (sym) = bp->groks_macro_args;
+ SYMBOL_BLIND_NO_ARGS (sym) = bp->blind_if_no_args;
+ SYMBOL_FUNC (sym) = bp->func;
+}
+
+/* Storage for the compiled regular expression of
+ --warn-macro-sequence. */
+static struct re_pattern_buffer macro_sequence_buf;
+
+/* Storage for the matches of --warn-macro-sequence. */
+static struct re_registers macro_sequence_regs;
+
+/* True if --warn-macro-sequence is in effect. */
+static bool macro_sequence_inuse;
+
+/*----------------------------------------.
+| Clean up regular expression variables. |
+`----------------------------------------*/
+
+static void
+free_pattern_buffer (struct re_pattern_buffer *buf, struct re_registers *regs)
+{
+ regfree (buf);
+ free (regs->start);
+ free (regs->end);
+}
+
+/*-----------------------------------------------------------------.
+| Set the regular expression of --warn-macro-sequence that will be |
+| checked during define and pushdef. Exit on failure. |
+`-----------------------------------------------------------------*/
+void
+set_macro_sequence (const char *regexp)
+{
+ const char *msg;
+
+ if (! regexp)
+ regexp = DEFAULT_MACRO_SEQUENCE;
+ else if (regexp[0] == '\0')
+ {
+ macro_sequence_inuse = false;
+ return;
+ }
+
+ msg = re_compile_pattern (regexp, strlen (regexp), &macro_sequence_buf);
+ if (msg != NULL)
+ {
+ M4ERROR ((EXIT_FAILURE, 0,
+ "--warn-macro-sequence: bad regular expression `%s': %s",
+ regexp, msg));
+ }
+ re_set_registers (&macro_sequence_buf, &macro_sequence_regs,
+ macro_sequence_regs.num_regs,
+ macro_sequence_regs.start, macro_sequence_regs.end);
+ macro_sequence_inuse = true;
+}
+
+/*-----------------------------------------------------------.
+| Free dynamic memory utilized by the macro sequence regular |
+| expression during the define builtin. |
+`-----------------------------------------------------------*/
+void
+free_macro_sequence (void)
+{
+ free_pattern_buffer (&macro_sequence_buf, &macro_sequence_regs);
+}
+
+/*-----------------------------------------------------------------.
+| Define a predefined or user-defined macro, with name NAME, and |
+| expansion TEXT. MODE destinguishes between the "define" and the |
+| "pushdef" case. It is also used from main. |
+`-----------------------------------------------------------------*/
+
+void
+define_user_macro (const char *name, const char *text, symbol_lookup mode)
+{
+ symbol *s;
+ char *defn = xstrdup (text ? text : "");
+
+ s = lookup_symbol (name, mode);
+ if (SYMBOL_TYPE (s) == TOKEN_TEXT)
+ free (SYMBOL_TEXT (s));
+
+ SYMBOL_TYPE (s) = TOKEN_TEXT;
+ SYMBOL_TEXT (s) = defn;
+
+ /* Implement --warn-macro-sequence. */
+ if (macro_sequence_inuse && text)
+ {
+ regoff_t offset = 0;
+ size_t len = strlen (defn);
+
+ while ((offset = re_search (&macro_sequence_buf, defn, len, offset,
+ len - offset, &macro_sequence_regs)) >= 0)
+ {
+ /* Skip empty matches. */
+ if (macro_sequence_regs.start[0] == macro_sequence_regs.end[0])
+ offset++;
+ else
+ {
+ char tmp;
+ offset = macro_sequence_regs.end[0];
+ tmp = defn[offset];
+ defn[offset] = '\0';
+ M4ERROR ((warning_status, 0,
+ "Warning: definition of `%s' contains sequence `%s'",
+ name, defn + macro_sequence_regs.start[0]));
+ defn[offset] = tmp;
+ }
+ }
+ if (offset == -2)
+ M4ERROR ((warning_status, 0,
+ "error checking --warn-macro-sequence for macro `%s'",
+ name));
+ }
+}
+
+/*-----------------------------------------------.
+| Initialize all builtin and predefined macros. |
+`-----------------------------------------------*/
+
+void
+builtin_init (void)
+{
+ const builtin *bp;
+ const predefined *pp;
+ char *string;
+
+ for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
+ if (!no_gnu_extensions || !bp->gnu_extension)
+ {
+ if (prefix_all_builtins)
+ {
+ string = (char *) xmalloc (strlen (bp->name) + 4);
+ strcpy (string, "m4_");
+ strcat (string, bp->name);
+ define_builtin (string, bp, SYMBOL_INSERT);
+ free (string);
+ }
+ else
+ define_builtin (bp->name, bp, SYMBOL_INSERT);
+ }
+
+ for (pp = &predefined_tab[0]; pp->func != NULL; pp++)
+ if (no_gnu_extensions)
+ {
+ if (pp->unix_name != NULL)
+ define_user_macro (pp->unix_name, pp->func, SYMBOL_INSERT);
+ }
+ else
+ {
+ if (pp->gnu_name != NULL)
+ define_user_macro (pp->gnu_name, pp->func, SYMBOL_INSERT);
+ }
+}
+
+/*-------------------------------------------------------------------.
+| Give friendly warnings if a builtin macro is passed an |
+| inappropriate number of arguments. NAME is the macro name for |
+| messages, ARGC is actual number of arguments, MIN is the minimum |
+| number of acceptable arguments, negative if not applicable, MAX is |
+| the maximum number, negative if not applicable. |
+`-------------------------------------------------------------------*/
+
+static bool
+bad_argc (token_data *name, int argc, int min, int max)
+{
+ bool isbad = false;
+
+ if (min > 0 && argc < min)
+ {
+ if (!suppress_warnings)
+ M4ERROR ((warning_status, 0,
+ "Warning: too few arguments to builtin `%s'",
+ TOKEN_DATA_TEXT (name)));
+ isbad = true;
+ }
+ else if (max > 0 && argc > max && !suppress_warnings)
+ M4ERROR ((warning_status, 0,
+ "Warning: excess arguments to builtin `%s' ignored",
+ TOKEN_DATA_TEXT (name)));
+
+ return isbad;
+}
+
+/*-----------------------------------------------------------------.
+| The function numeric_arg () converts ARG to an int pointed to by |
+| VALUEP. If the conversion fails, print error message for macro |
+| MACRO. Return true iff conversion succeeds. |
+`-----------------------------------------------------------------*/
+
+static bool
+numeric_arg (token_data *macro, const char *arg, int *valuep)
+{
+ char *endp;
+
+ if (*arg == '\0')
+ {
+ *valuep = 0;
+ M4ERROR ((warning_status, 0,
+ "empty string treated as 0 in builtin `%s'",
+ TOKEN_DATA_TEXT (macro)));
+ }
+ else
+ {
+ errno = 0;
+ *valuep = strtol (arg, &endp, 10);
+ if (*endp != '\0')
+ {
+ M4ERROR ((warning_status, 0,
+ "non-numeric argument to builtin `%s'",
+ TOKEN_DATA_TEXT (macro)));
+ return false;
+ }
+ if (isspace (to_uchar (*arg)))
+ M4ERROR ((warning_status, 0,
+ "leading whitespace ignored in builtin `%s'",
+ TOKEN_DATA_TEXT (macro)));
+ else if (errno == ERANGE)
+ M4ERROR ((warning_status, 0,
+ "numeric overflow detected in builtin `%s'",
+ TOKEN_DATA_TEXT (macro)));
+ }
+ return true;
+}
+
+/*------------------------------------------------------.
+| The function ntoa () converts VALUE to a signed ASCII |
+| representation in radix RADIX. |
+`------------------------------------------------------*/
+
+/* Digits for number to ASCII conversions. */
+static char const digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+const char *
+ntoa (int32_t value, int radix)
+{
+ bool negative;
+ uint32_t uvalue;
+ static char str[256];
+ char *s = &str[sizeof str];
+
+ *--s = '\0';
+
+ if (value < 0)
+ {
+ negative = true;
+ uvalue = -(uint32_t) value;
+ }
+ else
+ {
+ negative = false;
+ uvalue = (uint32_t) value;
+ }
+
+ do
+ {
+ *--s = digits[uvalue % radix];
+ uvalue /= radix;
+ }
+ while (uvalue > 0);
+
+ if (negative)
+ *--s = '-';
+ return s;
+}
+
+/*---------------------------------------------------------------.
+| Format an int VAL, and stuff it into an obstack OBS. Used for |
+| macros expanding to numbers. |
+`---------------------------------------------------------------*/
+
+static void
+shipout_int (struct obstack *obs, int val)
+{
+ const char *s;
+
+ s = ntoa ((int32_t) val, 10);
+ obstack_grow (obs, s, strlen (s));
+}
+
+/*-------------------------------------------------------------------.
+| Print ARGC arguments from the table ARGV to obstack OBS, separated |
+| by SEP, and quoted by the current quotes if QUOTED is true. |
+`-------------------------------------------------------------------*/
+
+static void
+dump_args (struct obstack *obs, int argc, token_data **argv,
+ const char *sep, bool quoted)
+{
+ int i;
+ size_t len = strlen (sep);
+
+ for (i = 1; i < argc; i++)
+ {
+ if (i > 1)
+ obstack_grow (obs, sep, len);
+ if (quoted)
+ obstack_grow (obs, lquote.string, lquote.length);
+ obstack_grow (obs, TOKEN_DATA_TEXT (argv[i]),
+ strlen (TOKEN_DATA_TEXT (argv[i])));
+ if (quoted)
+ obstack_grow (obs, rquote.string, rquote.length);
+ }
+}
+
+/* The rest of this file is code for builtins and expansion of user
+ defined macros. All the functions for builtins have a prototype as:
+
+ void m4_MACRONAME (struct obstack *obs, int argc, char *argv[]);
+
+ The function are expected to leave their expansion on the obstack OBS,
+ as an unfinished object. ARGV is a table of ARGC pointers to the
+ individual arguments to the macro. Please note that in general
+ argv[argc] != NULL. */
+
+/* The first section are macros for definining, undefining, examining,
+ changing, ... other macros. */
+
+/*-------------------------------------------------------------------.
+| The function define_macro is common for the builtins "define", |
+| "undefine", "pushdef" and "popdef". ARGC and ARGV is as for the |
+| caller, and MODE argument determines how the macro name is entered |
+| into the symbol table. |
+`-------------------------------------------------------------------*/
+
+static void
+define_macro (int argc, token_data **argv, symbol_lookup mode)
+{
+ const builtin *bp;
+
+ if (bad_argc (argv[0], argc, 2, 3))
+ return;
+
+ if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
+ {
+ M4ERROR ((warning_status, 0,
+ "Warning: %s: invalid macro name ignored", ARG (0)));
+ return;
+ }
+
+ if (argc == 2)
+ {
+ define_user_macro (ARG (1), "", mode);
+ return;
+ }
+
+ switch (TOKEN_DATA_TYPE (argv[2]))
+ {
+ case TOKEN_TEXT:
+ define_user_macro (ARG (1), ARG (2), mode);
+ break;
+
+ case TOKEN_FUNC:
+ bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[2]));
+ if (bp == NULL)
+ return;
+ else
+ define_builtin (ARG (1), bp, mode);
+ break;
+
+ case TOKEN_VOID:
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad token data type in define_macro ()"));
+ abort ();
+ }
+}
+
+static void
+m4_define (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ define_macro (argc, argv, SYMBOL_INSERT);
+}
+
+static void
+m4_undefine (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ int i;
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+ for (i = 1; i < argc; i++)
+ lookup_symbol (ARG (i), SYMBOL_DELETE);
+}
+
+static void
+m4_pushdef (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ define_macro (argc, argv, SYMBOL_PUSHDEF);
+}
+
+static void
+m4_popdef (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ int i;
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+ for (i = 1; i < argc; i++)
+ lookup_symbol (ARG (i), SYMBOL_POPDEF);
+}
+
+/*---------------------.
+| Conditionals of m4. |
+`---------------------*/
+
+static void
+m4_ifdef (struct obstack *obs, int argc, token_data **argv)
+{
+ symbol *s;
+ const char *result;
+
+ if (bad_argc (argv[0], argc, 3, 4))
+ return;
+ s = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
+
+ if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID)
+ result = ARG (2);
+ else if (argc >= 4)
+ result = ARG (3);
+ else
+ result = NULL;
+
+ if (result != NULL)
+ obstack_grow (obs, result, strlen (result));
+}
+
+static void
+m4_ifelse (struct obstack *obs, int argc, token_data **argv)
+{
+ const char *result;
+ token_data *me = argv[0];
+
+ if (argc == 2)
+ return;
+
+ if (bad_argc (me, argc, 4, -1))
+ return;
+ else
+ /* Diagnose excess arguments if 5, 8, 11, etc., actual arguments. */
+ bad_argc (me, (argc + 2) % 3, -1, 1);
+
+ argv++;
+ argc--;
+
+ result = NULL;
+ while (result == NULL)
+
+ if (STREQ (ARG (0), ARG (1)))
+ result = ARG (2);
+
+ else
+ switch (argc)
+ {
+ case 3:
+ return;
+
+ case 4:
+ case 5:
+ result = ARG (3);
+ break;
+
+ default:
+ argc -= 3;
+ argv += 3;
+ }
+
+ obstack_grow (obs, result, strlen (result));
+}
+
+/*-------------------------------------------------------------------.
+| The function dump_symbol () is for use by "dumpdef". It builds up |
+| a table of all defined, un-shadowed, symbols. |
+`-------------------------------------------------------------------*/
+
+/* The structure dump_symbol_data is used to pass the information needed
+ from call to call to dump_symbol. */
+
+struct dump_symbol_data
+{
+ struct obstack *obs; /* obstack for table */
+ symbol **base; /* base of table */
+ int size; /* size of table */
+};
+
+static void
+dump_symbol (symbol *sym, void *arg)
+{
+ struct dump_symbol_data *data = (struct dump_symbol_data *) arg;
+ if (!SYMBOL_SHADOWED (sym) && SYMBOL_TYPE (sym) != TOKEN_VOID)
+ {
+ obstack_blank (data->obs, sizeof (symbol *));
+ data->base = (symbol **) obstack_base (data->obs);
+ data->base[data->size++] = sym;
+ }
+}
+
+/*------------------------------------------------------------------------.
+| qsort comparison routine, for sorting the table made in m4_dumpdef (). |
+`------------------------------------------------------------------------*/
+
+static int
+dumpdef_cmp (const void *s1, const void *s2)
+{
+ return strcmp (SYMBOL_NAME (* (symbol *const *) s1),
+ SYMBOL_NAME (* (symbol *const *) s2));
+}
+
+/*-------------------------------------------------------------.
+| Implementation of "dumpdef" itself. It builds up a table of |
+| pointers to symbols, sorts it and prints the sorted table. |
+`-------------------------------------------------------------*/
+
+static void
+m4_dumpdef (struct obstack *obs, int argc, token_data **argv)
+{
+ symbol *s;
+ int i;
+ struct dump_symbol_data data;
+ const builtin *bp;
+
+ data.obs = obs;
+ data.base = (symbol **) obstack_base (obs);
+ data.size = 0;
+
+ if (argc == 1)
+ {
+ hack_all_symbols (dump_symbol, &data);
+ }
+ else
+ {
+ for (i = 1; i < argc; i++)
+ {
+ s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP);
+ if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID)
+ dump_symbol (s, &data);
+ else
+ M4ERROR ((warning_status, 0,
+ "undefined macro `%s'", TOKEN_DATA_TEXT (argv[i])));
+ }
+ }
+
+ /* Make table of symbols invisible to expand_macro (). */
+
+ obstack_finish (obs);
+
+ qsort (data.base, data.size, sizeof (symbol *), dumpdef_cmp);
+
+ for (; data.size > 0; --data.size, data.base++)
+ {
+ DEBUG_PRINT1 ("%s:\t", SYMBOL_NAME (data.base[0]));
+
+ switch (SYMBOL_TYPE (data.base[0]))
+ {
+ case TOKEN_TEXT:
+ if (debug_level & DEBUG_TRACE_QUOTE)
+ DEBUG_PRINT3 ("%s%s%s\n",
+ lquote.string, SYMBOL_TEXT (data.base[0]), rquote.string);
+ else
+ DEBUG_PRINT1 ("%s\n", SYMBOL_TEXT (data.base[0]));
+ break;
+
+ case TOKEN_FUNC:
+ bp = find_builtin_by_addr (SYMBOL_FUNC (data.base[0]));
+ if (bp == NULL)
+ {
+ M4ERROR ((warning_status, 0, "\
+INTERNAL ERROR: builtin not found in builtin table"));
+ abort ();
+ }
+ DEBUG_PRINT1 ("<%s>\n", bp->name);
+ break;
+
+ case TOKEN_VOID:
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad token data type in m4_dumpdef ()"));
+ abort ();
+ break;
+ }
+ }
+}
+
+/*-----------------------------------------------------------------.
+| The builtin "builtin" allows calls to builtin macros, even if |
+| their definition has been overridden or shadowed. It is thus |
+| possible to redefine builtins, and still access their original |
+| definition. This macro is not available in compatibility mode. |
+`-----------------------------------------------------------------*/
+
+static void
+m4_builtin (struct obstack *obs, int argc, token_data **argv)
+{
+ const builtin *bp;
+ const char *name;
+
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+ if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
+ {
+ M4ERROR ((warning_status, 0,
+ "Warning: %s: invalid macro name ignored", ARG (0)));
+ return;
+ }
+
+ name = ARG (1);
+ bp = find_builtin_by_name (name);
+ if (bp->func == m4_placeholder)
+ M4ERROR ((warning_status, 0,
+ "undefined builtin `%s'", name));
+ else
+ {
+ int i;
+ if (! bp->groks_macro_args)
+ for (i = 2; i < argc; i++)
+ if (TOKEN_DATA_TYPE (argv[i]) != TOKEN_TEXT)
+ {
+ TOKEN_DATA_TYPE (argv[i]) = TOKEN_TEXT;
+ TOKEN_DATA_TEXT (argv[i]) = (char *) "";
+ }
+ bp->func (obs, argc - 1, argv + 1);
+ }
+}
+
+/*-------------------------------------------------------------------.
+| The builtin "indir" allows indirect calls to macros, even if their |
+| name is not a proper macro name. It is thus possible to define |
+| macros with ill-formed names for internal use in larger macro |
+| packages. This macro is not available in compatibility mode. |
+`-------------------------------------------------------------------*/
+
+static void
+m4_indir (struct obstack *obs, int argc, token_data **argv)
+{
+ symbol *s;
+ const char *name;
+
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+ if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
+ {
+ M4ERROR ((warning_status, 0,
+ "Warning: %s: invalid macro name ignored", ARG (0)));
+ return;
+ }
+
+ name = ARG (1);
+ s = lookup_symbol (name, SYMBOL_LOOKUP);
+ if (s == NULL || SYMBOL_TYPE (s) == TOKEN_VOID)
+ M4ERROR ((warning_status, 0,
+ "undefined macro `%s'", name));
+ else
+ {
+ int i;
+ if (! SYMBOL_MACRO_ARGS (s))
+ for (i = 2; i < argc; i++)
+ if (TOKEN_DATA_TYPE (argv[i]) != TOKEN_TEXT)
+ {
+ TOKEN_DATA_TYPE (argv[i]) = TOKEN_TEXT;
+ TOKEN_DATA_TEXT (argv[i]) = (char *) "";
+ }
+ call_macro (s, argc - 1, argv + 1, obs);
+ }
+}
+
+/*------------------------------------------------------------------.
+| The macro "defn" returns the quoted definition of the macro named |
+| by the first argument. If the macro is builtin, it will push a |
+| special macro-definition token on the input stack. |
+`------------------------------------------------------------------*/
+
+static void
+m4_defn (struct obstack *obs, int argc, token_data **argv)
+{
+ symbol *s;
+ builtin_func *b;
+ unsigned int i;
+
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+
+ assert (0 < argc && argc <= INT_MAX);
+ for (i = 1; i < (unsigned) argc; i++)
+ {
+ const char *arg = ARG((int) i);
+ s = lookup_symbol (arg, SYMBOL_LOOKUP);
+ if (s == NULL)
+ continue;
+
+ switch (SYMBOL_TYPE (s))
+ {
+ case TOKEN_TEXT:
+ obstack_grow (obs, lquote.string, lquote.length);
+ obstack_grow (obs, SYMBOL_TEXT (s), strlen (SYMBOL_TEXT (s)));
+ obstack_grow (obs, rquote.string, rquote.length);
+ break;
+
+ case TOKEN_FUNC:
+ b = SYMBOL_FUNC (s);
+ if (b == m4_placeholder)
+ M4ERROR ((warning_status, 0, "\
+builtin `%s' requested by frozen file is not supported", arg));
+ else if (argc != 2)
+ M4ERROR ((warning_status, 0,
+ "Warning: cannot concatenate builtin `%s'",
+ arg));
+ else
+ push_macro (b);
+ break;
+
+ case TOKEN_VOID:
+ /* Nothing to do for traced but undefined macro. */
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad symbol type in m4_defn ()"));
+ abort ();
+ }
+ }
+}
+
+/*--------------------------------------------------------------.
+| This section contains macros to handle the builtins "syscmd", |
+| "esyscmd" and "sysval". "esyscmd" is GNU specific. |
+`--------------------------------------------------------------*/
+
+/* Exit code from last "syscmd" command. */
+static int sysval;
+
+static void
+m4_syscmd (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ const char *cmd = ARG (1);
+ int status;
+ int sig_status;
+ const char *prog_args[4] = { "sh", "-c" };
+ if (bad_argc (argv[0], argc, 2, 2) || !*cmd)
+ {
+ /* The empty command is successful. */
+ sysval = 0;
+ return;
+ }
+
+ debug_flush_files ();
+#if W32_NATIVE
+ if (strstr (SYSCMD_SHELL, "cmd"))
+ {
+ prog_args[0] = "cmd";
+ prog_args[1] = "/c";
+ }
+#endif
+ prog_args[2] = cmd;
+ errno = 0;
+ status = execute (ARG (0), SYSCMD_SHELL, (char **) prog_args, false,
+ false, false, false, true, false, &sig_status);
+ if (sig_status)
+ {
+ assert (status == 127);
+ sysval = sig_status << 8;
+ }
+ else
+ {
+ if (status == 127 && errno)
+ M4ERROR ((warning_status, errno, "cannot run command `%s'", cmd));
+ sysval = status;
+ }
+}
+
+static void
+m4_esyscmd (struct obstack *obs, int argc, token_data **argv)
+{
+ const char *cmd = ARG (1);
+ const char *prog_args[4] = { "sh", "-c" };
+ pid_t child;
+ int fd;
+ FILE *pin;
+ int status;
+ int sig_status;
+
+ if (bad_argc (argv[0], argc, 2, 2) || !*cmd)
+ {
+ /* The empty command is successful. */
+ sysval = 0;
+ return;
+ }
+
+ debug_flush_files ();
+#if W32_NATIVE
+ if (strstr (SYSCMD_SHELL, "cmd"))
+ {
+ prog_args[0] = "cmd";
+ prog_args[1] = "/c";
+ }
+#endif
+ prog_args[2] = cmd;
+ errno = 0;
+ child = create_pipe_in (ARG (0), SYSCMD_SHELL, (char **) prog_args,
+ NULL, false, true, false, &fd);
+ if (child == -1)
+ {
+ M4ERROR ((warning_status, errno, "cannot run command `%s'", cmd));
+ sysval = 127;
+ return;
+ }
+ pin = fdopen (fd, "r");
+ if (pin == NULL)
+ {
+ M4ERROR ((warning_status, errno, "cannot run command `%s'", cmd));
+ sysval = 127;
+ close (fd);
+ return;
+ }
+ while (1)
+ {
+ size_t avail = obstack_room (obs);
+ size_t len;
+ if (!avail)
+ {
+ int ch = getc (pin);
+ if (ch == EOF)
+ break;
+ obstack_1grow (obs, ch);
+ continue;
+ }
+ len = fread (obstack_next_free (obs), 1, avail, pin);
+ if (len <= 0)
+ break;
+ obstack_blank_fast (obs, len);
+ }
+ if (ferror (pin) || fclose (pin))
+ M4ERROR ((EXIT_FAILURE, errno, "cannot read pipe"));
+ errno = 0;
+ status = wait_subprocess (child, ARG (0), false, true, true, false,
+ &sig_status);
+ if (sig_status)
+ {
+ assert (status == 127);
+ sysval = sig_status << 8;
+ }
+ else
+ {
+ if (status == 127 && errno)
+ M4ERROR ((warning_status, errno, "cannot run command `%s'", cmd));
+ sysval = status;
+ }
+}
+
+static void
+m4_sysval (struct obstack *obs, int argc M4_GNUC_UNUSED,
+ token_data **argv M4_GNUC_UNUSED)
+{
+ shipout_int (obs, sysval);
+}
+
+/*------------------------------------------------------------------.
+| This section contains the top level code for the "eval" builtin. |
+| The actual work is done in the function evaluate (), which lives |
+| in eval.c. |
+`------------------------------------------------------------------*/
+
+static void
+m4_eval (struct obstack *obs, int argc, token_data **argv)
+{
+ int32_t value = 0;
+ int radix = 10;
+ int min = 1;
+ const char *s;
+
+ if (bad_argc (argv[0], argc, 2, 4))
+ return;
+
+ if (*ARG (2) && !numeric_arg (argv[0], ARG (2), &radix))
+ return;
+
+ if (radix < 1 || radix > (int) strlen (digits))
+ {
+ M4ERROR ((warning_status, 0,
+ "radix %d in builtin `%s' out of range",
+ radix, ARG (0)));
+ return;
+ }
+
+ if (argc >= 4 && !numeric_arg (argv[0], ARG (3), &min))
+ return;
+ if (min < 0)
+ {
+ M4ERROR ((warning_status, 0,
+ "negative width to builtin `%s'", ARG (0)));
+ return;
+ }
+
+ if (!*ARG (1))
+ M4ERROR ((warning_status, 0,
+ "empty string treated as 0 in builtin `%s'", ARG (0)));
+ else if (evaluate (ARG (1), &value))
+ return;
+
+ if (radix == 1)
+ {
+ if (value < 0)
+ {
+ obstack_1grow (obs, '-');
+ value = -value;
+ }
+ /* This assumes 2's-complement for correctly handling INT_MIN. */
+ while (min-- - value > 0)
+ obstack_1grow (obs, '0');
+ while (value-- != 0)
+ obstack_1grow (obs, '1');
+ obstack_1grow (obs, '\0');
+ return;
+ }
+
+ s = ntoa (value, radix);
+
+ if (*s == '-')
+ {
+ obstack_1grow (obs, '-');
+ s++;
+ }
+ for (min -= strlen (s); --min >= 0;)
+ obstack_1grow (obs, '0');
+
+ obstack_grow (obs, s, strlen (s));
+}
+
+static void
+m4_incr (struct obstack *obs, int argc, token_data **argv)
+{
+ int value;
+
+ if (bad_argc (argv[0], argc, 2, 2))
+ return;
+
+ if (!numeric_arg (argv[0], ARG (1), &value))
+ return;
+
+ shipout_int (obs, value + 1);
+}
+
+static void
+m4_decr (struct obstack *obs, int argc, token_data **argv)
+{
+ int value;
+
+ if (bad_argc (argv[0], argc, 2, 2))
+ return;
+
+ if (!numeric_arg (argv[0], ARG (1), &value))
+ return;
+
+ shipout_int (obs, value - 1);
+}
+
+/* This section contains the macros "divert", "undivert" and "divnum" for
+ handling diversion. The utility functions used lives in output.c. */
+
+/*-----------------------------------------------------------------.
+| Divert further output to the diversion given by ARGV[1]. Out of |
+| range means discard further output. |
+`-----------------------------------------------------------------*/
+
+static void
+m4_divert (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ int i = 0;
+
+ if (bad_argc (argv[0], argc, 1, 2))
+ return;
+
+ if (argc >= 2 && !numeric_arg (argv[0], ARG (1), &i))
+ return;
+
+ make_diversion (i);
+}
+
+/*-----------------------------------------------------.
+| Expand to the current diversion number, -1 if none. |
+`-----------------------------------------------------*/
+
+static void
+m4_divnum (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 1, 1))
+ return;
+ shipout_int (obs, current_diversion);
+}
+
+/*------------------------------------------------------------------.
+| Bring back the diversion given by the argument list. If none is |
+| specified, bring back all diversions. GNU specific is the option |
+| of undiverting named files, by passing a non-numeric argument to |
+| undivert (). |
+`------------------------------------------------------------------*/
+
+static void
+m4_undivert (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ int i, file;
+ FILE *fp;
+ char *endp;
+
+ if (argc == 1)
+ undivert_all ();
+ else
+ for (i = 1; i < argc; i++)
+ {
+ file = strtol (ARG (i), &endp, 10);
+ if (*endp == '\0' && !isspace (to_uchar (*ARG (i))))
+ insert_diversion (file);
+ else if (no_gnu_extensions)
+ M4ERROR ((warning_status, 0,
+ "non-numeric argument to builtin `%s'", ARG (0)));
+ else
+ {
+ fp = m4_path_search (ARG (i), NULL);
+ if (fp != NULL)
+ {
+ insert_file (fp);
+ if (fclose (fp) == EOF)
+ M4ERROR ((warning_status, errno,
+ "error undiverting `%s'", ARG (i)));
+ }
+ else
+ M4ERROR ((warning_status, errno,
+ "cannot undivert `%s'", ARG (i)));
+ }
+ }
+}
+
+/* This section contains various macros, which does not fall into any
+ specific group. These are "dnl", "shift", "changequote", "changecom"
+ and "changeword". */
+
+/*-----------------------------------------------------------.
+| Delete all subsequent whitespace from input. The function |
+| skip_line () lives in input.c. |
+`-----------------------------------------------------------*/
+
+static void
+m4_dnl (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 1, 1))
+ return;
+
+ skip_line ();
+}
+
+/*--------------------------------------------------------------------.
+| Shift all arguments one to the left, discarding the first |
+| argument. Each output argument is quoted with the current quotes. |
+`--------------------------------------------------------------------*/
+
+static void
+m4_shift (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+ dump_args (obs, argc - 1, argv + 1, ",", true);
+}
+
+/*--------------------------------------------------------------------------.
+| Change the current quotes. The function set_quotes () lives in input.c. |
+`--------------------------------------------------------------------------*/
+
+static void
+m4_changequote (struct obstack *obs M4_GNUC_UNUSED, int argc,
+ token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 1, 3))
+ return;
+
+ /* Explicit NULL distinguishes between empty and missing argument. */
+ set_quotes ((argc >= 2) ? TOKEN_DATA_TEXT (argv[1]) : NULL,
+ (argc >= 3) ? TOKEN_DATA_TEXT (argv[2]) : NULL);
+}
+
+/*-----------------------------------------------------------------.
+| Change the current comment delimiters. The function set_comment |
+| () lives in input.c. |
+`-----------------------------------------------------------------*/
+
+static void
+m4_changecom (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 1, 3))
+ return;
+
+ /* Explicit NULL distinguishes between empty and missing argument. */
+ set_comment ((argc >= 2) ? TOKEN_DATA_TEXT (argv[1]) : NULL,
+ (argc >= 3) ? TOKEN_DATA_TEXT (argv[2]) : NULL);
+}
+
+#ifdef ENABLE_CHANGEWORD
+
+/*---------------------------------------------------------------.
+| Change the regular expression used for breaking the input into |
+| words. The function set_word_regexp () lives in input.c. |
+`---------------------------------------------------------------*/
+
+static void
+m4_changeword (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 2, 2))
+ return;
+
+ set_word_regexp (TOKEN_DATA_TEXT (argv[1]));
+}
+
+#endif /* ENABLE_CHANGEWORD */
+
+/* This section contains macros for inclusion of other files -- "include"
+ and "sinclude". This differs from bringing back diversions, in that
+ the input is scanned before being copied to the output. */
+
+/*---------------------------------------------------------------.
+| Generic include function. Include the file given by the first |
+| argument, if it exists. Complain about inaccessible files iff |
+| SILENT is false. |
+`---------------------------------------------------------------*/
+
+static void
+include (int argc, token_data **argv, bool silent)
+{
+ FILE *fp;
+ char *name;
+
+ if (bad_argc (argv[0], argc, 2, 2))
+ return;
+
+ fp = m4_path_search (ARG (1), &name);
+ if (fp == NULL)
+ {
+ if (!silent)
+ {
+ M4ERROR ((warning_status, errno, "cannot open `%s'", ARG (1)));
+ retcode = EXIT_FAILURE;
+ }
+ return;
+ }
+
+ push_file (fp, name, true);
+ free (name);
+}
+
+/*------------------------------------------------.
+| Include a file, complaining in case of errors. |
+`------------------------------------------------*/
+
+static void
+m4_include (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ include (argc, argv, false);
+}
+
+/*----------------------------------.
+| Include a file, ignoring errors. |
+`----------------------------------*/
+
+static void
+m4_sinclude (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ include (argc, argv, true);
+}
+
+/* More miscellaneous builtins -- "maketemp", "errprint", "__file__",
+ "__line__", and "__program__". The last three are GNU specific. */
+
+/*------------------------------------------------------------------.
+| Use the first argument as at template for a temporary file name. |
+`------------------------------------------------------------------*/
+
+/* Add trailing 'X' to PATTERN of length LEN as necessary, then
+ securely create the file, and place the quoted new file name on
+ OBS. Report errors on behalf of ME. */
+static void
+mkstemp_helper (struct obstack *obs, const char *me, const char *pattern,
+ size_t len)
+{
+ int fd;
+ size_t i;
+ char *name;
+
+ /* Guarantee that there are six trailing 'X' characters, even if the
+ user forgot to supply them. Output must be quoted if
+ successful. */
+ obstack_grow (obs, lquote.string, lquote.length);
+ obstack_grow (obs, pattern, len);
+ for (i = 0; len > 0 && i < 6; i++)
+ if (pattern[len - i - 1] != 'X')
+ break;
+ obstack_grow0 (obs, "XXXXXX", 6 - i);
+ name = (char *) obstack_base (obs) + lquote.length;
+
+ errno = 0;
+ fd = mkstemp (name);
+ if (fd < 0)
+ {
+ M4ERROR ((0, errno, "%s: cannot create tempfile `%s'", me, pattern));
+ obstack_free (obs, obstack_finish (obs));
+ }
+ else
+ {
+ close (fd);
+ /* Remove NUL, then finish quote. */
+ obstack_blank (obs, -1);
+ obstack_grow (obs, rquote.string, rquote.length);
+ }
+}
+
+static void
+m4_maketemp (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 2, 2))
+ return;
+ if (no_gnu_extensions)
+ {
+ /* POSIX states "any trailing 'X' characters [are] replaced with
+ the current process ID as a string", without referencing the
+ file system. Horribly insecure, but we have to do it when we
+ are in traditional mode.
+
+ For reference, Solaris m4 does:
+ maketemp() -> `'
+ maketemp(X) -> `X'
+ maketemp(XX) -> `Xn', where n is last digit of pid
+ maketemp(XXXXXXXX) -> `X00nnnnn', where nnnnn is 16-bit pid
+ */
+ const char *str = ARG (1);
+ int len = strlen (str);
+ int i;
+ int len2;
+
+ M4ERROR ((warning_status, 0, "recommend using mkstemp instead"));
+ for (i = len; i > 1; i--)
+ if (str[i - 1] != 'X')
+ break;
+ obstack_grow (obs, str, i);
+ str = ntoa ((int32_t) getpid (), 10);
+ len2 = strlen (str);
+ if (len2 > len - i)
+ obstack_grow0 (obs, str + len2 - (len - i), len - i);
+ else
+ {
+ while (i++ < len - len2)
+ obstack_1grow (obs, '0');
+ obstack_grow0 (obs, str, len2);
+ }
+ }
+ else
+ mkstemp_helper (obs, ARG (0), ARG (1), strlen (ARG (1)));
+}
+
+static void
+m4_mkstemp (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 2, 2))
+ return;
+ mkstemp_helper (obs, ARG (0), ARG (1), strlen (ARG (1)));
+}
+
+/*----------------------------------------.
+| Print all arguments on standard error. |
+`----------------------------------------*/
+
+static void
+m4_errprint (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+ dump_args (obs, argc, argv, " ", false);
+ obstack_1grow (obs, '\0');
+ debug_flush_files ();
+ xfprintf (stderr, "%s", (char *) obstack_finish (obs));
+ fflush (stderr);
+}
+
+static void
+m4___file__ (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 1, 1))
+ return;
+ obstack_grow (obs, lquote.string, lquote.length);
+ obstack_grow (obs, current_file, strlen (current_file));
+ obstack_grow (obs, rquote.string, rquote.length);
+}
+
+static void
+m4___line__ (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 1, 1))
+ return;
+ shipout_int (obs, current_line);
+}
+
+static void
+m4___program__ (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 1, 1))
+ return;
+ obstack_grow (obs, lquote.string, lquote.length);
+ obstack_grow (obs, program_name, strlen (program_name));
+ obstack_grow (obs, rquote.string, rquote.length);
+}
+
+/* This section contains various macros for exiting, saving input until
+ EOF is seen, and tracing macro calls. That is: "m4exit", "m4wrap",
+ "traceon" and "traceoff". */
+
+/*----------------------------------------------------------.
+| Exit immediately, with exit status specified by the first |
+| argument, or 0 if no arguments are present. |
+`----------------------------------------------------------*/
+
+static void M4_GNUC_NORETURN
+m4_m4exit (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ int exit_code = EXIT_SUCCESS;
+
+ /* Warn on bad arguments, but still exit. */
+ bad_argc (argv[0], argc, 1, 2);
+ if (argc >= 2 && !numeric_arg (argv[0], ARG (1), &exit_code))
+ exit_code = EXIT_FAILURE;
+ if (exit_code < 0 || exit_code > 255)
+ {
+ M4ERROR ((warning_status, 0,
+ "exit status out of range: `%d'", exit_code));
+ exit_code = EXIT_FAILURE;
+ }
+ /* Change debug stream back to stderr, to force flushing debug stream and
+ detect any errors it might have encountered. */
+ debug_set_output (NULL);
+ debug_flush_files ();
+ if (exit_code == EXIT_SUCCESS && retcode != EXIT_SUCCESS)
+ exit_code = retcode;
+ /* Propagate non-zero status to atexit handlers. */
+ if (exit_code != EXIT_SUCCESS)
+ exit_failure = exit_code;
+ exit (exit_code);
+}
+
+/*------------------------------------------------------------------.
+| Save the argument text until EOF has been seen, allowing for user |
+| specified cleanup action. GNU version saves all arguments, the |
+| standard version only the first. |
+`------------------------------------------------------------------*/
+
+static void
+m4_m4wrap (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+ if (no_gnu_extensions)
+ obstack_grow (obs, ARG (1), strlen (ARG (1)));
+ else
+ dump_args (obs, argc, argv, " ", false);
+ obstack_1grow (obs, '\0');
+ push_wrapup ((char *) obstack_finish (obs));
+}
+
+/* Enable tracing of all specified macros, or all, if none is specified.
+ Tracing is disabled by default, when a macro is defined. This can be
+ overridden by the "t" debug flag. */
+
+/*------------------------------------------------------------------.
+| Set_trace () is used by "traceon" and "traceoff" to enable and |
+| disable tracing of a macro. It disables tracing if DATA is NULL, |
+| otherwise it enables tracing. |
+`------------------------------------------------------------------*/
+
+static void
+set_trace (symbol *sym, void *data)
+{
+ SYMBOL_TRACED (sym) = data != NULL;
+ /* Remove placeholder from table if macro is undefined and untraced. */
+ if (SYMBOL_TYPE (sym) == TOKEN_VOID && data == NULL)
+ lookup_symbol (SYMBOL_NAME (sym), SYMBOL_POPDEF);
+}
+
+static void
+m4_traceon (struct obstack *obs, int argc, token_data **argv)
+{
+ symbol *s;
+ int i;
+
+ if (argc == 1)
+ hack_all_symbols (set_trace, obs);
+ else
+ for (i = 1; i < argc; i++)
+ {
+ s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
+ if (!s)
+ s = lookup_symbol (ARG (i), SYMBOL_INSERT);
+ set_trace (s, obs);
+ }
+}
+
+/*------------------------------------------------------------------------.
+| Disable tracing of all specified macros, or all, if none is specified. |
+`------------------------------------------------------------------------*/
+
+static void
+m4_traceoff (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ symbol *s;
+ int i;
+
+ if (argc == 1)
+ hack_all_symbols (set_trace, NULL);
+ else
+ for (i = 1; i < argc; i++)
+ {
+ s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP);
+ if (s != NULL)
+ set_trace (s, NULL);
+ }
+}
+
+/*------------------------------------------------------------------.
+| On-the-fly control of the format of the tracing output. It takes |
+| one argument, which is a character string like given to the -d |
+| option, or none in which case the debug_level is zeroed. |
+`------------------------------------------------------------------*/
+
+static void
+m4_debugmode (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ int new_debug_level;
+ int change_flag;
+
+ if (bad_argc (argv[0], argc, 1, 2))
+ return;
+
+ if (argc == 1)
+ debug_level = 0;
+ else
+ {
+ if (ARG (1)[0] == '+' || ARG (1)[0] == '-')
+ {
+ change_flag = ARG (1)[0];
+ new_debug_level = debug_decode (ARG (1) + 1);
+ }
+ else
+ {
+ change_flag = 0;
+ new_debug_level = debug_decode (ARG (1));
+ }
+
+ if (new_debug_level < 0)
+ M4ERROR ((warning_status, 0,
+ "Debugmode: bad debug flags: `%s'", ARG (1)));
+ else
+ {
+ switch (change_flag)
+ {
+ case 0:
+ debug_level = new_debug_level;
+ break;
+
+ case '+':
+ debug_level |= new_debug_level;
+ break;
+
+ case '-':
+ debug_level &= ~new_debug_level;
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad flag in m4_debugmode ()"));
+ abort ();
+ }
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------.
+| Specify the destination of the debugging output. With one argument, the |
+| argument is taken as a file name, with no arguments, revert to stderr. |
+`-------------------------------------------------------------------------*/
+
+static void
+m4_debugfile (struct obstack *obs M4_GNUC_UNUSED, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 1, 2))
+ return;
+
+ if (argc == 1)
+ debug_set_output (NULL);
+ else if (!debug_set_output (ARG (1)))
+ M4ERROR ((warning_status, errno,
+ "cannot set debug file `%s'", ARG (1)));
+}
+
+/* This section contains text processing macros: "len", "index",
+ "substr", "translit", "format", "regexp" and "patsubst". The last
+ three are GNU specific. */
+
+/*---------------------------------------------.
+| Expand to the length of the first argument. |
+`---------------------------------------------*/
+
+static void
+m4_len (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 2, 2))
+ return;
+ shipout_int (obs, strlen (ARG (1)));
+}
+
+/*-------------------------------------------------------------------.
+| The macro expands to the first index of the second argument in the |
+| first argument. |
+`-------------------------------------------------------------------*/
+
+static void
+m4_index (struct obstack *obs, int argc, token_data **argv)
+{
+ const char *haystack;
+ const char *result;
+ int retval;
+
+ if (bad_argc (argv[0], argc, 3, 3))
+ {
+ /* builtin(`index') is blank, but index(`abc') is 0. */
+ if (argc == 2)
+ shipout_int (obs, 0);
+ return;
+ }
+
+ haystack = ARG (1);
+ result = strstr (haystack, ARG (2));
+ retval = result ? result - haystack : -1;
+
+ shipout_int (obs, retval);
+}
+
+/*-----------------------------------------------------------------.
+| The macro "substr" extracts substrings from the first argument, |
+| starting from the index given by the second argument, extending |
+| for a length given by the third argument. If the third argument |
+| is missing, the substring extends to the end of the first |
+| argument. |
+`-----------------------------------------------------------------*/
+
+static void
+m4_substr (struct obstack *obs, int argc, token_data **argv)
+{
+ int start = 0;
+ int length, avail;
+
+ if (bad_argc (argv[0], argc, 3, 4))
+ {
+ /* builtin(`substr') is blank, but substr(`abc') is abc. */
+ if (argc == 2)
+ obstack_grow (obs, ARG (1), strlen (ARG (1)));
+ return;
+ }
+
+ length = avail = strlen (ARG (1));
+ if (!numeric_arg (argv[0], ARG (2), &start))
+ return;
+
+ if (argc >= 4 && !numeric_arg (argv[0], ARG (3), &length))
+ return;
+
+ if (start < 0 || length <= 0 || start >= avail)
+ return;
+
+ if (start + length > avail)
+ length = avail - start;
+ obstack_grow (obs, ARG (1) + start, length);
+}
+
+/*------------------------------------------------------------------.
+| For "translit", ranges are allowed in the second and third |
+| argument. They are expanded in the following function, and the |
+| expanded strings, without any ranges left, are used to translate |
+| the characters of the first argument. A single - (dash) can be |
+| included in the strings by being the first or the last character |
+| in the string. If the first character in a range is after the |
+| first in the character set, the range is made backwards, thus 9-0 |
+| is the string 9876543210. |
+`------------------------------------------------------------------*/
+
+static const char *
+expand_ranges (const char *s, struct obstack *obs)
+{
+ unsigned char from;
+ unsigned char to;
+
+ for (from = '\0'; *s != '\0'; from = to_uchar (*s++))
+ {
+ if (*s == '-' && from != '\0')
+ {
+ to = to_uchar (*++s);
+ if (to == '\0')
+ {
+ /* trailing dash */
+ obstack_1grow (obs, '-');
+ break;
+ }
+ else if (from <= to)
+ {
+ while (from++ < to)
+ obstack_1grow (obs, from);
+ }
+ else
+ {
+ while (--from >= to)
+ obstack_1grow (obs, from);
+ }
+ }
+ else
+ obstack_1grow (obs, *s);
+ }
+ obstack_1grow (obs, '\0');
+ return (char *) obstack_finish (obs);
+}
+
+/*-----------------------------------------------------------------.
+| The macro "translit" translates all characters in the first |
+| argument, which are present in the second argument, into the |
+| corresponding character from the third argument. If the third |
+| argument is shorter than the second, the extra characters in the |
+| second argument are deleted from the first. |
+`-----------------------------------------------------------------*/
+
+static void
+m4_translit (struct obstack *obs, int argc, token_data **argv)
+{
+ const char *data = ARG (1);
+ const char *from = ARG (2);
+ const char *to;
+ char map[UCHAR_MAX + 1];
+ char found[UCHAR_MAX + 1];
+ unsigned char ch;
+
+ if (bad_argc (argv[0], argc, 3, 4) || !*data || !*from)
+ {
+ /* builtin(`translit') is blank, but translit(`abc') is abc. */
+ if (2 <= argc)
+ obstack_grow (obs, data, strlen (data));
+ return;
+ }
+
+ to = ARG (3);
+ if (strchr (to, '-') != NULL)
+ {
+ to = expand_ranges (to, obs);
+ assert (to && *to);
+ }
+
+ /* If there are only one or two bytes to replace, it is faster to
+ use memchr2. Using expand_ranges does nothing unless there are
+ at least three bytes. */
+ if (!from[1] || !from[2])
+ {
+ const char *p;
+ size_t len = strlen (data);
+ while ((p = (char *) memchr2 (data, from[0], from[1], len)))
+ {
+ obstack_grow (obs, data, p - data);
+ len -= p - data;
+ if (!len)
+ return;
+ data = p + 1;
+ len--;
+ if (*p == from[0] && to[0])
+ obstack_1grow (obs, to[0]);
+ else if (*p == from[1] && to[0] && to[1])
+ obstack_1grow (obs, to[1]);
+ }
+ obstack_grow (obs, data, len);
+ return;
+ }
+
+ if (strchr (from, '-') != NULL)
+ {
+ from = expand_ranges (from, obs);
+ assert (from && *from);
+ }
+
+ /* Calling strchr(from) for each character in data is quadratic,
+ since both strings can be arbitrarily long. Instead, create a
+ from-to mapping in one pass of from, then use that map in one
+ pass of data, for linear behavior. Traditional behavior is that
+ only the first instance of a character in from is consulted,
+ hence the found map. */
+ memset (map, 0, sizeof map);
+ memset (found, 0, sizeof found);
+ for ( ; (ch = *from) != '\0'; from++)
+ {
+ if (! found[ch])
+ {
+ found[ch] = 1;
+ map[ch] = *to;
+ }
+ if (*to != '\0')
+ to++;
+ }
+
+ for (data = ARG (1); (ch = *data) != '\0'; data++)
+ {
+ if (! found[ch])
+ obstack_1grow (obs, ch);
+ else if (map[ch])
+ obstack_1grow (obs, map[ch]);
+ }
+}
+
+/*-------------------------------------------------------------------.
+| Frontend for printf like formatting. The function format () lives |
+| in the file format.c. |
+`-------------------------------------------------------------------*/
+
+static void
+m4_format (struct obstack *obs, int argc, token_data **argv)
+{
+ if (bad_argc (argv[0], argc, 2, -1))
+ return;
+ expand_format (obs, argc - 1, argv + 1);
+}
+
+/*------------------------------------------------------------------.
+| Function to perform substitution by regular expressions. Used by |
+| the builtins regexp and patsubst. The changed text is placed on |
+| the obstack. The substitution is REPL, with \& substituted by |
+| this part of VICTIM matched by the last whole regular expression, |
+| taken from REGS[0], and \N substituted by the text matched by the |
+| Nth parenthesized sub-expression, taken from REGS[N]. |
+`------------------------------------------------------------------*/
+
+static int substitute_warned = 0;
+
+static void
+substitute (struct obstack *obs, const char *victim, const char *repl,
+ struct re_registers *regs)
+{
+ int ch;
+ __re_size_t ind;
+ while (1)
+ {
+ const char *backslash = strchr (repl, '\\');
+ if (!backslash)
+ {
+ obstack_grow (obs, repl, strlen (repl));
+ return;
+ }
+ obstack_grow (obs, repl, backslash - repl);
+ repl = backslash;
+ ch = *++repl;
+ switch (ch)
+ {
+ case '0':
+ if (!substitute_warned)
+ {
+ M4ERROR ((warning_status, 0, "\
+Warning: \\0 will disappear, use \\& instead in replacements"));
+ substitute_warned = 1;
+ }
+ /* Fall through. */
+
+ case '&':
+ obstack_grow (obs, victim + regs->start[0],
+ regs->end[0] - regs->start[0]);
+ repl++;
+ break;
+
+ case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ ind = ch -= '0';
+ if (regs->num_regs - 1 <= ind)
+ M4ERROR ((warning_status, 0,
+ "Warning: sub-expression %d not present", ch));
+ else if (regs->end[ch] > 0)
+ obstack_grow (obs, victim + regs->start[ch],
+ regs->end[ch] - regs->start[ch]);
+ repl++;
+ break;
+
+ case '\0':
+ M4ERROR ((warning_status, 0,
+ "Warning: trailing \\ ignored in replacement"));
+ return;
+
+ default:
+ obstack_1grow (obs, ch);
+ repl++;
+ break;
+ }
+ }
+}
+
+/*------------------------------------------.
+| Initialize regular expression variables. |
+`------------------------------------------*/
+
+void
+init_pattern_buffer (struct re_pattern_buffer *buf, struct re_registers *regs)
+{
+ buf->translate = NULL;
+ buf->fastmap = NULL;
+ buf->buffer = NULL;
+ buf->allocated = 0;
+ if (regs)
+ {
+ regs->start = NULL;
+ regs->end = NULL;
+ }
+}
+
+/*------------------------------------------------------------------.
+| Regular expression version of index. Given two arguments, expand |
+| to the index of the first match of the second argument (a regexp) |
+| in the first. Expand to -1 if here is no match. Given a third |
+| argument, it changes the expansion to this argument. |
+`------------------------------------------------------------------*/
+
+static void
+m4_regexp (struct obstack *obs, int argc, token_data **argv)
+{
+ const char *victim; /* first argument */
+ const char *regexp; /* regular expression */
+ const char *repl; /* replacement string */
+
+ struct re_pattern_buffer buf; /* compiled regular expression */
+ struct re_registers regs; /* for subexpression matches */
+ const char *msg; /* error message from re_compile_pattern */
+ int startpos; /* start position of match */
+ int length; /* length of first argument */
+
+ if (bad_argc (argv[0], argc, 3, 4))
+ {
+ /* builtin(`regexp') is blank, but regexp(`abc') is 0. */
+ if (argc == 2)
+ shipout_int (obs, 0);
+ return;
+ }
+
+ victim = TOKEN_DATA_TEXT (argv[1]);
+ regexp = TOKEN_DATA_TEXT (argv[2]);
+
+ init_pattern_buffer (&buf, &regs);
+ msg = re_compile_pattern (regexp, strlen (regexp), &buf);
+
+ if (msg != NULL)
+ {
+ M4ERROR ((warning_status, 0,
+ "bad regular expression: `%s': %s", regexp, msg));
+ free_pattern_buffer (&buf, &regs);
+ return;
+ }
+
+ length = strlen (victim);
+ /* Avoid overhead of allocating regs if we won't use it. */
+ startpos = re_search (&buf, victim, length, 0, length,
+ argc == 3 ? NULL : &regs);
+
+ if (startpos == -2)
+ M4ERROR ((warning_status, 0,
+ "error matching regular expression `%s'", regexp));
+ else if (argc == 3)
+ shipout_int (obs, startpos);
+ else if (startpos >= 0)
+ {
+ repl = TOKEN_DATA_TEXT (argv[3]);
+ substitute (obs, victim, repl, &regs);
+ }
+
+ free_pattern_buffer (&buf, &regs);
+}
+
+/*--------------------------------------------------------------------------.
+| Substitute all matches of a regexp occuring in a string. Each match of |
+| the second argument (a regexp) in the first argument is changed to the |
+| third argument, with \& substituted by the matched text, and \N |
+| substituted by the text matched by the Nth parenthesized sub-expression. |
+`--------------------------------------------------------------------------*/
+
+static void
+m4_patsubst (struct obstack *obs, int argc, token_data **argv)
+{
+ const char *victim; /* first argument */
+ const char *regexp; /* regular expression */
+
+ struct re_pattern_buffer buf; /* compiled regular expression */
+ struct re_registers regs; /* for subexpression matches */
+ const char *msg; /* error message from re_compile_pattern */
+ int matchpos; /* start position of match */
+ int offset; /* current match offset */
+ int length; /* length of first argument */
+
+ if (bad_argc (argv[0], argc, 3, 4))
+ {
+ /* builtin(`patsubst') is blank, but patsubst(`abc') is abc. */
+ if (argc == 2)
+ obstack_grow (obs, ARG (1), strlen (ARG (1)));
+ return;
+ }
+
+ regexp = TOKEN_DATA_TEXT (argv[2]);
+
+ init_pattern_buffer (&buf, &regs);
+ msg = re_compile_pattern (regexp, strlen (regexp), &buf);
+
+ if (msg != NULL)
+ {
+ M4ERROR ((warning_status, 0,
+ "bad regular expression `%s': %s", regexp, msg));
+ free (buf.buffer);
+ return;
+ }
+
+ victim = TOKEN_DATA_TEXT (argv[1]);
+ length = strlen (victim);
+
+ offset = 0;
+ while (offset <= length)
+ {
+ matchpos = re_search (&buf, victim, length,
+ offset, length - offset, &regs);
+ if (matchpos < 0)
+ {
+
+ /* Match failed -- either error or there is no match in the
+ rest of the string, in which case the rest of the string is
+ copied verbatim. */
+
+ if (matchpos == -2)
+ M4ERROR ((warning_status, 0,
+ "error matching regular expression `%s'", regexp));
+ else if (offset < length)
+ obstack_grow (obs, victim + offset, length - offset);
+ break;
+ }
+
+ /* Copy the part of the string that was skipped by re_search (). */
+
+ if (matchpos > offset)
+ obstack_grow (obs, victim + offset, matchpos - offset);
+
+ /* Handle the part of the string that was covered by the match. */
+
+ substitute (obs, victim, ARG (3), &regs);
+
+ /* Update the offset to the end of the match. If the regexp
+ matched a null string, advance offset one more, to avoid
+ infinite loops. */
+
+ offset = regs.end[0];
+ if (regs.start[0] == regs.end[0])
+ obstack_1grow (obs, victim[offset++]);
+ }
+ obstack_1grow (obs, '\0');
+
+ free_pattern_buffer (&buf, &regs);
+}
+
+/* Finally, a placeholder builtin. This builtin is not installed by
+ default, but when reading back frozen files, this is associated
+ with any builtin we don't recognize (for example, if the frozen
+ file was created with a changeword capable m4, but is then loaded
+ by a different m4 that does not support changeword). This way, we
+ can keep 'm4 -R' quiet in the common case that the user did not
+ know or care about the builtin when the frozen file was created,
+ while still flagging it as a potential error if an attempt is made
+ to actually use the builtin. */
+
+/*--------------------------------------------------------------------.
+| Issue a warning that this macro is a placeholder for an unsupported |
+| builtin that was requested while reloading a frozen file. |
+`--------------------------------------------------------------------*/
+
+void
+m4_placeholder (struct obstack *obs M4_GNUC_UNUSED, int argc,
+ token_data **argv)
+{
+ M4ERROR ((warning_status, 0, "\
+builtin `%s' requested by frozen file is not supported", ARG (0)));
+}
+
+/*-------------------------------------------------------------------.
+| This function handles all expansion of user defined and predefined |
+| macros. It is called with an obstack OBS, where the macros |
+| expansion will be placed, as an unfinished object. SYM points to |
+| the macro definition, giving the expansion text. ARGC and ARGV |
+| are the arguments, as usual. |
+`-------------------------------------------------------------------*/
+
+void
+expand_user_macro (struct obstack *obs, symbol *sym,
+ int argc, token_data **argv)
+{
+ const char *text = SYMBOL_TEXT (sym);
+ int i;
+ while (1)
+ {
+ const char *dollar = strchr (text, '$');
+ if (!dollar)
+ {
+ obstack_grow (obs, text, strlen (text));
+ return;
+ }
+ obstack_grow (obs, text, dollar - text);
+ text = dollar;
+ switch (*++text)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (no_gnu_extensions)
+ {
+ i = *text++ - '0';
+ }
+ else
+ {
+ for (i = 0; isdigit (to_uchar (*text)); text++)
+ i = i*10 + (*text - '0');
+ }
+ if (i < argc)
+ obstack_grow (obs, TOKEN_DATA_TEXT (argv[i]),
+ strlen (TOKEN_DATA_TEXT (argv[i])));
+ break;
+
+ case '#': /* number of arguments */
+ shipout_int (obs, argc - 1);
+ text++;
+ break;
+
+ case '*': /* all arguments */
+ case '@': /* ... same, but quoted */
+ dump_args (obs, argc, argv, ",", *text == '@');
+ text++;
+ break;
+
+ default:
+ obstack_1grow (obs, '$');
+ break;
+ }
+ }
+}
diff --git a/src/debug.c b/src/debug.c
new file mode 100644
index 0000000..0fef85c
--- /dev/null
+++ b/src/debug.c
@@ -0,0 +1,444 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1991-1994, 2004, 2006-2007, 2009-2011 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/>.
+*/
+
+#include "m4.h"
+
+#include <stdarg.h>
+#include <sys/stat.h>
+
+/* File for debugging output. */
+FILE *debug = NULL;
+
+/* Obstack for trace messages. */
+static struct obstack trace;
+
+extern int expansion_level;
+
+static void debug_set_file (FILE *);
+
+/*----------------------------------.
+| Initialise the debugging module. |
+`----------------------------------*/
+
+void
+debug_init (void)
+{
+ debug_set_file (stderr);
+ obstack_init (&trace);
+}
+
+/*-----------------------------------------------------------------.
+| Function to decode the debugging flags OPTS. Used by main while |
+| processing option -d, and by the builtin debugmode (). |
+`-----------------------------------------------------------------*/
+
+int
+debug_decode (const char *opts)
+{
+ int level;
+
+ if (opts == NULL || *opts == '\0')
+ level = DEBUG_TRACE_DEFAULT;
+ else
+ {
+ for (level = 0; *opts; opts++)
+ {
+ switch (*opts)
+ {
+ case 'a':
+ level |= DEBUG_TRACE_ARGS;
+ break;
+
+ case 'e':
+ level |= DEBUG_TRACE_EXPANSION;
+ break;
+
+ case 'q':
+ level |= DEBUG_TRACE_QUOTE;
+ break;
+
+ case 't':
+ level |= DEBUG_TRACE_ALL;
+ break;
+
+ case 'l':
+ level |= DEBUG_TRACE_LINE;
+ break;
+
+ case 'f':
+ level |= DEBUG_TRACE_FILE;
+ break;
+
+ case 'p':
+ level |= DEBUG_TRACE_PATH;
+ break;
+
+ case 'c':
+ level |= DEBUG_TRACE_CALL;
+ break;
+
+ case 'i':
+ level |= DEBUG_TRACE_INPUT;
+ break;
+
+ case 'x':
+ level |= DEBUG_TRACE_CALLID;
+ break;
+
+ case 'V':
+ level |= DEBUG_TRACE_VERBOSE;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+ }
+
+ /* This is to avoid screwing up the trace output due to changes in the
+ debug_level. */
+
+ obstack_free (&trace, obstack_finish (&trace));
+
+ return level;
+}
+
+/*-----------------------------------------------------------------.
+| Change the debug output stream to FP. If the underlying file is |
+| the same as stdout, use stdout instead so that debug messages |
+| appear in the correct relative position. |
+`-----------------------------------------------------------------*/
+
+static void
+debug_set_file (FILE *fp)
+{
+ struct stat stdout_stat, debug_stat;
+
+ if (debug != NULL && debug != stderr && debug != stdout
+ && close_stream (debug) != 0)
+ {
+ M4ERROR ((warning_status, errno, "error writing to debug stream"));
+ retcode = EXIT_FAILURE;
+ }
+ debug = fp;
+
+ if (debug != NULL && debug != stdout)
+ {
+ if (fstat (STDOUT_FILENO, &stdout_stat) < 0)
+ return;
+ if (fstat (fileno (debug), &debug_stat) < 0)
+ return;
+
+ /* mingw has a bug where fstat on a regular file reports st_ino
+ of 0. On normal system, st_ino should never be 0. */
+ if (stdout_stat.st_ino == debug_stat.st_ino
+ && stdout_stat.st_dev == debug_stat.st_dev
+ && stdout_stat.st_ino != 0)
+ {
+ if (debug != stderr && close_stream (debug) != 0)
+ {
+ M4ERROR ((warning_status, errno,
+ "error writing to debug stream"));
+ retcode = EXIT_FAILURE;
+ }
+ debug = stdout;
+ }
+ }
+}
+
+/*-----------------------------------------------------------.
+| Serialize files. Used before executing a system command. |
+`-----------------------------------------------------------*/
+
+void
+debug_flush_files (void)
+{
+ fflush (stdout);
+ fflush (stderr);
+ if (debug != NULL && debug != stdout && debug != stderr)
+ fflush (debug);
+ /* POSIX requires that if m4 doesn't consume all input, but stdin is
+ opened on a seekable file, that the file pointer be left at the
+ next character on exit (but places no restrictions on the file
+ pointer location on a non-seekable file). It also requires that
+ fflush() followed by fseeko() on an input file set the underlying
+ file pointer, and gnulib guarantees these semantics. However,
+ fflush() on a non-seekable file can lose buffered data, which we
+ might otherwise want to process after syscmd. Hence, we must
+ check whether stdin is seekable. We must also be tolerant of
+ operating with stdin closed, so we don't report any failures in
+ this attempt. The stdio-safer module and friends are essential,
+ so that if stdin was closed, this lseek is not on some other file
+ that we have since opened. */
+ if (lseek (STDIN_FILENO, 0, SEEK_CUR) >= 0
+ && fflush (stdin) == 0)
+ {
+ fseeko (stdin, 0, SEEK_CUR);
+ }
+}
+
+/*--------------------------------------------------------------.
+| Change the debug output to file NAME. If NAME is NULL, debug |
+| output is reverted to stderr, and if empty, debug output is |
+| discarded. Return true iff the output stream was changed. |
+`--------------------------------------------------------------*/
+
+bool
+debug_set_output (const char *name)
+{
+ FILE *fp;
+
+ if (name == NULL)
+ debug_set_file (stderr);
+ else if (*name == '\0')
+ debug_set_file (NULL);
+ else
+ {
+ fp = fopen (name, "a");
+ if (fp == NULL)
+ return false;
+
+ if (set_cloexec_flag (fileno (fp), true) != 0)
+ M4ERROR ((warning_status, errno,
+ "Warning: cannot protect debug file across forks"));
+ debug_set_file (fp);
+ }
+ return true;
+}
+
+/*--------------------------------------------------------------.
+| Print the header of a one-line debug message, starting by "m4 |
+| debug". |
+`--------------------------------------------------------------*/
+
+void
+debug_message_prefix (void)
+{
+ xfprintf (debug, "m4debug:");
+ if (current_line)
+ {
+ if (debug_level & DEBUG_TRACE_FILE)
+ xfprintf (debug, "%s:", current_file);
+ if (debug_level & DEBUG_TRACE_LINE)
+ xfprintf (debug, "%d:", current_line);
+ }
+ putc (' ', debug);
+}
+
+/* The rest of this file contains the functions for macro tracing output.
+ All tracing output for a macro call is collected on an obstack TRACE,
+ and printed whenever the line is complete. This prevents tracing
+ output from interfering with other debug messages generated by the
+ various builtins. */
+
+/*------------------------------------------------------------------.
+| Tracing output is formatted here, by a simplified |
+| printf-to-obstack function trace_format (). Understands only %S, |
+| %s, %d, %l (optional left quote) and %r (optional right quote). |
+`------------------------------------------------------------------*/
+
+static void
+trace_format (const char *fmt, ...)
+{
+ va_list args;
+ char ch;
+
+ int d;
+ const char *s;
+ int slen;
+ int maxlen;
+
+ va_start (args, fmt);
+
+ while (true)
+ {
+ while ((ch = *fmt++) != '\0' && ch != '%')
+ obstack_1grow (&trace, ch);
+
+ if (ch == '\0')
+ break;
+
+ maxlen = 0;
+ switch (*fmt++)
+ {
+ case 'S':
+ maxlen = max_debug_argument_length;
+ /* fall through */
+
+ case 's':
+ s = va_arg (args, const char *);
+ break;
+
+ case 'l':
+ s = (debug_level & DEBUG_TRACE_QUOTE) ? lquote.string : "";
+ break;
+
+ case 'r':
+ s = (debug_level & DEBUG_TRACE_QUOTE) ? rquote.string : "";
+ break;
+
+ case 'd':
+ d = va_arg (args, int);
+ s = ntoa (d, 10);
+ break;
+
+ default:
+ s = "";
+ break;
+ }
+
+ slen = strlen (s);
+ if (maxlen == 0 || maxlen > slen)
+ obstack_grow (&trace, s, slen);
+ else
+ {
+ obstack_grow (&trace, s, maxlen);
+ obstack_grow (&trace, "...", 3);
+ }
+ }
+
+ va_end (args);
+}
+
+/*------------------------------------------------------------------.
+| Format the standard header attached to all tracing output lines. |
+`------------------------------------------------------------------*/
+
+static void
+trace_header (int id)
+{
+ trace_format ("m4trace:");
+ if (current_line)
+ {
+ if (debug_level & DEBUG_TRACE_FILE)
+ trace_format ("%s:", current_file);
+ if (debug_level & DEBUG_TRACE_LINE)
+ trace_format ("%d:", current_line);
+ }
+ trace_format (" -%d- ", expansion_level);
+ if (debug_level & DEBUG_TRACE_CALLID)
+ trace_format ("id %d: ", id);
+}
+
+/*----------------------------------------------------.
+| Print current tracing line, and clear the obstack. |
+`----------------------------------------------------*/
+
+static void
+trace_flush (void)
+{
+ char *line;
+
+ obstack_1grow (&trace, '\0');
+ line = (char *) obstack_finish (&trace);
+ DEBUG_PRINT1 ("%s\n", line);
+ obstack_free (&trace, line);
+}
+
+/*-------------------------------------------------------------.
+| Do pre-argument-collction tracing for macro NAME. Used from |
+| expand_macro (). |
+`-------------------------------------------------------------*/
+
+void
+trace_prepre (const char *name, int id)
+{
+ trace_header (id);
+ trace_format ("%s ...", name);
+ trace_flush ();
+}
+
+/*--------------------------------------------------------------.
+| Format the parts of a trace line, that can be made before the |
+| macro is actually expanded. Used from expand_macro (). |
+`--------------------------------------------------------------*/
+
+void
+trace_pre (const char *name, int id, int argc, token_data **argv)
+{
+ int i;
+ const builtin *bp;
+
+ trace_header (id);
+ trace_format ("%s", name);
+
+ if (argc > 1 && (debug_level & DEBUG_TRACE_ARGS))
+ {
+ trace_format ("(");
+
+ for (i = 1; i < argc; i++)
+ {
+ if (i != 1)
+ trace_format (", ");
+
+ switch (TOKEN_DATA_TYPE (argv[i]))
+ {
+ case TOKEN_TEXT:
+ trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv[i]));
+ break;
+
+ case TOKEN_FUNC:
+ bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[i]));
+ if (bp == NULL)
+ {
+ M4ERROR ((warning_status, 0, "\
+INTERNAL ERROR: builtin not found in builtin table! (trace_pre ())"));
+ abort ();
+ }
+ trace_format ("<%s>", bp->name);
+ break;
+
+ case TOKEN_VOID:
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad token data type (trace_pre ())"));
+ abort ();
+ }
+
+ }
+ trace_format (")");
+ }
+
+ if (debug_level & DEBUG_TRACE_CALL)
+ {
+ trace_format (" -> ???");
+ trace_flush ();
+ }
+}
+
+/*-------------------------------------------------------------------.
+| Format the final part of a trace line and print it all. Used from |
+| expand_macro (). |
+`-------------------------------------------------------------------*/
+
+void
+trace_post (const char *name, int id, int argc, const char *expanded)
+{
+ if (debug_level & DEBUG_TRACE_CALL)
+ {
+ trace_header (id);
+ trace_format ("%s%s", name, (argc > 1) ? "(...)" : "");
+ }
+
+ if (expanded && (debug_level & DEBUG_TRACE_EXPANSION))
+ trace_format (" -> %l%S%r", expanded);
+ trace_flush ();
+}
diff --git a/src/eval.c b/src/eval.c
new file mode 100644
index 0000000..b74bce8
--- /dev/null
+++ b/src/eval.c
@@ -0,0 +1,855 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2006-2007, 2009-2011 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/>.
+*/
+
+/* This file contains the functions to evaluate integer expressions for
+ the "eval" macro. It is a little, fairly self-contained module, with
+ its own scanner, and a recursive descent parser. The only entry point
+ is evaluate (). */
+
+#include "m4.h"
+
+/* Evaluates token types. */
+
+typedef enum eval_token
+ {
+ ERROR, BADOP,
+ PLUS, MINUS,
+ EXPONENT,
+ TIMES, DIVIDE, MODULO,
+ ASSIGN, EQ, NOTEQ, GT, GTEQ, LS, LSEQ,
+ LSHIFT, RSHIFT,
+ LNOT, LAND, LOR,
+ NOT, AND, OR, XOR,
+ LEFTP, RIGHTP,
+ NUMBER, EOTEXT
+ }
+eval_token;
+
+/* Error types. */
+
+typedef enum eval_error
+ {
+ NO_ERROR,
+ DIVIDE_ZERO,
+ MODULO_ZERO,
+ NEGATIVE_EXPONENT,
+ /* All errors prior to SYNTAX_ERROR can be ignored in a dead
+ branch of && and ||. All errors after are just more details
+ about a syntax error. */
+ SYNTAX_ERROR,
+ MISSING_RIGHT,
+ UNKNOWN_INPUT,
+ EXCESS_INPUT,
+ INVALID_OPERATOR
+ }
+eval_error;
+
+static eval_error logical_or_term (eval_token, int32_t *);
+static eval_error logical_and_term (eval_token, int32_t *);
+static eval_error or_term (eval_token, int32_t *);
+static eval_error xor_term (eval_token, int32_t *);
+static eval_error and_term (eval_token, int32_t *);
+static eval_error equality_term (eval_token, int32_t *);
+static eval_error cmp_term (eval_token, int32_t *);
+static eval_error shift_term (eval_token, int32_t *);
+static eval_error add_term (eval_token, int32_t *);
+static eval_error mult_term (eval_token, int32_t *);
+static eval_error exp_term (eval_token, int32_t *);
+static eval_error unary_term (eval_token, int32_t *);
+static eval_error simple_term (eval_token, int32_t *);
+
+/*--------------------.
+| Lexical functions. |
+`--------------------*/
+
+/* Pointer to next character of input text. */
+static const char *eval_text;
+
+/* Value of eval_text, from before last call of eval_lex (). This is so we
+ can back up, if we have read too much. */
+static const char *last_text;
+
+static void
+eval_init_lex (const char *text)
+{
+ eval_text = text;
+ last_text = NULL;
+}
+
+static void
+eval_undo (void)
+{
+ eval_text = last_text;
+}
+
+/* VAL is numerical value, if any. */
+
+static eval_token
+eval_lex (int32_t *val)
+{
+ while (isspace (to_uchar (*eval_text)))
+ eval_text++;
+
+ last_text = eval_text;
+
+ if (*eval_text == '\0')
+ return EOTEXT;
+
+ if (isdigit (to_uchar (*eval_text)))
+ {
+ int base, digit;
+
+ if (*eval_text == '0')
+ {
+ eval_text++;
+ switch (*eval_text)
+ {
+ case 'x':
+ case 'X':
+ base = 16;
+ eval_text++;
+ break;
+
+ case 'b':
+ case 'B':
+ base = 2;
+ eval_text++;
+ break;
+
+ case 'r':
+ case 'R':
+ base = 0;
+ eval_text++;
+ while (isdigit (to_uchar (*eval_text)) && base <= 36)
+ base = 10 * base + *eval_text++ - '0';
+ if (base == 0 || base > 36 || *eval_text != ':')
+ return ERROR;
+ eval_text++;
+ break;
+
+ default:
+ base = 8;
+ }
+ }
+ else
+ base = 10;
+
+ /* FIXME - this calculation can overflow. Consider xstrtol. */
+ *val = 0;
+ for (; *eval_text; eval_text++)
+ {
+ if (isdigit (to_uchar (*eval_text)))
+ digit = *eval_text - '0';
+ else if (islower (to_uchar (*eval_text)))
+ digit = *eval_text - 'a' + 10;
+ else if (isupper (to_uchar (*eval_text)))
+ digit = *eval_text - 'A' + 10;
+ else
+ break;
+
+ if (base == 1)
+ {
+ if (digit == 1)
+ (*val)++;
+ else if (digit == 0 && !*val)
+ continue;
+ else
+ break;
+ }
+ else if (digit >= base)
+ break;
+ else
+ *val = *val * base + digit;
+ }
+ return NUMBER;
+ }
+
+ switch (*eval_text++)
+ {
+ case '+':
+ if (*eval_text == '+' || *eval_text == '=')
+ return BADOP;
+ return PLUS;
+ case '-':
+ if (*eval_text == '-' || *eval_text == '=')
+ return BADOP;
+ return MINUS;
+ case '*':
+ if (*eval_text == '*')
+ {
+ eval_text++;
+ return EXPONENT;
+ }
+ else if (*eval_text == '=')
+ return BADOP;
+ return TIMES;
+ case '/':
+ if (*eval_text == '=')
+ return BADOP;
+ return DIVIDE;
+ case '%':
+ if (*eval_text == '=')
+ return BADOP;
+ return MODULO;
+ case '=':
+ if (*eval_text == '=')
+ {
+ eval_text++;
+ return EQ;
+ }
+ return ASSIGN;
+ case '!':
+ if (*eval_text == '=')
+ {
+ eval_text++;
+ return NOTEQ;
+ }
+ return LNOT;
+ case '>':
+ if (*eval_text == '=')
+ {
+ eval_text++;
+ return GTEQ;
+ }
+ else if (*eval_text == '>')
+ {
+ if (*++eval_text == '=')
+ return BADOP;
+ return RSHIFT;
+ }
+ return GT;
+ case '<':
+ if (*eval_text == '=')
+ {
+ eval_text++;
+ return LSEQ;
+ }
+ else if (*eval_text == '<')
+ {
+ if (*++eval_text == '=')
+ return BADOP;
+ return LSHIFT;
+ }
+ return LS;
+ case '^':
+ if (*eval_text == '=')
+ return BADOP;
+ return XOR;
+ case '~':
+ return NOT;
+ case '&':
+ if (*eval_text == '&')
+ {
+ eval_text++;
+ return LAND;
+ }
+ else if (*eval_text == '=')
+ return BADOP;
+ return AND;
+ case '|':
+ if (*eval_text == '|')
+ {
+ eval_text++;
+ return LOR;
+ }
+ else if (*eval_text == '=')
+ return BADOP;
+ return OR;
+ case '(':
+ return LEFTP;
+ case ')':
+ return RIGHTP;
+ default:
+ return ERROR;
+ }
+}
+
+/*---------------------------------------.
+| Main entry point, called from "eval". |
+`---------------------------------------*/
+
+bool
+evaluate (const char *expr, int32_t *val)
+{
+ eval_token et;
+ eval_error err;
+
+ eval_init_lex (expr);
+ et = eval_lex (val);
+ err = logical_or_term (et, val);
+
+ if (err == NO_ERROR && *eval_text != '\0')
+ {
+ if (eval_lex (val) == BADOP)
+ err = INVALID_OPERATOR;
+ else
+ err = EXCESS_INPUT;
+ }
+
+ switch (err)
+ {
+ case NO_ERROR:
+ break;
+
+ case MISSING_RIGHT:
+ M4ERROR ((warning_status, 0,
+ "bad expression in eval (missing right parenthesis): %s",
+ expr));
+ break;
+
+ case SYNTAX_ERROR:
+ M4ERROR ((warning_status, 0,
+ "bad expression in eval: %s", expr));
+ break;
+
+ case UNKNOWN_INPUT:
+ M4ERROR ((warning_status, 0,
+ "bad expression in eval (bad input): %s", expr));
+ break;
+
+ case EXCESS_INPUT:
+ M4ERROR ((warning_status, 0,
+ "bad expression in eval (excess input): %s", expr));
+ break;
+
+ case INVALID_OPERATOR:
+ M4ERROR ((warning_status, 0,
+ "invalid operator in eval: %s", expr));
+ retcode = EXIT_FAILURE;
+ break;
+
+ case DIVIDE_ZERO:
+ M4ERROR ((warning_status, 0,
+ "divide by zero in eval: %s", expr));
+ break;
+
+ case MODULO_ZERO:
+ M4ERROR ((warning_status, 0,
+ "modulo by zero in eval: %s", expr));
+ break;
+
+ case NEGATIVE_EXPONENT:
+ M4ERROR ((warning_status, 0,
+ "negative exponent in eval: %s", expr));
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad error code in evaluate ()"));
+ abort ();
+ }
+
+ return err != NO_ERROR;
+}
+
+/*---------------------------.
+| Recursive descent parser. |
+`---------------------------*/
+
+static eval_error
+logical_or_term (eval_token et, int32_t *v1)
+{
+ int32_t v2;
+ eval_error er;
+
+ if ((er = logical_and_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((et = eval_lex (&v2)) == LOR)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ /* Implement short-circuiting of valid syntax. */
+ er = logical_and_term (et, &v2);
+ if (er == NO_ERROR)
+ *v1 = *v1 || v2;
+ else if (*v1 != 0 && er < SYNTAX_ERROR)
+ *v1 = 1;
+ else
+ return er;
+ }
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+logical_and_term (eval_token et, int32_t *v1)
+{
+ int32_t v2;
+ eval_error er;
+
+ if ((er = or_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((et = eval_lex (&v2)) == LAND)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ /* Implement short-circuiting of valid syntax. */
+ er = or_term (et, &v2);
+ if (er == NO_ERROR)
+ *v1 = *v1 && v2;
+ else if (*v1 == 0 && er < SYNTAX_ERROR)
+ ; /* v1 is already 0 */
+ else
+ return er;
+ }
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+or_term (eval_token et, int32_t *v1)
+{
+ int32_t v2;
+ eval_error er;
+
+ if ((er = xor_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((et = eval_lex (&v2)) == OR)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = xor_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ *v1 |= v2;
+ }
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+xor_term (eval_token et, int32_t *v1)
+{
+ int32_t v2;
+ eval_error er;
+
+ if ((er = and_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((et = eval_lex (&v2)) == XOR)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = and_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ *v1 ^= v2;
+ }
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+and_term (eval_token et, int32_t *v1)
+{
+ int32_t v2;
+ eval_error er;
+
+ if ((er = equality_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((et = eval_lex (&v2)) == AND)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = equality_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ *v1 &= v2;
+ }
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+equality_term (eval_token et, int32_t *v1)
+{
+ eval_token op;
+ int32_t v2;
+ eval_error er;
+
+ if ((er = cmp_term (et, v1)) != NO_ERROR)
+ return er;
+
+ /* In the 1.4.x series, we maintain the traditional behavior that
+ '=' is a synonym for '=='; however, this is contrary to POSIX and
+ we hope to convert '=' to mean assignment in 2.0. */
+ while ((op = eval_lex (&v2)) == EQ || op == NOTEQ || op == ASSIGN)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = cmp_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ if (op == ASSIGN)
+ {
+ M4ERROR ((warning_status, 0, "\
+Warning: recommend ==, not =, for equality operator"));
+ op = EQ;
+ }
+ *v1 = (op == EQ) == (*v1 == v2);
+ }
+ if (op == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+cmp_term (eval_token et, int32_t *v1)
+{
+ eval_token op;
+ int32_t v2;
+ eval_error er;
+
+ if ((er = shift_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((op = eval_lex (&v2)) == GT || op == GTEQ
+ || op == LS || op == LSEQ)
+ {
+
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = shift_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ switch (op)
+ {
+ case GT:
+ *v1 = *v1 > v2;
+ break;
+
+ case GTEQ:
+ *v1 = *v1 >= v2;
+ break;
+
+ case LS:
+ *v1 = *v1 < v2;
+ break;
+
+ case LSEQ:
+ *v1 = *v1 <= v2;
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad comparison operator in cmp_term ()"));
+ abort ();
+ }
+ }
+ if (op == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+shift_term (eval_token et, int32_t *v1)
+{
+ eval_token op;
+ int32_t v2;
+ uint32_t u1;
+ eval_error er;
+
+ if ((er = add_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((op = eval_lex (&v2)) == LSHIFT || op == RSHIFT)
+ {
+
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = add_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ /* Minimize undefined C behavior (shifting by a negative number,
+ shifting by the width or greater, left shift overflow, or
+ right shift of a negative number). Implement Java 32-bit
+ wrap-around semantics. This code assumes that the
+ implementation-defined overflow when casting unsigned to
+ signed is a silent twos-complement wrap-around. */
+ switch (op)
+ {
+ case LSHIFT:
+ u1 = *v1;
+ u1 <<= (uint32_t) (v2 & 0x1f);
+ *v1 = u1;
+ break;
+
+ case RSHIFT:
+ u1 = *v1 < 0 ? ~*v1 : *v1;
+ u1 >>= (uint32_t) (v2 & 0x1f);
+ *v1 = *v1 < 0 ? ~u1 : u1;
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad shift operator in shift_term ()"));
+ abort ();
+ }
+ }
+ if (op == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+add_term (eval_token et, int32_t *v1)
+{
+ eval_token op;
+ int32_t v2;
+ eval_error er;
+
+ if ((er = mult_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((op = eval_lex (&v2)) == PLUS || op == MINUS)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = mult_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ /* Minimize undefined C behavior on overflow. This code assumes
+ that the implementation-defined overflow when casting
+ unsigned to signed is a silent twos-complement
+ wrap-around. */
+ if (op == PLUS)
+ *v1 = (int32_t) ((uint32_t) *v1 + (uint32_t) v2);
+ else
+ *v1 = (int32_t) ((uint32_t) *v1 - (uint32_t) v2);
+ }
+ if (op == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+mult_term (eval_token et, int32_t *v1)
+{
+ eval_token op;
+ int32_t v2;
+ eval_error er;
+
+ if ((er = exp_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((op = eval_lex (&v2)) == TIMES || op == DIVIDE || op == MODULO)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = exp_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ /* Minimize undefined C behavior on overflow. This code assumes
+ that the implementation-defined overflow when casting
+ unsigned to signed is a silent twos-complement
+ wrap-around. */
+ switch (op)
+ {
+ case TIMES:
+ *v1 = (int32_t) ((uint32_t) *v1 * (uint32_t) v2);
+ break;
+
+ case DIVIDE:
+ if (v2 == 0)
+ return DIVIDE_ZERO;
+ else if (v2 == -1)
+ /* Avoid overflow, and the x86 SIGFPE on INT_MIN / -1. */
+ *v1 = (int32_t) -(uint32_t) *v1;
+ else
+ *v1 /= v2;
+ break;
+
+ case MODULO:
+ if (v2 == 0)
+ return MODULO_ZERO;
+ else if (v2 == -1)
+ /* Avoid the x86 SIGFPE on INT_MIN % -1. */
+ *v1 = 0;
+ else
+ *v1 %= v2;
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad operator in mult_term ()"));
+ abort ();
+ }
+ }
+ if (op == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+exp_term (eval_token et, int32_t *v1)
+{
+ uint32_t result;
+ int32_t v2;
+ eval_error er;
+
+ if ((er = unary_term (et, v1)) != NO_ERROR)
+ return er;
+
+ while ((et = eval_lex (&v2)) == EXPONENT)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = exp_term (et, &v2)) != NO_ERROR)
+ return er;
+
+ /* Minimize undefined C behavior on overflow. This code assumes
+ that the implementation-defined overflow when casting
+ unsigned to signed is a silent twos-complement
+ wrap-around. */
+ result = 1;
+ if (v2 < 0)
+ return NEGATIVE_EXPONENT;
+ if (*v1 == 0 && v2 == 0)
+ return DIVIDE_ZERO;
+ while (v2-- > 0)
+ result *= (uint32_t) *v1;
+ *v1 = result;
+ }
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+unary_term (eval_token et, int32_t *v1)
+{
+ eval_error er;
+
+ if (et == PLUS || et == MINUS || et == NOT || et == LNOT)
+ {
+ eval_token et2 = eval_lex (v1);
+ if (et2 == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = unary_term (et2, v1)) != NO_ERROR)
+ return er;
+
+ /* Minimize undefined C behavior on overflow. This code assumes
+ that the implementation-defined overflow when casting
+ unsigned to signed is a silent twos-complement
+ wrap-around. */
+ if (et == MINUS)
+ *v1 = (int32_t) -(uint32_t) *v1;
+ else if (et == NOT)
+ *v1 = ~*v1;
+ else if (et == LNOT)
+ *v1 = *v1 == 0 ? 1 : 0;
+ }
+ else if ((er = simple_term (et, v1)) != NO_ERROR)
+ return er;
+
+ return NO_ERROR;
+}
+
+static eval_error
+simple_term (eval_token et, int32_t *v1)
+{
+ int32_t v2;
+ eval_error er;
+
+ switch (et)
+ {
+ case LEFTP:
+ et = eval_lex (v1);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = logical_or_term (et, v1)) != NO_ERROR)
+ return er;
+
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if (et != RIGHTP)
+ return MISSING_RIGHT;
+
+ break;
+
+ case NUMBER:
+ break;
+
+ case BADOP:
+ return INVALID_OPERATOR;
+
+ default:
+ return SYNTAX_ERROR;
+ }
+ return NO_ERROR;
+}
diff --git a/src/format.c b/src/format.c
new file mode 100644
index 0000000..43a23cd
--- /dev/null
+++ b/src/format.c
@@ -0,0 +1,385 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2006-2011 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/>.
+*/
+
+/* printf like formatting for m4. */
+
+#include "m4.h"
+#include "xvasprintf.h"
+
+/* Simple varargs substitute. We assume int and unsigned int are the
+ same size; likewise for long and unsigned long. */
+
+/* Parse STR as an integer, reporting warnings. */
+static int
+arg_int (const char *str)
+{
+ char *endp;
+ long value;
+ size_t len = strlen (str);
+
+ if (!len)
+ {
+ M4ERROR ((warning_status, 0, _("empty string treated as 0")));
+ return 0;
+ }
+ errno = 0;
+ value = strtol (str, &endp, 10);
+ if (endp - str - len)
+ M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
+ else if (isspace (to_uchar (*str)))
+ M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
+ else if (errno == ERANGE || (int) value != value)
+ M4ERROR ((warning_status, 0, _("numeric overflow detected")));
+ return value;
+}
+
+/* Parse STR as a long, reporting warnings. */
+static long
+arg_long (const char *str)
+{
+ char *endp;
+ long value;
+ size_t len = strlen (str);
+
+ if (!len)
+ {
+ M4ERROR ((warning_status, 0, _("empty string treated as 0")));
+ return 0L;
+ }
+ errno = 0;
+ value = strtol (str, &endp, 10);
+ if (endp - str - len)
+ M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
+ else if (isspace (to_uchar (*str)))
+ M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
+ else if (errno == ERANGE)
+ M4ERROR ((warning_status, 0, _("numeric overflow detected")));
+ return value;
+}
+
+/* Parse STR as a double, reporting warnings. */
+static double
+arg_double (const char *str)
+{
+ char *endp;
+ double value;
+ size_t len = strlen (str);
+
+ if (!len)
+ {
+ M4ERROR ((warning_status, 0, _("empty string treated as 0")));
+ return 0.0;
+ }
+ errno = 0;
+ value = strtod (str, &endp);
+ if (endp - str - len)
+ M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
+ else if (isspace (to_uchar (*str)))
+ M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
+ else if (errno == ERANGE)
+ M4ERROR ((warning_status, 0, _("numeric overflow detected")));
+ return value;
+}
+
+#define ARG_INT(argc, argv) \
+ ((argc == 0) ? 0 : \
+ (--argc, argv++, arg_int (TOKEN_DATA_TEXT (argv[-1]))))
+
+#define ARG_LONG(argc, argv) \
+ ((argc == 0) ? 0 : \
+ (--argc, argv++, arg_long (TOKEN_DATA_TEXT (argv[-1]))))
+
+#define ARG_STR(argc, argv) \
+ ((argc == 0) ? "" : \
+ (--argc, argv++, TOKEN_DATA_TEXT (argv[-1])))
+
+#define ARG_DOUBLE(argc, argv) \
+ ((argc == 0) ? 0 : \
+ (--argc, argv++, arg_double (TOKEN_DATA_TEXT (argv[-1]))))
+
+
+/*------------------------------------------------------------------.
+| The main formatting function. Output is placed on the obstack |
+| OBS, the first argument in ARGV is the formatting string, and the |
+| rest is arguments for the string. Warn rather than invoke |
+| unspecified behavior in the underlying printf when we do not |
+| recognize a format. |
+`------------------------------------------------------------------*/
+
+void
+expand_format (struct obstack *obs, int argc, token_data **argv)
+{
+ const char *f; /* format control string */
+ const char *fmt; /* position within f */
+ char fstart[] = "%'+- 0#*.*hhd"; /* current format spec */
+ char *p; /* position within fstart */
+ unsigned char c; /* a simple character */
+
+ /* Flags. */
+ char flags; /* flags to use in fstart */
+ enum {
+ THOUSANDS = 0x01, /* ' */
+ PLUS = 0x02, /* + */
+ MINUS = 0x04, /* - */
+ SPACE = 0x08, /* */
+ ZERO = 0x10, /* 0 */
+ ALT = 0x20, /* # */
+ DONE = 0x40 /* no more flags */
+ };
+
+ /* Precision specifiers. */
+ int width; /* minimum field width */
+ int prec; /* precision */
+ char lflag; /* long flag */
+
+ /* Specifiers we are willing to accept. ok['x'] implies %x is ok.
+ Various modifiers reduce the set, in order to avoid undefined
+ behavior in printf. */
+ char ok[128];
+
+ /* Buffer and stuff. */
+ char *str; /* malloc'd buffer of formatted text */
+ enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
+
+ f = fmt = ARG_STR (argc, argv);
+ memset (ok, 0, sizeof ok);
+ while (1)
+ {
+ const char *percent = strchr (fmt, '%');
+ if (!percent)
+ {
+ obstack_grow (obs, fmt, strlen (fmt));
+ return;
+ }
+ obstack_grow (obs, fmt, percent - fmt);
+ fmt = percent + 1;
+
+ if (*fmt == '%')
+ {
+ obstack_1grow (obs, '%');
+ fmt++;
+ continue;
+ }
+
+ p = fstart + 1; /* % */
+ lflag = 0;
+ ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E']
+ = ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o']
+ = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
+
+ /* Parse flags. */
+ flags = 0;
+ do
+ {
+ switch (*fmt)
+ {
+ case '\'': /* thousands separator */
+ ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E']
+ = ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
+ flags |= THOUSANDS;
+ break;
+
+ case '+': /* mandatory sign */
+ ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
+ flags |= PLUS;
+ break;
+
+ case ' ': /* space instead of positive sign */
+ ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
+ flags |= SPACE;
+ break;
+
+ case '0': /* zero padding */
+ ok['c'] = ok['s'] = 0;
+ flags |= ZERO;
+ break;
+
+ case '#': /* alternate output */
+ ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
+ flags |= ALT;
+ break;
+
+ case '-': /* left justification */
+ flags |= MINUS;
+ break;
+
+ default:
+ flags |= DONE;
+ break;
+ }
+ }
+ while (!(flags & DONE) && fmt++);
+ if (flags & THOUSANDS)
+ *p++ = '\'';
+ if (flags & PLUS)
+ *p++ = '+';
+ if (flags & MINUS)
+ *p++ = '-';
+ if (flags & SPACE)
+ *p++ = ' ';
+ if (flags & ZERO)
+ *p++ = '0';
+ if (flags & ALT)
+ *p++ = '#';
+
+ /* Minimum field width; an explicit 0 is the same as not giving
+ the width. */
+ width = 0;
+ *p++ = '*';
+ if (*fmt == '*')
+ {
+ width = ARG_INT (argc, argv);
+ fmt++;
+ }
+ else
+ while (isdigit (to_uchar (*fmt)))
+ {
+ width = 10 * width + *fmt - '0';
+ fmt++;
+ }
+
+ /* Maximum precision; an explicit negative precision is the same
+ as not giving the precision. A lone '.' is a precision of 0. */
+ prec = -1;
+ *p++ = '.';
+ *p++ = '*';
+ if (*fmt == '.')
+ {
+ ok['c'] = 0;
+ if (*(++fmt) == '*')
+ {
+ prec = ARG_INT (argc, argv);
+ ++fmt;
+ }
+ else
+ {
+ prec = 0;
+ while (isdigit (to_uchar (*fmt)))
+ {
+ prec = 10 * prec + *fmt - '0';
+ fmt++;
+ }
+ }
+ }
+
+ /* Length modifiers. We don't yet recognize ll, j, t, or z. */
+ if (*fmt == 'l')
+ {
+ *p++ = 'l';
+ lflag = 1;
+ fmt++;
+ ok['c'] = ok['s'] = 0;
+ }
+ else if (*fmt == 'h')
+ {
+ *p++ = 'h';
+ fmt++;
+ if (*fmt == 'h')
+ {
+ *p++ = 'h';
+ fmt++;
+ }
+ ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = ok['f'] = ok['F']
+ = ok['g'] = ok['G'] = ok['s'] = 0;
+ }
+
+ c = *fmt++;
+ if (sizeof ok <= c || !ok[c])
+ {
+ M4ERROR ((warning_status, 0,
+ "Warning: unrecognized specifier in `%s'", f));
+ if (c == '\0')
+ fmt--;
+ continue;
+ }
+
+ /* Specifiers. We don't yet recognize C, S, n, or p. */
+ switch (c)
+ {
+ case 'c':
+ datatype = CHAR;
+ p -= 2; /* %.*c is undefined, so undo the '.*'. */
+ break;
+
+ case 's':
+ datatype = STR;
+ break;
+
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'u':
+ datatype = lflag ? LONG : INT;
+ break;
+
+ case 'a':
+ case 'A':
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ datatype = DOUBLE;
+ break;
+
+ default:
+ abort ();
+ }
+ *p++ = c;
+ *p = '\0';
+
+ switch (datatype)
+ {
+ case CHAR:
+ str = xasprintf (fstart, width, ARG_INT(argc, argv));
+ break;
+
+ case INT:
+ str = xasprintf (fstart, width, prec, ARG_INT(argc, argv));
+ break;
+
+ case LONG:
+ str = xasprintf (fstart, width, prec, ARG_LONG(argc, argv));
+ break;
+
+ case DOUBLE:
+ str = xasprintf (fstart, width, prec, ARG_DOUBLE(argc, argv));
+ break;
+
+ case STR:
+ str = xasprintf (fstart, width, prec, ARG_STR(argc, argv));
+ break;
+
+ default:
+ abort();
+ }
+
+ /* NULL was returned on failure, such as invalid format string. For
+ now, just silently ignore that bad specifier. */
+ if (str == NULL)
+ continue;
+
+ obstack_grow (obs, str, strlen (str));
+ free (str);
+ }
+}
diff --git a/src/freeze.c b/src/freeze.c
new file mode 100644
index 0000000..bf47376
--- /dev/null
+++ b/src/freeze.c
@@ -0,0 +1,398 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2006-2011 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/>.
+*/
+
+/* This module handles frozen files. */
+
+#include "m4.h"
+
+/*-------------------------------------------------------------------.
+| Destructively reverse a symbol list and return the reversed list. |
+`-------------------------------------------------------------------*/
+
+static symbol *
+reverse_symbol_list (symbol *sym)
+{
+ symbol *result;
+ symbol *next;
+
+ result = NULL;
+ while (sym)
+ {
+ next = SYMBOL_NEXT (sym);
+ SYMBOL_NEXT (sym) = result;
+ result = sym;
+ sym = next;
+ }
+ return result;
+}
+
+/*------------------------------------------------.
+| Produce a frozen state to the given file NAME. |
+`------------------------------------------------*/
+
+void
+produce_frozen_state (const char *name)
+{
+ FILE *file;
+ size_t h;
+ symbol *sym;
+ const builtin *bp;
+
+ file = fopen (name, O_BINARY ? "wb" : "w");
+ if (!file)
+ {
+ M4ERROR ((EXIT_FAILURE, errno, "cannot open `%s'", name));
+ return;
+ }
+
+ /* Write a recognizable header. */
+
+ xfprintf (file, "# This is a frozen state file generated by %s\n",
+ PACKAGE_STRING);
+ xfprintf (file, "V1\n");
+
+ /* Dump quote delimiters. */
+
+ if (strcmp (lquote.string, DEF_LQUOTE) || strcmp (rquote.string, DEF_RQUOTE))
+ {
+ xfprintf (file, "Q%d,%d\n", (int) lquote.length, (int) rquote.length);
+ fputs (lquote.string, file);
+ fputs (rquote.string, file);
+ fputc ('\n', file);
+ }
+
+ /* Dump comment delimiters. */
+
+ if (strcmp (bcomm.string, DEF_BCOMM) || strcmp (ecomm.string, DEF_ECOMM))
+ {
+ xfprintf (file, "C%d,%d\n", (int) bcomm.length, (int) ecomm.length);
+ fputs (bcomm.string, file);
+ fputs (ecomm.string, file);
+ fputc ('\n', file);
+ }
+
+ /* Dump all symbols. */
+
+ for (h = 0; h < hash_table_size; h++)
+ {
+
+ /* Process all entries in one bucket, from the last to the first.
+ This order ensures that, at reload time, pushdef's will be
+ executed with the oldest definitions first. */
+
+ symtab[h] = reverse_symbol_list (symtab[h]);
+ for (sym = symtab[h]; sym; sym = SYMBOL_NEXT (sym))
+ {
+ switch (SYMBOL_TYPE (sym))
+ {
+ case TOKEN_TEXT:
+ xfprintf (file, "T%d,%d\n",
+ (int) strlen (SYMBOL_NAME (sym)),
+ (int) strlen (SYMBOL_TEXT (sym)));
+ fputs (SYMBOL_NAME (sym), file);
+ fputs (SYMBOL_TEXT (sym), file);
+ fputc ('\n', file);
+ break;
+
+ case TOKEN_FUNC:
+ bp = find_builtin_by_addr (SYMBOL_FUNC (sym));
+ if (bp == NULL)
+ {
+ M4ERROR ((warning_status, 0, "\
+INTERNAL ERROR: builtin not found in builtin table!"));
+ abort ();
+ }
+ xfprintf (file, "F%d,%d\n",
+ (int) strlen (SYMBOL_NAME (sym)),
+ (int) strlen (bp->name));
+ fputs (SYMBOL_NAME (sym), file);
+ fputs (bp->name, file);
+ fputc ('\n', file);
+ break;
+
+ case TOKEN_VOID:
+ /* Ignore placeholder tokens that exist due to traceon. */
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0, "\
+INTERNAL ERROR: bad token data type in freeze_one_symbol ()"));
+ abort ();
+ break;
+ }
+ }
+
+ /* Reverse the bucket once more, putting it back as it was. */
+
+ symtab[h] = reverse_symbol_list (symtab[h]);
+ }
+
+ /* Let diversions be issued from output.c module, its cleaner to have this
+ piece of code there. */
+
+ freeze_diversions (file);
+
+ /* All done. */
+
+ fputs ("# End of frozen state file\n", file);
+ if (close_stream (file) != 0)
+ M4ERROR ((EXIT_FAILURE, errno, "unable to create frozen state"));
+}
+
+/*----------------------------------------------------------------------.
+| Issue a message saying that some character is an EXPECTED character. |
+`----------------------------------------------------------------------*/
+
+static void
+issue_expect_message (int expected)
+{
+ if (expected == '\n')
+ M4ERROR ((EXIT_FAILURE, 0, "expecting line feed in frozen file"));
+ else
+ M4ERROR ((EXIT_FAILURE, 0, "expecting character `%c' in frozen file",
+ expected));
+}
+
+/*-------------------------------------------------.
+| Reload a frozen state from the given file NAME. |
+`-------------------------------------------------*/
+
+/* We are seeking speed, here. */
+
+void
+reload_frozen_state (const char *name)
+{
+ FILE *file;
+ int character;
+ int operation;
+ char *string[2];
+ int allocated[2];
+ int number[2];
+ const builtin *bp;
+ bool advance_line = true;
+
+#define GET_CHARACTER \
+ do \
+ { \
+ if (advance_line) \
+ { \
+ current_line++; \
+ advance_line = false; \
+ } \
+ (character = getc (file)); \
+ if (character == '\n') \
+ advance_line = true; \
+ } \
+ while (0)
+
+#define GET_NUMBER(Number, AllowNeg) \
+ do \
+ { \
+ unsigned int n = 0; \
+ while (isdigit (character) && n <= INT_MAX / 10U) \
+ { \
+ n = 10 * n + character - '0'; \
+ GET_CHARACTER; \
+ } \
+ if (((AllowNeg) ? INT_MIN : INT_MAX) + 0U < n \
+ || isdigit (character)) \
+ m4_error (EXIT_FAILURE, 0, \
+ _("integer overflow in frozen file")); \
+ (Number) = n; \
+ } \
+ while (0)
+
+#define VALIDATE(Expected) \
+ do \
+ { \
+ if (character != (Expected)) \
+ issue_expect_message (Expected); \
+ } \
+ while (0)
+
+ /* Skip comments (`#' at beginning of line) and blank lines, setting
+ character to the next directive or to EOF. */
+
+#define GET_DIRECTIVE \
+ do \
+ { \
+ GET_CHARACTER; \
+ if (character == '#') \
+ { \
+ while (character != EOF && character != '\n') \
+ GET_CHARACTER; \
+ VALIDATE ('\n'); \
+ } \
+ } \
+ while (character == '\n')
+
+#define GET_STRING(i) \
+ do \
+ { \
+ void *tmp; \
+ char *p; \
+ if (number[(i)] + 1 > allocated[(i)]) \
+ { \
+ free (string[(i)]); \
+ allocated[(i)] = number[(i)] + 1; \
+ string[(i)] = xcharalloc ((size_t) allocated[(i)]); \
+ } \
+ if (number[(i)] > 0 \
+ && !fread (string[(i)], (size_t) number[(i)], 1, file)) \
+ m4_error (EXIT_FAILURE, 0, \
+ _("premature end of frozen file")); \
+ string[(i)][number[(i)]] = '\0'; \
+ p = string[(i)]; \
+ while ((tmp = memchr(p, '\n', number[(i)] - (p - string[(i)])))) \
+ { \
+ current_line++; \
+ p = (char *) tmp + 1; \
+ } \
+ } \
+ while (0)
+
+ file = m4_path_search (name, NULL);
+ if (file == NULL)
+ M4ERROR ((EXIT_FAILURE, errno, "cannot open %s", name));
+ current_file = name;
+
+ allocated[0] = 100;
+ string[0] = xcharalloc ((size_t) allocated[0]);
+ allocated[1] = 100;
+ string[1] = xcharalloc ((size_t) allocated[1]);
+
+ /* Validate format version. Only `1' is acceptable for now. */
+ GET_DIRECTIVE;
+ VALIDATE ('V');
+ GET_CHARACTER;
+ GET_NUMBER (number[0], false);
+ if (number[0] > 1)
+ M4ERROR ((EXIT_MISMATCH, 0,
+ "frozen file version %d greater than max supported of 1",
+ number[0]));
+ else if (number[0] < 1)
+ M4ERROR ((EXIT_FAILURE, 0,
+ "ill-formed frozen file, version directive expected"));
+ VALIDATE ('\n');
+
+ GET_DIRECTIVE;
+ while (character != EOF)
+ {
+ switch (character)
+ {
+ default:
+ M4ERROR ((EXIT_FAILURE, 0, "ill-formed frozen file"));
+
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'T':
+ case 'Q':
+ operation = character;
+ GET_CHARACTER;
+
+ /* Get string lengths. Accept a negative diversion number. */
+
+ if (operation == 'D' && character == '-')
+ {
+ GET_CHARACTER;
+ GET_NUMBER (number[0], true);
+ number[0] = -number[0];
+ }
+ else
+ GET_NUMBER (number[0], false);
+ VALIDATE (',');
+ GET_CHARACTER;
+ GET_NUMBER (number[1], false);
+ VALIDATE ('\n');
+
+ if (operation != 'D')
+ GET_STRING (0);
+ GET_STRING (1);
+ GET_CHARACTER;
+ VALIDATE ('\n');
+
+ /* Act according to operation letter. */
+
+ switch (operation)
+ {
+ case 'C':
+
+ /* Change comment strings. */
+
+ set_comment (string[0], string[1]);
+ break;
+
+ case 'D':
+
+ /* Select a diversion and add a string to it. */
+
+ make_diversion (number[0]);
+ if (number[1] > 0)
+ output_text (string[1], number[1]);
+ break;
+
+ case 'F':
+
+ /* Enter a macro having a builtin function as a definition. */
+
+ bp = find_builtin_by_name (string[1]);
+ define_builtin (string[0], bp, SYMBOL_PUSHDEF);
+ break;
+
+ case 'T':
+
+ /* Enter a macro having an expansion text as a definition. */
+
+ define_user_macro (string[0], string[1], SYMBOL_PUSHDEF);
+ break;
+
+ case 'Q':
+
+ /* Change quote strings. */
+
+ set_quotes (string[0], string[1]);
+ break;
+
+ default:
+
+ /* Cannot happen. */
+
+ break;
+ }
+ break;
+
+ }
+ GET_DIRECTIVE;
+ }
+
+ free (string[0]);
+ free (string[1]);
+ if (close_stream (file) != 0)
+ m4_error (EXIT_FAILURE, errno, _("unable to read frozen state"));
+ current_file = NULL;
+ current_line = 0;
+
+#undef GET_CHARACTER
+#undef GET_DIRECTIVE
+#undef GET_NUMBER
+#undef VALIDATE
+#undef GET_STRING
+}
diff --git a/src/input.c b/src/input.c
new file mode 100644
index 0000000..be259a0
--- /dev/null
+++ b/src/input.c
@@ -0,0 +1,1156 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2004-2011 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/>.
+*/
+
+/* Handling of different input sources, and lexical analysis. */
+
+#include "m4.h"
+
+#include "memchr2.h"
+
+/* Unread input can be either files, that should be read (eg. included
+ files), strings, which should be rescanned (eg. macro expansion text),
+ or quoted macro definitions (as returned by the builtin "defn").
+ Unread input are organised in a stack, implemented with an obstack.
+ Each input source is described by a "struct input_block". The obstack
+ is "current_input". The top of the input stack is "isp".
+
+ The macro "m4wrap" places the text to be saved on another input
+ stack, on the obstack "wrapup_stack", whose top is "wsp". When EOF
+ is seen on normal input (eg, when "current_input" is empty), input is
+ switched over to "wrapup_stack", and the original "current_input" is
+ freed. A new stack is allocated for "wrapup_stack", which will
+ accept any text produced by calls to "m4wrap" from within the
+ wrapped text. This process of shuffling "wrapup_stack" to
+ "current_input" can continue indefinitely, even generating infinite
+ loops (e.g. "define(`f',`m4wrap(`f')')f"), without memory leaks.
+
+ Pushing new input on the input stack is done by push_file (),
+ push_string (), push_wrapup () (for wrapup text), and push_macro ()
+ (for macro definitions). Because macro expansion needs direct access
+ to the current input obstack (for optimisation), push_string () are
+ split in two functions, push_string_init (), which returns a pointer
+ to the current input stack, and push_string_finish (), which return a
+ pointer to the final text. The input_block *next is used to manage
+ the coordination between the different push routines.
+
+ The current file and line number are stored in two global
+ variables, for use by the error handling functions in m4.c. Macro
+ expansion wants to report the line where a macro name was detected,
+ rather than where it finished collecting arguments. This also
+ applies to text resulting from macro expansions. So each input
+ block maintains its own notion of the current file and line, and
+ swapping between input blocks updates the global variables
+ accordingly. */
+
+#ifdef ENABLE_CHANGEWORD
+#include "regex.h"
+#endif
+
+enum input_type
+{
+ INPUT_STRING, /* String resulting from macro expansion. */
+ INPUT_FILE, /* File from command line or include. */
+ INPUT_MACRO /* Builtin resulting from defn. */
+};
+
+typedef enum input_type input_type;
+
+struct input_block
+{
+ struct input_block *prev; /* previous input_block on the input stack */
+ input_type type; /* see enum values */
+ const char *file; /* file where this input is from */
+ int line; /* line where this input is from */
+ union
+ {
+ struct
+ {
+ char *string; /* remaining string value */
+ char *end; /* terminating NUL of string */
+ }
+ u_s; /* INPUT_STRING */
+ struct
+ {
+ FILE *fp; /* input file handle */
+ bool_bitfield end : 1; /* true if peek has seen EOF */
+ bool_bitfield close : 1; /* true if we should close file on pop */
+ bool_bitfield advance : 1; /* track previous start_of_input_line */
+ }
+ u_f; /* INPUT_FILE */
+ builtin_func *func; /* pointer to macro's function */
+ }
+ u;
+};
+
+typedef struct input_block input_block;
+
+
+/* Current input file name. */
+const char *current_file;
+
+/* Current input line number. */
+int current_line;
+
+/* Obstack for storing individual tokens. */
+static struct obstack token_stack;
+
+/* Obstack for storing file names. */
+static struct obstack file_names;
+
+/* Wrapup input stack. */
+static struct obstack *wrapup_stack;
+
+/* Current stack, from input or wrapup. */
+static struct obstack *current_input;
+
+/* Bottom of token_stack, for obstack_free. */
+static void *token_bottom;
+
+/* Pointer to top of current_input. */
+static input_block *isp;
+
+/* Pointer to top of wrapup_stack. */
+static input_block *wsp;
+
+/* Aux. for handling split push_string (). */
+static input_block *next;
+
+/* Flag for next_char () to increment current_line. */
+static bool start_of_input_line;
+
+/* Flag for next_char () to recognize change in input block. */
+static bool input_change;
+
+#define CHAR_EOF 256 /* character return on EOF */
+#define CHAR_MACRO 257 /* character return for MACRO token */
+
+/* Quote chars. */
+STRING rquote;
+STRING lquote;
+
+/* Comment chars. */
+STRING bcomm;
+STRING ecomm;
+
+#ifdef ENABLE_CHANGEWORD
+
+# define DEFAULT_WORD_REGEXP "[_a-zA-Z][_a-zA-Z0-9]*"
+
+static struct re_pattern_buffer word_regexp;
+static int default_word_regexp;
+static struct re_registers regs;
+
+#else /* ! ENABLE_CHANGEWORD */
+# define default_word_regexp 1
+#endif /* ! ENABLE_CHANGEWORD */
+
+#ifdef DEBUG_INPUT
+static const char *token_type_string (token_type);
+#endif
+
+
+/*-------------------------------------------------------------------.
+| push_file () pushes an input file on the input stack, saving the |
+| current file name and line number. If next is non-NULL, this push |
+| invalidates a call to push_string_init (), whose storage is |
+| consequently released. If CLOSE_WHEN_DONE, then close FP after |
+| EOF is detected. |
+`-------------------------------------------------------------------*/
+
+void
+push_file (FILE *fp, const char *title, bool close_when_done)
+{
+ input_block *i;
+
+ if (next != NULL)
+ {
+ obstack_free (current_input, next);
+ next = NULL;
+ }
+
+ if (debug_level & DEBUG_TRACE_INPUT)
+ DEBUG_MESSAGE1 ("input read from %s", title);
+
+ i = (input_block *) obstack_alloc (current_input,
+ sizeof (struct input_block));
+ i->type = INPUT_FILE;
+ i->file = (char *) obstack_copy0 (&file_names, title, strlen (title));
+ i->line = 1;
+ input_change = true;
+
+ i->u.u_f.fp = fp;
+ i->u.u_f.end = false;
+ i->u.u_f.close = close_when_done;
+ i->u.u_f.advance = start_of_input_line;
+ output_current_line = -1;
+
+ i->prev = isp;
+ isp = i;
+}
+
+/*---------------------------------------------------------------.
+| push_macro () pushes a builtin macro's definition on the input |
+| stack. If next is non-NULL, this push invalidates a call to |
+| push_string_init (), whose storage is consequently released. |
+`---------------------------------------------------------------*/
+
+void
+push_macro (builtin_func *func)
+{
+ input_block *i;
+
+ if (next != NULL)
+ {
+ obstack_free (current_input, next);
+ next = NULL;
+ }
+
+ i = (input_block *) obstack_alloc (current_input,
+ sizeof (struct input_block));
+ i->type = INPUT_MACRO;
+ i->file = current_file;
+ i->line = current_line;
+ input_change = true;
+
+ i->u.func = func;
+ i->prev = isp;
+ isp = i;
+}
+
+/*------------------------------------------------------------------.
+| First half of push_string (). The pointer next points to the new |
+| input_block. |
+`------------------------------------------------------------------*/
+
+struct obstack *
+push_string_init (void)
+{
+ if (next != NULL)
+ {
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: recursive push_string!"));
+ abort ();
+ }
+
+ next = (input_block *) obstack_alloc (current_input,
+ sizeof (struct input_block));
+ next->type = INPUT_STRING;
+ next->file = current_file;
+ next->line = current_line;
+
+ return current_input;
+}
+
+/*-------------------------------------------------------------------.
+| Last half of push_string (). If next is now NULL, a call to |
+| push_file () has invalidated the previous call to push_string_init |
+| (), so we just give up. If the new object is void, we do not push |
+| it. The function push_string_finish () returns a pointer to the |
+| finished object. This pointer is only for temporary use, since |
+| reading the next token might release the memory used for the |
+| object. |
+`-------------------------------------------------------------------*/
+
+const char *
+push_string_finish (void)
+{
+ const char *ret = NULL;
+
+ if (next == NULL)
+ return NULL;
+
+ if (obstack_object_size (current_input) > 0)
+ {
+ size_t len = obstack_object_size (current_input);
+ obstack_1grow (current_input, '\0');
+ next->u.u_s.string = (char *) obstack_finish (current_input);
+ next->u.u_s.end = next->u.u_s.string + len;
+ next->prev = isp;
+ isp = next;
+ ret = isp->u.u_s.string; /* for immediate use only */
+ input_change = true;
+ }
+ else
+ obstack_free (current_input, next); /* people might leave garbage on it. */
+ next = NULL;
+ return ret;
+}
+
+/*------------------------------------------------------------------.
+| The function push_wrapup () pushes a string on the wrapup stack. |
+| When the normal input stack gets empty, the wrapup stack will |
+| become the input stack, and push_string () and push_file () will |
+| operate on wrapup_stack. Push_wrapup should be done as |
+| push_string (), but this will suffice, as long as arguments to |
+| m4_m4wrap () are moderate in size. |
+`------------------------------------------------------------------*/
+
+void
+push_wrapup (const char *s)
+{
+ size_t len = strlen (s);
+ input_block *i;
+ i = (input_block *) obstack_alloc (wrapup_stack,
+ sizeof (struct input_block));
+ i->prev = wsp;
+ i->type = INPUT_STRING;
+ i->file = current_file;
+ i->line = current_line;
+ i->u.u_s.string = (char *) obstack_copy0 (wrapup_stack, s, len);
+ i->u.u_s.end = i->u.u_s.string + len;
+ wsp = i;
+}
+
+
+/*-------------------------------------------------------------------.
+| The function pop_input () pops one level of input sources. If the |
+| popped input_block is a file, current_file and current_line are |
+| reset to the saved values before the memory for the input_block is |
+| released. |
+`-------------------------------------------------------------------*/
+
+static void
+pop_input (void)
+{
+ input_block *tmp = isp->prev;
+
+ switch (isp->type)
+ {
+ case INPUT_STRING:
+ case INPUT_MACRO:
+ break;
+
+ case INPUT_FILE:
+ if (debug_level & DEBUG_TRACE_INPUT)
+ {
+ if (tmp)
+ DEBUG_MESSAGE2 ("input reverted to %s, line %d",
+ tmp->file, tmp->line);
+ else
+ DEBUG_MESSAGE ("input exhausted");
+ }
+
+ if (ferror (isp->u.u_f.fp))
+ {
+ M4ERROR ((warning_status, 0, "read error"));
+ if (isp->u.u_f.close)
+ fclose (isp->u.u_f.fp);
+ retcode = EXIT_FAILURE;
+ }
+ else if (isp->u.u_f.close && fclose (isp->u.u_f.fp) == EOF)
+ {
+ M4ERROR ((warning_status, errno, "error reading file"));
+ retcode = EXIT_FAILURE;
+ }
+ start_of_input_line = isp->u.u_f.advance;
+ output_current_line = -1;
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: input stack botch in pop_input ()"));
+ abort ();
+ }
+ obstack_free (current_input, isp);
+ next = NULL; /* might be set in push_string_init () */
+
+ isp = tmp;
+ input_change = true;
+}
+
+/*-------------------------------------------------------------------.
+| To switch input over to the wrapup stack, main calls pop_wrapup |
+| (). Since wrapup text can install new wrapup text, pop_wrapup () |
+| returns false when there is no wrapup text on the stack, and true |
+| otherwise. |
+`-------------------------------------------------------------------*/
+
+bool
+pop_wrapup (void)
+{
+ next = NULL;
+ obstack_free (current_input, NULL);
+ free (current_input);
+
+ if (wsp == NULL)
+ {
+ /* End of the program. Free all memory even though we are about
+ to exit, since it makes leak detection easier. */
+ obstack_free (&token_stack, NULL);
+ obstack_free (&file_names, NULL);
+ obstack_free (wrapup_stack, NULL);
+ free (wrapup_stack);
+#ifdef ENABLE_CHANGEWORD
+ regfree (&word_regexp);
+#endif /* ENABLE_CHANGEWORD */
+ return false;
+ }
+
+ current_input = wrapup_stack;
+ wrapup_stack = (struct obstack *) xmalloc (sizeof (struct obstack));
+ obstack_init (wrapup_stack);
+
+ isp = wsp;
+ wsp = NULL;
+ input_change = true;
+
+ return true;
+}
+
+/*-------------------------------------------------------------------.
+| When a MACRO token is seen, next_token () uses init_macro_token () |
+| to retrieve the value of the function pointer. |
+`-------------------------------------------------------------------*/
+
+static void
+init_macro_token (token_data *td)
+{
+ if (isp->type != INPUT_MACRO)
+ {
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad call to init_macro_token ()"));
+ abort ();
+ }
+
+ TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
+ TOKEN_DATA_FUNC (td) = isp->u.func;
+}
+
+
+/*-----------------------------------------------------------------.
+| Low level input is done a character at a time. The function |
+| peek_input () is used to look at the next character in the input |
+| stream. At any given time, it reads from the input_block on the |
+| top of the current input stack. |
+`-----------------------------------------------------------------*/
+
+static int
+peek_input (void)
+{
+ int ch;
+ input_block *block = isp;
+
+ while (1)
+ {
+ if (block == NULL)
+ return CHAR_EOF;
+
+ switch (block->type)
+ {
+ case INPUT_STRING:
+ ch = to_uchar (block->u.u_s.string[0]);
+ if (ch != '\0')
+ return ch;
+ break;
+
+ case INPUT_FILE:
+ ch = getc (block->u.u_f.fp);
+ if (ch != EOF)
+ {
+ ungetc (ch, block->u.u_f.fp);
+ return ch;
+ }
+ block->u.u_f.end = true;
+ break;
+
+ case INPUT_MACRO:
+ return CHAR_MACRO;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: input stack botch in peek_input ()"));
+ abort ();
+ }
+ block = block->prev;
+ }
+}
+
+/*-------------------------------------------------------------------.
+| The function next_char () is used to read and advance the input to |
+| the next character. It also manages line numbers for error |
+| messages, so they do not get wrong, due to lookahead. The token |
+| consisting of a newline alone is taken as belonging to the line it |
+| ends, and the current line number is not incremented until the |
+| next character is read. 99.9% of all calls will read from a |
+| string, so factor that out into a macro for speed. |
+`-------------------------------------------------------------------*/
+
+#define next_char() \
+ (isp && isp->type == INPUT_STRING && isp->u.u_s.string[0] \
+ && !input_change \
+ ? to_uchar (*isp->u.u_s.string++) \
+ : next_char_1 ())
+
+static int
+next_char_1 (void)
+{
+ int ch;
+
+ while (1)
+ {
+ if (isp == NULL)
+ {
+ current_file = "";
+ current_line = 0;
+ return CHAR_EOF;
+ }
+
+ if (input_change)
+ {
+ current_file = isp->file;
+ current_line = isp->line;
+ input_change = false;
+ }
+
+ switch (isp->type)
+ {
+ case INPUT_STRING:
+ ch = to_uchar (*isp->u.u_s.string++);
+ if (ch != '\0')
+ return ch;
+ break;
+
+ case INPUT_FILE:
+ if (start_of_input_line)
+ {
+ start_of_input_line = false;
+ current_line = ++isp->line;
+ }
+
+ /* If stdin is a terminal, calling getc after peek_input
+ already called it would make the user have to hit ^D
+ twice to quit. */
+ ch = isp->u.u_f.end ? EOF : getc (isp->u.u_f.fp);
+ if (ch != EOF)
+ {
+ if (ch == '\n')
+ start_of_input_line = true;
+ return ch;
+ }
+ break;
+
+ case INPUT_MACRO:
+ pop_input (); /* INPUT_MACRO input sources has only one token */
+ return CHAR_MACRO;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: input stack botch in next_char ()"));
+ abort ();
+ }
+
+ /* End of input source --- pop one level. */
+ pop_input ();
+ }
+}
+
+/*-------------------------------------------------------------------.
+| skip_line () simply discards all immediately following characters, |
+| upto the first newline. It is only used from m4_dnl (). |
+`-------------------------------------------------------------------*/
+
+void
+skip_line (void)
+{
+ int ch;
+ const char *file = current_file;
+ int line = current_line;
+
+ while ((ch = next_char ()) != CHAR_EOF && ch != '\n')
+ ;
+ if (ch == CHAR_EOF)
+ /* current_file changed to "" if we see CHAR_EOF, use the
+ previous value we stored earlier. */
+ M4ERROR_AT_LINE ((warning_status, 0, file, line,
+ "Warning: end of file treated as newline"));
+ /* On the rare occasion that dnl crosses include file boundaries
+ (either the input file did not end in a newline, or changeword
+ was used), calling next_char can update current_file and
+ current_line, and that update will be undone as we return to
+ expand_macro. This informs next_char to fix things again. */
+ if (file != current_file || line != current_line)
+ input_change = true;
+}
+
+
+/*------------------------------------------------------------------.
+| This function is for matching a string against a prefix of the |
+| input stream. If the string matches the input and consume is |
+| true, the input is discarded; otherwise any characters read are |
+| pushed back again. The function is used only when multicharacter |
+| quotes or comment delimiters are used. |
+`------------------------------------------------------------------*/
+
+static bool
+match_input (const char *s, bool consume)
+{
+ int n; /* number of characters matched */
+ int ch; /* input character */
+ const char *t;
+ bool result = false;
+
+ ch = peek_input ();
+ if (ch != to_uchar (*s))
+ return false; /* fail */
+
+ if (s[1] == '\0')
+ {
+ if (consume)
+ next_char ();
+ return true; /* short match */
+ }
+
+ next_char ();
+ for (n = 1, t = s++; peek_input () == to_uchar (*s++); )
+ {
+ next_char ();
+ n++;
+ if (*s == '\0') /* long match */
+ {
+ if (consume)
+ return true;
+ result = true;
+ break;
+ }
+ }
+
+ /* Failed or shouldn't consume, push back input. */
+ {
+ struct obstack *h = push_string_init ();
+
+ /* `obstack_grow' may be macro evaluating its arg 1 several times. */
+ obstack_grow (h, t, n);
+ }
+ push_string_finish ();
+ return result;
+}
+
+/*--------------------------------------------------------------------.
+| The macro MATCH() is used to match a string S against the input. |
+| The first character is handled inline, for speed. Hopefully, this |
+| will not hurt efficiency too much when single character quotes and |
+| comment delimiters are used. If CONSUME, then CH is the result of |
+| next_char, and a successful match will discard the matched string. |
+| Otherwise, CH is the result of peek_char, and the input stream is |
+| effectively unchanged. |
+`--------------------------------------------------------------------*/
+
+#define MATCH(ch, s, consume) \
+ (to_uchar ((s)[0]) == (ch) \
+ && (ch) != '\0' \
+ && ((s)[1] == '\0' || (match_input ((s) + (consume), consume))))
+
+
+/*--------------------------------------------------------.
+| Initialize input stacks, and quote/comment characters. |
+`--------------------------------------------------------*/
+
+void
+input_init (void)
+{
+ current_file = "";
+ current_line = 0;
+
+ current_input = (struct obstack *) xmalloc (sizeof (struct obstack));
+ obstack_init (current_input);
+ wrapup_stack = (struct obstack *) xmalloc (sizeof (struct obstack));
+ obstack_init (wrapup_stack);
+
+ obstack_init (&file_names);
+
+ /* Allocate an object in the current chunk, so that obstack_free
+ will always work even if the first token parsed spills to a new
+ chunk. */
+ obstack_init (&token_stack);
+ obstack_alloc (&token_stack, 1);
+ token_bottom = obstack_base (&token_stack);
+
+ isp = NULL;
+ wsp = NULL;
+ next = NULL;
+
+ start_of_input_line = false;
+
+ lquote.string = xstrdup (DEF_LQUOTE);
+ lquote.length = strlen (lquote.string);
+ rquote.string = xstrdup (DEF_RQUOTE);
+ rquote.length = strlen (rquote.string);
+ bcomm.string = xstrdup (DEF_BCOMM);
+ bcomm.length = strlen (bcomm.string);
+ ecomm.string = xstrdup (DEF_ECOMM);
+ ecomm.length = strlen (ecomm.string);
+
+#ifdef ENABLE_CHANGEWORD
+ set_word_regexp (user_word_regexp);
+#endif
+}
+
+
+/*------------------------------------------------------------------.
+| Functions for setting quotes and comment delimiters. Used by |
+| m4_changecom () and m4_changequote (). Pass NULL if the argument |
+| was not present, to distinguish from an explicit empty string. |
+`------------------------------------------------------------------*/
+
+void
+set_quotes (const char *lq, const char *rq)
+{
+ free (lquote.string);
+ free (rquote.string);
+
+ /* POSIX states that with 0 arguments, the default quotes are used.
+ POSIX XCU ERN 112 states that behavior is implementation-defined
+ if there was only one argument, or if there is an empty string in
+ either position when there are two arguments. We allow an empty
+ left quote to disable quoting, but a non-empty left quote will
+ always create a non-empty right quote. See the texinfo for what
+ some other implementations do. */
+ if (!lq)
+ {
+ lq = DEF_LQUOTE;
+ rq = DEF_RQUOTE;
+ }
+ else if (!rq || (*lq && !*rq))
+ rq = DEF_RQUOTE;
+
+ lquote.string = xstrdup (lq);
+ lquote.length = strlen (lquote.string);
+ rquote.string = xstrdup (rq);
+ rquote.length = strlen (rquote.string);
+}
+
+void
+set_comment (const char *bc, const char *ec)
+{
+ free (bcomm.string);
+ free (ecomm.string);
+
+ /* POSIX requires no arguments to disable comments. It requires
+ empty arguments to be used as-is, but this is counter to
+ traditional behavior, because a non-null begin and null end makes
+ it impossible to end a comment. An aardvark has been filed:
+ http://www.opengroup.org/austin/mailarchives/ag-review/msg02168.html
+ This implementation assumes the aardvark will be approved. See
+ the texinfo for what some other implementations do. */
+ if (!bc)
+ bc = ec = "";
+ else if (!ec || (*bc && !*ec))
+ ec = DEF_ECOMM;
+
+ bcomm.string = xstrdup (bc);
+ bcomm.length = strlen (bcomm.string);
+ ecomm.string = xstrdup (ec);
+ ecomm.length = strlen (ecomm.string);
+}
+
+#ifdef ENABLE_CHANGEWORD
+
+void
+set_word_regexp (const char *regexp)
+{
+ const char *msg;
+ struct re_pattern_buffer new_word_regexp;
+
+ if (!*regexp || STREQ (regexp, DEFAULT_WORD_REGEXP))
+ {
+ default_word_regexp = true;
+ return;
+ }
+
+ /* Dry run to see whether the new expression is compilable. */
+ init_pattern_buffer (&new_word_regexp, NULL);
+ msg = re_compile_pattern (regexp, strlen (regexp), &new_word_regexp);
+ regfree (&new_word_regexp);
+
+ if (msg != NULL)
+ {
+ M4ERROR ((warning_status, 0,
+ "bad regular expression `%s': %s", regexp, msg));
+ return;
+ }
+
+ /* If compilation worked, retry using the word_regexp struct. We
+ can't rely on struct assigns working, so redo the compilation.
+ The fastmap can be reused between compilations, and will be freed
+ by the final regfree. */
+ if (!word_regexp.fastmap)
+ word_regexp.fastmap = xcharalloc (UCHAR_MAX + 1);
+ msg = re_compile_pattern (regexp, strlen (regexp), &word_regexp);
+ assert (!msg);
+ re_set_registers (&word_regexp, &regs, regs.num_regs, regs.start, regs.end);
+ if (re_compile_fastmap (&word_regexp))
+ assert (false);
+
+ default_word_regexp = false;
+}
+
+#endif /* ENABLE_CHANGEWORD */
+
+
+/*--------------------------------------------------------------------.
+| Parse and return a single token from the input stream. A token |
+| can either be TOKEN_EOF, if the input_stack is empty; it can be |
+| TOKEN_STRING for a quoted string; TOKEN_WORD for something that is |
+| a potential macro name; and TOKEN_SIMPLE for any single character |
+| that is not a part of any of the previous types. If LINE is not |
+| NULL, set *LINE to the line where the token starts. |
+| |
+| Next_token () return the token type, and passes back a pointer to |
+| the token data through TD. The token text is collected on the |
+| obstack token_stack, which never contains more than one token text |
+| at a time. The storage pointed to by the fields in TD is |
+| therefore subject to change the next time next_token () is called. |
+`--------------------------------------------------------------------*/
+
+token_type
+next_token (token_data *td, int *line)
+{
+ int ch;
+ int quote_level;
+ token_type type;
+#ifdef ENABLE_CHANGEWORD
+ int startpos;
+ char *orig_text = NULL;
+#endif
+ const char *file;
+ int dummy;
+
+ obstack_free (&token_stack, token_bottom);
+ if (!line)
+ line = &dummy;
+
+ /* Can't consume character until after CHAR_MACRO is handled. */
+ ch = peek_input ();
+ if (ch == CHAR_EOF)
+ {
+#ifdef DEBUG_INPUT
+ xfprintf (stderr, "next_token -> EOF\n");
+#endif
+ next_char ();
+ return TOKEN_EOF;
+ }
+ if (ch == CHAR_MACRO)
+ {
+ init_macro_token (td);
+ next_char ();
+#ifdef DEBUG_INPUT
+ xfprintf (stderr, "next_token -> MACDEF (%s)\n",
+ find_builtin_by_addr (TOKEN_DATA_FUNC (td))->name);
+#endif
+ return TOKEN_MACDEF;
+ }
+
+ next_char (); /* Consume character we already peeked at. */
+ file = current_file;
+ *line = current_line;
+ if (MATCH (ch, bcomm.string, true))
+ {
+ obstack_grow (&token_stack, bcomm.string, bcomm.length);
+ while ((ch = next_char ()) != CHAR_EOF
+ && !MATCH (ch, ecomm.string, true))
+ obstack_1grow (&token_stack, ch);
+ if (ch != CHAR_EOF)
+ obstack_grow (&token_stack, ecomm.string, ecomm.length);
+ else
+ /* current_file changed to "" if we see CHAR_EOF, use the
+ previous value we stored earlier. */
+ M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, *line,
+ "ERROR: end of file in comment"));
+
+ type = TOKEN_STRING;
+ }
+ else if (default_word_regexp && (isalpha (ch) || ch == '_'))
+ {
+ obstack_1grow (&token_stack, ch);
+ while ((ch = peek_input ()) != CHAR_EOF && (isalnum (ch) || ch == '_'))
+ {
+ obstack_1grow (&token_stack, ch);
+ next_char ();
+ }
+ type = TOKEN_WORD;
+ }
+
+#ifdef ENABLE_CHANGEWORD
+
+ else if (!default_word_regexp && word_regexp.fastmap[ch])
+ {
+ obstack_1grow (&token_stack, ch);
+ while (1)
+ {
+ ch = peek_input ();
+ if (ch == CHAR_EOF)
+ break;
+ obstack_1grow (&token_stack, ch);
+ startpos = re_search (&word_regexp,
+ (char *) obstack_base (&token_stack),
+ obstack_object_size (&token_stack), 0, 0,
+ &regs);
+ if (startpos ||
+ regs.end [0] != (regoff_t) obstack_object_size (&token_stack))
+ {
+ *(((char *) obstack_base (&token_stack)
+ + obstack_object_size (&token_stack)) - 1) = '\0';
+ break;
+ }
+ next_char ();
+ }
+
+ obstack_1grow (&token_stack, '\0');
+ orig_text = (char *) obstack_finish (&token_stack);
+
+ if (regs.start[1] != -1)
+ obstack_grow (&token_stack,orig_text + regs.start[1],
+ regs.end[1] - regs.start[1]);
+ else
+ obstack_grow (&token_stack, orig_text,regs.end[0]);
+
+ type = TOKEN_WORD;
+ }
+
+#endif /* ENABLE_CHANGEWORD */
+
+ else if (!MATCH (ch, lquote.string, true))
+ {
+ switch (ch)
+ {
+ case '(':
+ type = TOKEN_OPEN;
+ break;
+ case ',':
+ type = TOKEN_COMMA;
+ break;
+ case ')':
+ type = TOKEN_CLOSE;
+ break;
+ default:
+ type = TOKEN_SIMPLE;
+ break;
+ }
+ obstack_1grow (&token_stack, ch);
+ }
+ else
+ {
+ bool fast = lquote.length == 1 && rquote.length == 1;
+ quote_level = 1;
+ while (1)
+ {
+ /* Try scanning a buffer first. */
+ const char *buffer = (isp && isp->type == INPUT_STRING
+ ? isp->u.u_s.string : NULL);
+ if (buffer && *buffer)
+ {
+ size_t len = isp->u.u_s.end - buffer;
+ const char *p = buffer;
+ do
+ {
+ p = (char *) memchr2 (p, *lquote.string, *rquote.string,
+ buffer + len - p);
+ }
+ while (p && fast && (*p++ == *rquote.string
+ ? --quote_level : ++quote_level));
+ if (p)
+ {
+ if (fast)
+ {
+ assert (!quote_level);
+ obstack_grow (&token_stack, buffer, p - buffer - 1);
+ isp->u.u_s.string += p - buffer;
+ break;
+ }
+ obstack_grow (&token_stack, buffer, p - buffer);
+ ch = to_uchar (*p);
+ isp->u.u_s.string += p - buffer + 1;
+ }
+ else
+ {
+ obstack_grow (&token_stack, buffer, len);
+ isp->u.u_s.string += len;
+ continue;
+ }
+ }
+ /* Fall back to a byte. */
+ else
+ ch = next_char ();
+ if (ch == CHAR_EOF)
+ /* current_file changed to "" if we see CHAR_EOF, use
+ the previous value we stored earlier. */
+ M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, *line,
+ "ERROR: end of file in string"));
+
+ if (MATCH (ch, rquote.string, true))
+ {
+ if (--quote_level == 0)
+ break;
+ obstack_grow (&token_stack, rquote.string, rquote.length);
+ }
+ else if (MATCH (ch, lquote.string, true))
+ {
+ quote_level++;
+ obstack_grow (&token_stack, lquote.string, lquote.length);
+ }
+ else
+ obstack_1grow (&token_stack, ch);
+ }
+ type = TOKEN_STRING;
+ }
+
+ obstack_1grow (&token_stack, '\0');
+
+ TOKEN_DATA_TYPE (td) = TOKEN_TEXT;
+ TOKEN_DATA_TEXT (td) = (char *) obstack_finish (&token_stack);
+#ifdef ENABLE_CHANGEWORD
+ if (orig_text == NULL)
+ orig_text = TOKEN_DATA_TEXT (td);
+ TOKEN_DATA_ORIG_TEXT (td) = orig_text;
+#endif
+#ifdef DEBUG_INPUT
+ xfprintf (stderr, "next_token -> %s (%s)\n",
+ token_type_string (type), TOKEN_DATA_TEXT (td));
+#endif
+ return type;
+}
+
+/*-----------------------------------------------.
+| Peek at the next token from the input stream. |
+`-----------------------------------------------*/
+
+token_type
+peek_token (void)
+{
+ token_type result;
+ int ch = peek_input ();
+
+ if (ch == CHAR_EOF)
+ {
+ result = TOKEN_EOF;
+ }
+ else if (ch == CHAR_MACRO)
+ {
+ result = TOKEN_MACDEF;
+ }
+ else if (MATCH (ch, bcomm.string, false))
+ {
+ result = TOKEN_STRING;
+ }
+ else if ((default_word_regexp && (isalpha (ch) || ch == '_'))
+#ifdef ENABLE_CHANGEWORD
+ || (! default_word_regexp && word_regexp.fastmap[ch])
+#endif /* ENABLE_CHANGEWORD */
+ )
+ {
+ result = TOKEN_WORD;
+ }
+ else if (MATCH (ch, lquote.string, false))
+ {
+ result = TOKEN_STRING;
+ }
+ else
+ switch (ch)
+ {
+ case '(':
+ result = TOKEN_OPEN;
+ break;
+ case ',':
+ result = TOKEN_COMMA;
+ break;
+ case ')':
+ result = TOKEN_CLOSE;
+ break;
+ default:
+ result = TOKEN_SIMPLE;
+ }
+
+#ifdef DEBUG_INPUT
+ xfprintf (stderr, "peek_token -> %s\n", token_type_string (result));
+#endif /* DEBUG_INPUT */
+ return result;
+}
+
+
+#ifdef DEBUG_INPUT
+
+static const char *
+token_type_string (token_type t)
+{
+ switch (t)
+ { /* TOKSW */
+ case TOKEN_EOF:
+ return "EOF";
+ case TOKEN_STRING:
+ return "STRING";
+ case TOKEN_WORD:
+ return "WORD";
+ case TOKEN_OPEN:
+ return "OPEN";
+ case TOKEN_COMMA:
+ return "COMMA";
+ case TOKEN_CLOSE:
+ return "CLOSE";
+ case TOKEN_SIMPLE:
+ return "SIMPLE";
+ case TOKEN_MACDEF:
+ return "MACDEF";
+ default:
+ abort ();
+ }
+ }
+
+static void
+print_token (const char *s, token_type t, token_data *td)
+{
+ xfprintf (stderr, "%s: ", s);
+ switch (t)
+ { /* TOKSW */
+ case TOKEN_OPEN:
+ case TOKEN_COMMA:
+ case TOKEN_CLOSE:
+ case TOKEN_SIMPLE:
+ xfprintf (stderr, "char:");
+ break;
+
+ case TOKEN_WORD:
+ xfprintf (stderr, "word:");
+ break;
+
+ case TOKEN_STRING:
+ xfprintf (stderr, "string:");
+ break;
+
+ case TOKEN_MACDEF:
+ xfprintf (stderr, "macro: %p\n", TOKEN_DATA_FUNC (td));
+ break;
+
+ case TOKEN_EOF:
+ xfprintf (stderr, "eof\n");
+ break;
+ }
+ xfprintf (stderr, "\t\"%s\"\n", TOKEN_DATA_TEXT (td));
+}
+
+static void M4_GNUC_UNUSED
+lex_debug (void)
+{
+ token_type t;
+ token_data td;
+
+ while ((t = next_token (&td, NULL)) != TOKEN_EOF)
+ print_token ("lex", t, &td);
+}
+#endif /* DEBUG_INPUT */
diff --git a/src/m4.c b/src/m4.c
new file mode 100644
index 0000000..d7ad7cd
--- /dev/null
+++ b/src/m4.c
@@ -0,0 +1,688 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2004-2011 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/>.
+*/
+
+#include "m4.h"
+
+#include <getopt.h>
+#include <limits.h>
+#include <signal.h>
+
+#include "c-stack.h"
+#include "ignore-value.h"
+#include "progname.h"
+#include "version-etc.h"
+
+#ifdef DEBUG_STKOVF
+# include "assert.h"
+#endif
+
+#define AUTHORS "Rene' Seindal"
+
+static void usage (int) M4_GNUC_NORETURN;
+
+/* Enable sync output for /lib/cpp (-s). */
+int sync_output = 0;
+
+/* Debug (-d[flags]). */
+int debug_level = 0;
+
+/* Hash table size (should be a prime) (-Hsize). */
+size_t hash_table_size = HASHMAX;
+
+/* Disable GNU extensions (-G). */
+int no_gnu_extensions = 0;
+
+/* Prefix all builtin functions by `m4_'. */
+int prefix_all_builtins = 0;
+
+/* Max length of arguments in trace output (-lsize). */
+int max_debug_argument_length = 0;
+
+/* Suppress warnings about missing arguments. */
+int suppress_warnings = 0;
+
+/* If true, then warnings affect exit status. */
+static bool fatal_warnings = false;
+
+/* If not zero, then value of exit status for warning diagnostics. */
+int warning_status = 0;
+
+/* Artificial limit for expansion_level in macro.c. */
+int nesting_limit = 1024;
+
+#ifdef ENABLE_CHANGEWORD
+/* User provided regexp for describing m4 words. */
+const char *user_word_regexp = "";
+#endif
+
+/* Global catchall for any errors that should affect final error status, but
+ where we try to continue execution in the meantime. */
+int retcode;
+
+struct macro_definition
+{
+ struct macro_definition *next;
+ int code; /* D, U, s, t, '\1', or DEBUGFILE_OPTION. */
+ const char *arg;
+};
+typedef struct macro_definition macro_definition;
+
+/* Error handling functions. */
+
+/*-----------------------.
+| Wrapper around error. |
+`-----------------------*/
+
+void
+m4_error (int status, int errnum, const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ verror_at_line (status, errnum, current_line ? current_file : NULL,
+ current_line, format, args);
+ if (fatal_warnings && ! retcode)
+ retcode = EXIT_FAILURE;
+}
+
+/*-------------------------------.
+| Wrapper around error_at_line. |
+`-------------------------------*/
+
+void
+m4_error_at_line (int status, int errnum, const char *file, int line,
+ const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ verror_at_line (status, errnum, line ? file : NULL, line, format, args);
+ if (fatal_warnings && ! retcode)
+ retcode = EXIT_FAILURE;
+}
+
+#ifndef SIGBUS
+# define SIGBUS SIGILL
+#endif
+
+#ifndef NSIG
+# ifndef MAX
+# define MAX(a,b) ((a) < (b) ? (b) : (a))
+# endif
+# define NSIG (MAX (SIGABRT, MAX (SIGILL, MAX (SIGFPE, \
+ MAX (SIGSEGV, SIGBUS)))) + 1)
+#endif
+
+/* Pre-translated messages for program errors. Do not translate in
+ the signal handler, since gettext and strsignal are not
+ async-signal-safe. */
+static const char * volatile program_error_message;
+static const char * volatile signal_message[NSIG];
+
+/* Print a nicer message about any programmer errors, then exit. This
+ must be aysnc-signal safe, since it is executed as a signal
+ handler. If SIGNO is zero, this represents a stack overflow; in
+ that case, we return to allow c_stack_action to handle things. */
+static void
+fault_handler (int signo)
+{
+ if (signo)
+ {
+ /* POSIX states that reading static memory is, in general, not
+ async-safe. However, the static variables that we read are
+ never modified once this handler is installed, so this
+ particular usage is safe. And it seems an oversight that
+ POSIX claims strlen is not async-safe. Ignore write
+ failures, since we will exit with non-zero status anyway. */
+#define WRITE(f, b, l) ignore_value (write (f, b, l))
+ WRITE (STDERR_FILENO, program_name, strlen (program_name));
+ WRITE (STDERR_FILENO, ": ", 2);
+ WRITE (STDERR_FILENO, program_error_message,
+ strlen (program_error_message));
+ if (signal_message[signo])
+ {
+ WRITE (STDERR_FILENO, ": ", 2);
+ WRITE (STDERR_FILENO, signal_message[signo],
+ strlen (signal_message[signo]));
+ }
+ WRITE (STDERR_FILENO, "\n", 1);
+#undef WRITE
+ _exit (EXIT_INTERNAL_ERROR);
+ }
+}
+
+
+/*---------------------------------------------.
+| Print a usage message and exit with STATUS. |
+`---------------------------------------------*/
+
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ xfprintf (stderr, "Try `%s --help' for more information.\n", program_name);
+ else
+ {
+ xprintf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
+ fputs ("\
+Process macros in FILEs. If no FILE or if FILE is `-', standard input\n\
+is read.\n\
+", stdout);
+ fputs ("\
+\n\
+Mandatory or optional arguments to long options are mandatory or optional\n\
+for short options too.\n\
+\n\
+Operation modes:\n\
+ --help display this help and exit\n\
+ --version output version information and exit\n\
+", stdout);
+ xprintf ("\
+ -E, --fatal-warnings once: warnings become errors, twice: stop\n\
+ execution at first error\n\
+ -i, --interactive unbuffer output, ignore interrupts\n\
+ -P, --prefix-builtins force a `m4_' prefix to all builtins\n\
+ -Q, --quiet, --silent suppress some warnings for builtins\n\
+ --warn-macro-sequence[=REGEXP]\n\
+ warn if macro definition matches REGEXP,\n\
+ default %s\n\
+", DEFAULT_MACRO_SEQUENCE);
+#ifdef ENABLE_CHANGEWORD
+ fputs ("\
+ -W, --word-regexp=REGEXP use REGEXP for macro name syntax\n\
+", stdout);
+#endif
+ fputs ("\
+\n\
+Preprocessor features:\n\
+ -D, --define=NAME[=VALUE] define NAME as having VALUE, or empty\n\
+ -I, --include=DIRECTORY append DIRECTORY to include path\n\
+ -s, --synclines generate `#line NUM \"FILE\"' lines\n\
+ -U, --undefine=NAME undefine NAME\n\
+", stdout);
+ puts ("");
+ xprintf (_("\
+Limits control:\n\
+ -g, --gnu override -G to re-enable GNU extensions\n\
+ -G, --traditional suppress all GNU extensions\n\
+ -H, --hashsize=PRIME set symbol lookup hash table size [509]\n\
+ -L, --nesting-limit=NUMBER change nesting limit, 0 for unlimited [%d]\n\
+"), nesting_limit);
+ puts ("");
+ fputs ("\
+Frozen state files:\n\
+ -F, --freeze-state=FILE produce a frozen state on FILE at end\n\
+ -R, --reload-state=FILE reload a frozen state from FILE at start\n\
+", stdout);
+ fputs ("\
+\n\
+Debugging:\n\
+ -d, --debug[=FLAGS] set debug level (no FLAGS implies `aeq')\n\
+ --debugfile[=FILE] redirect debug and trace output to FILE\n\
+ (default stderr, discard if empty string)\n\
+ -l, --arglength=NUM restrict macro tracing size\n\
+ -t, --trace=NAME trace NAME when it is defined\n\
+", stdout);
+ fputs ("\
+\n\
+FLAGS is any of:\n\
+ a show actual arguments\n\
+ c show before collect, after collect and after call\n\
+ e show expansion\n\
+ f say current input file name\n\
+ i show changes in input files\n\
+ l say current input line number\n\
+ p show results of path searches\n\
+ q quote values as necessary, with a or e flag\n\
+ t trace for all macro calls, not only traceon'ed\n\
+ x add a unique macro call id, useful with c flag\n\
+ V shorthand for all of the above flags\n\
+", stdout);
+ fputs ("\
+\n\
+If defined, the environment variable `M4PATH' is a colon-separated list\n\
+of directories included after any specified by `-I'.\n\
+", stdout);
+ fputs ("\
+\n\
+Exit status is 0 for success, 1 for failure, 63 for frozen file version\n\
+mismatch, or whatever value was passed to the m4exit macro.\n\
+", stdout);
+ emit_bug_reporting_address ();
+ }
+ exit (status);
+}
+
+/*--------------------------------------.
+| Decode options and launch execution. |
+`--------------------------------------*/
+
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ DEBUGFILE_OPTION = CHAR_MAX + 1, /* no short opt */
+ DIVERSIONS_OPTION, /* not quite -N, because of message */
+ WARN_MACRO_SEQUENCE_OPTION, /* no short opt */
+
+ HELP_OPTION, /* no short opt */
+ VERSION_OPTION /* no short opt */
+};
+
+static const struct option long_options[] =
+{
+ {"arglength", required_argument, NULL, 'l'},
+ {"debug", optional_argument, NULL, 'd'},
+ {"define", required_argument, NULL, 'D'},
+ {"error-output", required_argument, NULL, 'o'}, /* FIXME: deprecate in 2.0 */
+ {"fatal-warnings", no_argument, NULL, 'E'},
+ {"freeze-state", required_argument, NULL, 'F'},
+ {"gnu", no_argument, NULL, 'g'},
+ {"hashsize", required_argument, NULL, 'H'},
+ {"include", required_argument, NULL, 'I'},
+ {"interactive", no_argument, NULL, 'i'},
+ {"nesting-limit", required_argument, NULL, 'L'},
+ {"prefix-builtins", no_argument, NULL, 'P'},
+ {"quiet", no_argument, NULL, 'Q'},
+ {"reload-state", required_argument, NULL, 'R'},
+ {"silent", no_argument, NULL, 'Q'},
+ {"synclines", no_argument, NULL, 's'},
+ {"trace", required_argument, NULL, 't'},
+ {"traditional", no_argument, NULL, 'G'},
+ {"undefine", required_argument, NULL, 'U'},
+ {"word-regexp", required_argument, NULL, 'W'},
+
+ {"debugfile", optional_argument, NULL, DEBUGFILE_OPTION},
+ {"diversions", required_argument, NULL, DIVERSIONS_OPTION},
+ {"warn-macro-sequence", optional_argument, NULL, WARN_MACRO_SEQUENCE_OPTION},
+
+ {"help", no_argument, NULL, HELP_OPTION},
+ {"version", no_argument, NULL, VERSION_OPTION},
+
+ { NULL, 0, NULL, 0 },
+};
+
+/* Process a command line file NAME, and return true only if it was
+ stdin. */
+static void
+process_file (const char *name)
+{
+ if (STREQ (name, "-"))
+ {
+ /* If stdin is a terminal, we want to allow 'm4 - file -'
+ to read input from stdin twice, like GNU cat. Besides,
+ there is no point closing stdin before wrapped text, to
+ minimize bugs in syscmd called from wrapped text. */
+ push_file (stdin, "stdin", false);
+ }
+ else
+ {
+ char *full_name;
+ FILE *fp = m4_path_search (name, &full_name);
+ if (fp == NULL)
+ {
+ error (0, errno, _("cannot open `%s'"), name);
+ /* Set the status to EXIT_FAILURE, even though we
+ continue to process files after a missing file. */
+ retcode = EXIT_FAILURE;
+ return;
+ }
+ push_file (fp, full_name, true);
+ free (full_name);
+ }
+ expand_input ();
+}
+
+/* POSIX requires only -D, -U, and -s; and says that the first two
+ must be recognized when interspersed with file names. Traditional
+ behavior also handles -s between files. Starting OPTSTRING with
+ '-' forces getopt_long to hand back file names as arguments to opt
+ '\1', rather than reordering the command line. */
+#ifdef ENABLE_CHANGEWORD
+#define OPTSTRING "-B:D:EF:GH:I:L:N:PQR:S:T:U:W:d::egil:o:st:"
+#else
+#define OPTSTRING "-B:D:EF:GH:I:L:N:PQR:S:T:U:d::egil:o:st:"
+#endif
+
+int
+main (int argc, char *const *argv)
+{
+ struct sigaction act;
+ macro_definition *head; /* head of deferred argument list */
+ macro_definition *tail;
+ macro_definition *defn;
+ int optchar; /* option character */
+
+ macro_definition *defines;
+ bool interactive = false;
+ bool seen_file = false;
+ const char *debugfile = NULL;
+ const char *frozen_file_to_read = NULL;
+ const char *frozen_file_to_write = NULL;
+ const char *macro_sequence = "";
+
+ set_program_name (argv[0]);
+ retcode = EXIT_SUCCESS;
+ atexit (close_stdin);
+
+ include_init ();
+ debug_init ();
+
+ /* Stack overflow and program error handling. Ignore failure to
+ install a handler, since this is merely for improved output on
+ crash, and we should never crash ;). We install SIGBUS and
+ SIGSEGV handlers prior to using the c-stack module; depending on
+ the platform, c-stack will then override none, SIGSEGV, or both
+ handlers. */
+ program_error_message
+ = xasprintf (_("internal error detected; please report this bug to <%s>"),
+ PACKAGE_BUGREPORT);
+ signal_message[SIGSEGV] = xstrdup (strsignal (SIGSEGV));
+ signal_message[SIGABRT] = xstrdup (strsignal (SIGABRT));
+ signal_message[SIGILL] = xstrdup (strsignal (SIGILL));
+ signal_message[SIGFPE] = xstrdup (strsignal (SIGFPE));
+ if (SIGBUS != SIGILL && SIGBUS != SIGSEGV)
+ signal_message[SIGBUS] = xstrdup (strsignal (SIGBUS));
+ sigemptyset (&act.sa_mask);
+ /* One-shot - if we fault while handling a fault, we want to revert
+ to default signal behavior. */
+ act.sa_flags = SA_NODEFER | SA_RESETHAND;
+ act.sa_handler = fault_handler;
+ sigaction (SIGSEGV, &act, NULL);
+ sigaction (SIGABRT, &act, NULL);
+ sigaction (SIGILL, &act, NULL);
+ sigaction (SIGFPE, &act, NULL);
+ sigaction (SIGBUS, &act, NULL);
+ if (c_stack_action (fault_handler) == 0)
+ nesting_limit = 0;
+
+#ifdef DEBUG_STKOVF
+ /* Make it easier to test our fault handlers. Exporting M4_CRASH=0
+ attempts a SIGSEGV, exporting it as 1 attempts an assertion
+ failure with a fallback to abort. */
+ {
+ char *crash = getenv ("M4_CRASH");
+ if (crash)
+ {
+ if (!strtol (crash, NULL, 10))
+ ++*(int *) 8;
+ assert (false);
+ abort ();
+ }
+ }
+#endif /* DEBUG_STKOVF */
+
+ /* First, we decode the arguments, to size up tables and stuff. */
+ head = tail = NULL;
+
+ while ((optchar = getopt_long (argc, (char **) argv, OPTSTRING,
+ long_options, NULL)) != -1)
+ switch (optchar)
+ {
+ default:
+ usage (EXIT_FAILURE);
+
+ case 'B':
+ case 'S':
+ case 'T':
+ /* Compatibility junk: options that other implementations
+ support, but which we ignore as no-ops and don't list in
+ --help. */
+ error (0, 0, _("warning: `m4 -%c' may be removed in a future release"),
+ optchar);
+ break;
+
+ case 'N':
+ case DIVERSIONS_OPTION:
+ /* -N became an obsolete no-op in 1.4.x. */
+ error (0, 0, _("warning: `m4 %s' is deprecated"),
+ optchar == 'N' ? "-N" : "--diversions");
+ break;
+
+ case 'D':
+ case 'U':
+ case 's':
+ case 't':
+ case '\1':
+ case DEBUGFILE_OPTION:
+ /* Arguments that cannot be handled until later are accumulated. */
+
+ defn = (macro_definition *) xmalloc (sizeof (macro_definition));
+ defn->code = optchar;
+ defn->arg = optarg;
+ defn->next = NULL;
+
+ if (head == NULL)
+ head = defn;
+ else
+ tail->next = defn;
+ tail = defn;
+
+ break;
+
+ case 'E':
+ if (! fatal_warnings)
+ fatal_warnings = true;
+ else
+ warning_status = EXIT_FAILURE;
+ break;
+
+ case 'F':
+ frozen_file_to_write = optarg;
+ break;
+
+ case 'G':
+ no_gnu_extensions = 1;
+ break;
+
+ case 'H':
+ hash_table_size = strtol (optarg, NULL, 10);
+ if (hash_table_size == 0)
+ hash_table_size = HASHMAX;
+ break;
+
+ case 'I':
+ add_include_directory (optarg);
+ break;
+
+ case 'L':
+ nesting_limit = strtol (optarg, NULL, 10);
+ break;
+
+ case 'P':
+ prefix_all_builtins = 1;
+ break;
+
+ case 'Q':
+ suppress_warnings = 1;
+ break;
+
+ case 'R':
+ frozen_file_to_read = optarg;
+ break;
+
+#ifdef ENABLE_CHANGEWORD
+ case 'W':
+ user_word_regexp = optarg;
+ break;
+#endif
+
+ case 'd':
+ debug_level = debug_decode (optarg);
+ if (debug_level < 0)
+ {
+ error (0, 0, _("bad debug flags: `%s'"), optarg);
+ debug_level = 0;
+ }
+ break;
+
+ case 'e':
+ error (0, 0, _("warning: `m4 -e' is deprecated, use `-i' instead"));
+ /* fall through */
+ case 'i':
+ interactive = true;
+ break;
+
+ case 'g':
+ no_gnu_extensions = 0;
+ break;
+
+ case 'l':
+ max_debug_argument_length = strtol (optarg, NULL, 10);
+ if (max_debug_argument_length <= 0)
+ max_debug_argument_length = 0;
+ break;
+
+ case 'o':
+ /* -o/--error-output are deprecated synonyms of --debugfile,
+ but don't issue a deprecation warning until autoconf 2.61
+ or later is more widely established, as such a warning
+ would interfere with all earlier versions of autoconf. */
+ /* Don't call debug_set_output here, as it has side effects. */
+ debugfile = optarg;
+ break;
+
+ case WARN_MACRO_SEQUENCE_OPTION:
+ /* Don't call set_macro_sequence here, as it can exit.
+ --warn-macro-sequence sets optarg to NULL (which uses the
+ default regexp); --warn-macro-sequence= sets optarg to ""
+ (which disables these warnings). */
+ macro_sequence = optarg;
+ break;
+
+ case VERSION_OPTION:
+ version_etc (stdout, PACKAGE, PACKAGE_NAME, VERSION, AUTHORS, NULL);
+ exit (EXIT_SUCCESS);
+ break;
+
+ case HELP_OPTION:
+ usage (EXIT_SUCCESS);
+ break;
+ }
+
+ defines = head;
+
+ /* Do the basic initializations. */
+ if (debugfile && !debug_set_output (debugfile))
+ M4ERROR ((warning_status, errno, "cannot set debug file `%s'", debugfile));
+
+ input_init ();
+ output_init ();
+ symtab_init ();
+ set_macro_sequence (macro_sequence);
+ include_env_init ();
+
+ if (frozen_file_to_read)
+ reload_frozen_state (frozen_file_to_read);
+ else
+ builtin_init ();
+
+ /* Interactive mode means unbuffered output, and interrupts ignored. */
+
+ if (interactive)
+ {
+ signal (SIGINT, SIG_IGN);
+ setbuf (stdout, (char *) NULL);
+ }
+
+ /* Handle deferred command line macro definitions. Must come after
+ initialization of the symbol table. */
+
+ while (defines != NULL)
+ {
+ macro_definition *next;
+ symbol *sym;
+
+ switch (defines->code)
+ {
+ case 'D':
+ {
+ /* defines->arg is read-only, so we need a copy. */
+ char *macro_name = xstrdup (defines->arg);
+ char *macro_value = strchr (macro_name, '=');
+ if (macro_value)
+ *macro_value++ = '\0';
+ define_user_macro (macro_name, macro_value, SYMBOL_INSERT);
+ free (macro_name);
+ }
+ break;
+
+ case 'U':
+ lookup_symbol (defines->arg, SYMBOL_DELETE);
+ break;
+
+ case 't':
+ sym = lookup_symbol (defines->arg, SYMBOL_INSERT);
+ SYMBOL_TRACED (sym) = true;
+ break;
+
+ case 's':
+ sync_output = 1;
+ break;
+
+ case '\1':
+ seen_file = true;
+ process_file (defines->arg);
+ break;
+
+ case DEBUGFILE_OPTION:
+ if (!debug_set_output (defines->arg))
+ M4ERROR ((warning_status, errno, "cannot set debug file `%s'",
+ debugfile ? debugfile : _("stderr")));
+ break;
+
+ default:
+ M4ERROR ((0, 0, "INTERNAL ERROR: bad code in deferred arguments"));
+ abort ();
+ }
+
+ next = defines->next;
+ free (defines);
+ defines = next;
+ }
+
+ /* Handle remaining input files. Each file is pushed on the input,
+ and the input read. Wrapup text is handled separately later. */
+
+ if (optind == argc && !seen_file)
+ process_file ("-");
+ else
+ for (; optind < argc; optind++)
+ process_file (argv[optind]);
+
+ /* Now handle wrapup text. */
+
+ while (pop_wrapup ())
+ expand_input ();
+
+ /* Change debug stream back to stderr, to force flushing the debug
+ stream and detect any errors it might have encountered. The
+ three standard streams are closed by close_stdin. */
+ debug_set_output (NULL);
+
+ if (frozen_file_to_write)
+ produce_frozen_state (frozen_file_to_write);
+ else
+ {
+ make_diversion (0);
+ undivert_all ();
+ }
+ output_exit ();
+ free_macro_sequence ();
+ exit (retcode);
+}
diff --git a/src/m4.h b/src/m4.h
new file mode 100644
index 0000000..3708a7c
--- /dev/null
+++ b/src/m4.h
@@ -0,0 +1,487 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2004-2011 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/>.
+*/
+
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+
+#include <config.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "binary-io.h"
+#include "clean-temp.h"
+#include "cloexec.h"
+#include "close-stream.h"
+#include "closein.h"
+#include "dirname.h"
+#include "error.h"
+#include "exitfail.h"
+#include "filenamecat.h"
+#include "obstack.h"
+#include "stdio--.h"
+#include "stdlib--.h"
+#include "unistd--.h"
+#include "verror.h"
+#include "xalloc.h"
+#include "xprintf.h"
+#include "xvasprintf.h"
+
+/* Canonicalize UNIX recognition macros. */
+#if defined unix || defined __unix || defined __unix__ \
+ || defined _POSIX_VERSION || defined _POSIX2_VERSION \
+ || defined __NetBSD__ || defined __OpenBSD__ \
+ || defined __APPLE__ || defined __APPLE_CC__
+# define UNIX 1
+#endif
+
+/* Canonicalize Windows recognition macros. */
+#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
+# define W32_NATIVE 1
+#endif
+
+/* Canonicalize OS/2 recognition macro. */
+#ifdef __EMX__
+# define OS2 1
+# undef UNIX
+#endif
+
+/* Used if any programmer error is detected (not possible, right?) */
+#define EXIT_INTERNAL_ERROR 2
+
+/* Used for version mismatch, when -R detects a frozen file it can't parse. */
+#define EXIT_MISMATCH 63
+
+/* No-op, for future gettext compatibility. */
+#define _(ARG) ARG
+
+/* Various declarations. */
+
+struct string
+ {
+ char *string; /* characters of the string */
+ size_t length; /* length of the string */
+ };
+typedef struct string STRING;
+
+/* Memory allocation. */
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* Those must come first. */
+typedef struct token_data token_data;
+typedef void builtin_func (struct obstack *, int, token_data **);
+
+/* Gnulib's stdbool doesn't work with bool bitfields. For nicer
+ debugging, use bool when we know it works, but use the more
+ portable unsigned int elsewhere. */
+#if __GNUC__ > 2
+typedef bool bool_bitfield;
+#else
+typedef unsigned int bool_bitfield;
+#endif /* ! __GNUC__ */
+
+/* Take advantage of GNU C compiler source level optimization hints,
+ using portable macros. */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 6)
+# define M4_GNUC_ATTRIBUTE(args) __attribute__ (args)
+#else
+# define M4_GNUC_ATTRIBUTE(args)
+#endif /* __GNUC__ */
+
+#define M4_GNUC_UNUSED M4_GNUC_ATTRIBUTE ((__unused__))
+#define M4_GNUC_PRINTF(fmt, arg) \
+ M4_GNUC_ATTRIBUTE ((__format__ (__printf__, fmt, arg)))
+#define M4_GNUC_NORETURN M4_GNUC_ATTRIBUTE ((__noreturn__))
+
+/* File: m4.c --- global definitions. */
+
+/* Option flags. */
+extern int sync_output; /* -s */
+extern int debug_level; /* -d */
+extern size_t hash_table_size; /* -H */
+extern int no_gnu_extensions; /* -G */
+extern int prefix_all_builtins; /* -P */
+extern int max_debug_argument_length; /* -l */
+extern int suppress_warnings; /* -Q */
+extern int warning_status; /* -E */
+extern int nesting_limit; /* -L */
+#ifdef ENABLE_CHANGEWORD
+extern const char *user_word_regexp; /* -W */
+#endif
+
+/* Error handling. */
+extern int retcode;
+extern const char *program_name;
+
+void m4_error (int, int, const char *, ...) M4_GNUC_PRINTF(3, 4);
+void m4_error_at_line (int, int, const char *, int,
+ const char *, ...) M4_GNUC_PRINTF(5, 6);
+
+#define M4ERROR(Arglist) (m4_error Arglist)
+#define M4ERROR_AT_LINE(Arglist) (m4_error_at_line Arglist)
+
+
+/* File: debug.c --- debugging and tracing function. */
+
+extern FILE *debug;
+
+/* The value of debug_level is a bitmask of the following. */
+
+/* a: show arglist in trace output */
+#define DEBUG_TRACE_ARGS 1
+/* e: show expansion in trace output */
+#define DEBUG_TRACE_EXPANSION 2
+/* q: quote args and expansion in trace output */
+#define DEBUG_TRACE_QUOTE 4
+/* t: trace all macros -- overrides trace{on,off} */
+#define DEBUG_TRACE_ALL 8
+/* l: add line numbers to trace output */
+#define DEBUG_TRACE_LINE 16
+/* f: add file name to trace output */
+#define DEBUG_TRACE_FILE 32
+/* p: trace path search of include files */
+#define DEBUG_TRACE_PATH 64
+/* c: show macro call before args collection */
+#define DEBUG_TRACE_CALL 128
+/* i: trace changes of input files */
+#define DEBUG_TRACE_INPUT 256
+/* x: add call id to trace output */
+#define DEBUG_TRACE_CALLID 512
+
+/* V: very verbose -- print everything */
+#define DEBUG_TRACE_VERBOSE 1023
+/* default flags -- equiv: aeq */
+#define DEBUG_TRACE_DEFAULT 7
+
+#define DEBUG_PRINT1(Fmt, Arg1) \
+ do \
+ { \
+ if (debug != NULL) \
+ xfprintf (debug, Fmt, Arg1); \
+ } \
+ while (0)
+
+#define DEBUG_PRINT3(Fmt, Arg1, Arg2, Arg3) \
+ do \
+ { \
+ if (debug != NULL) \
+ xfprintf (debug, Fmt, Arg1, Arg2, Arg3); \
+ } \
+ while (0)
+
+#define DEBUG_MESSAGE(Fmt) \
+ do \
+ { \
+ if (debug != NULL) \
+ { \
+ debug_message_prefix (); \
+ xfprintf (debug, Fmt); \
+ putc ('\n', debug); \
+ } \
+ } \
+ while (0)
+
+#define DEBUG_MESSAGE1(Fmt, Arg1) \
+ do \
+ { \
+ if (debug != NULL) \
+ { \
+ debug_message_prefix (); \
+ xfprintf (debug, Fmt, Arg1); \
+ putc ('\n', debug); \
+ } \
+ } \
+ while (0)
+
+#define DEBUG_MESSAGE2(Fmt, Arg1, Arg2) \
+ do \
+ { \
+ if (debug != NULL) \
+ { \
+ debug_message_prefix (); \
+ xfprintf (debug, Fmt, Arg1, Arg2); \
+ putc ('\n', debug); \
+ } \
+ } \
+ while (0)
+
+void debug_init (void);
+int debug_decode (const char *);
+void debug_flush_files (void);
+bool debug_set_output (const char *);
+void debug_message_prefix (void);
+
+void trace_prepre (const char *, int);
+void trace_pre (const char *, int, int, token_data **);
+void trace_post (const char *, int, int, const char *);
+
+/* File: input.c --- lexical definitions. */
+
+/* Various different token types. */
+enum token_type
+{
+ TOKEN_EOF, /* end of file */
+ TOKEN_STRING, /* a quoted string or comment */
+ TOKEN_WORD, /* an identifier */
+ TOKEN_OPEN, /* ( */
+ TOKEN_COMMA, /* , */
+ TOKEN_CLOSE, /* ) */
+ TOKEN_SIMPLE, /* any other single character */
+ TOKEN_MACDEF /* a macro's definition (see "defn") */
+};
+
+/* The data for a token, a macro argument, and a macro definition. */
+enum token_data_type
+{
+ TOKEN_VOID,
+ TOKEN_TEXT,
+ TOKEN_FUNC
+};
+
+struct token_data
+{
+ enum token_data_type type;
+ union
+ {
+ struct
+ {
+ char *text;
+#ifdef ENABLE_CHANGEWORD
+ char *original_text;
+#endif
+ }
+ u_t;
+ builtin_func *func;
+ }
+ u;
+};
+
+#define TOKEN_DATA_TYPE(Td) ((Td)->type)
+#define TOKEN_DATA_TEXT(Td) ((Td)->u.u_t.text)
+#ifdef ENABLE_CHANGEWORD
+# define TOKEN_DATA_ORIG_TEXT(Td) ((Td)->u.u_t.original_text)
+#endif
+#define TOKEN_DATA_FUNC(Td) ((Td)->u.func)
+
+typedef enum token_type token_type;
+typedef enum token_data_type token_data_type;
+
+void input_init (void);
+token_type peek_token (void);
+token_type next_token (token_data *, int *);
+void skip_line (void);
+
+/* push back input */
+void push_file (FILE *, const char *, bool);
+void push_macro (builtin_func *);
+struct obstack *push_string_init (void);
+const char *push_string_finish (void);
+void push_wrapup (const char *);
+bool pop_wrapup (void);
+
+/* current input file, and line */
+extern const char *current_file;
+extern int current_line;
+
+/* left and right quote, begin and end comment */
+extern STRING bcomm, ecomm;
+extern STRING lquote, rquote;
+
+#define DEF_LQUOTE "`"
+#define DEF_RQUOTE "\'"
+#define DEF_BCOMM "#"
+#define DEF_ECOMM "\n"
+
+void set_quotes (const char *, const char *);
+void set_comment (const char *, const char *);
+#ifdef ENABLE_CHANGEWORD
+void set_word_regexp (const char *);
+#endif
+
+/* File: output.c --- output functions. */
+extern int current_diversion;
+extern int output_current_line;
+
+void output_init (void);
+void output_exit (void);
+void output_text (const char *, int);
+void shipout_text (struct obstack *, const char *, int, int);
+void make_diversion (int);
+void insert_diversion (int);
+void insert_file (FILE *);
+void freeze_diversions (FILE *);
+
+/* File symtab.c --- symbol table definitions. */
+
+/* Operation modes for lookup_symbol (). */
+enum symbol_lookup
+{
+ SYMBOL_LOOKUP,
+ SYMBOL_INSERT,
+ SYMBOL_DELETE,
+ SYMBOL_PUSHDEF,
+ SYMBOL_POPDEF
+};
+
+/* Symbol table entry. */
+struct symbol
+{
+ struct symbol *next;
+ bool_bitfield traced : 1;
+ bool_bitfield shadowed : 1;
+ bool_bitfield macro_args : 1;
+ bool_bitfield blind_no_args : 1;
+ bool_bitfield deleted : 1;
+ int pending_expansions;
+
+ char *name;
+ token_data data;
+};
+
+#define SYMBOL_NEXT(S) ((S)->next)
+#define SYMBOL_TRACED(S) ((S)->traced)
+#define SYMBOL_SHADOWED(S) ((S)->shadowed)
+#define SYMBOL_MACRO_ARGS(S) ((S)->macro_args)
+#define SYMBOL_BLIND_NO_ARGS(S) ((S)->blind_no_args)
+#define SYMBOL_DELETED(S) ((S)->deleted)
+#define SYMBOL_PENDING_EXPANSIONS(S) ((S)->pending_expansions)
+#define SYMBOL_NAME(S) ((S)->name)
+#define SYMBOL_TYPE(S) (TOKEN_DATA_TYPE (&(S)->data))
+#define SYMBOL_TEXT(S) (TOKEN_DATA_TEXT (&(S)->data))
+#define SYMBOL_FUNC(S) (TOKEN_DATA_FUNC (&(S)->data))
+
+typedef enum symbol_lookup symbol_lookup;
+typedef struct symbol symbol;
+typedef void hack_symbol (symbol *, void *);
+
+#define HASHMAX 509 /* default, overridden by -Hsize */
+
+extern symbol **symtab;
+
+void free_symbol (symbol *sym);
+void symtab_init (void);
+symbol *lookup_symbol (const char *, symbol_lookup);
+void hack_all_symbols (hack_symbol *, void *);
+
+/* File: macro.c --- macro expansion. */
+
+void expand_input (void);
+void call_macro (symbol *, int, token_data **, struct obstack *);
+
+/* File: builtin.c --- builtins. */
+
+struct builtin
+{
+ const char *name;
+ bool_bitfield gnu_extension : 1;
+ bool_bitfield groks_macro_args : 1;
+ bool_bitfield blind_if_no_args : 1;
+ builtin_func *func;
+};
+
+struct predefined
+{
+ const char *unix_name;
+ const char *gnu_name;
+ const char *func;
+};
+
+typedef struct builtin builtin;
+typedef struct predefined predefined;
+struct re_pattern_buffer;
+struct re_registers;
+
+/* The default sequence detects multi-digit parameters (obsolete after
+ 1.4.x), and any use of extended arguments with the default ${}
+ syntax (new in 2.0). */
+#define DEFAULT_MACRO_SEQUENCE "\\$\\({[^}]*}\\|[0-9][0-9]+\\)"
+
+void builtin_init (void);
+void define_builtin (const char *, const builtin *, symbol_lookup);
+void set_macro_sequence (const char *);
+void free_macro_sequence (void);
+void define_user_macro (const char *, const char *, symbol_lookup);
+void undivert_all (void);
+void expand_user_macro (struct obstack *, symbol *, int, token_data **);
+void m4_placeholder (struct obstack *, int, token_data **);
+void init_pattern_buffer (struct re_pattern_buffer *, struct re_registers *);
+const char *ntoa (int32_t, int);
+
+const builtin *find_builtin_by_addr (builtin_func *);
+const builtin *find_builtin_by_name (const char *);
+
+/* File: path.c --- path search for include files. */
+
+void include_init (void);
+void include_env_init (void);
+void add_include_directory (const char *);
+FILE *m4_path_search (const char *, char **);
+
+/* File: eval.c --- expression evaluation. */
+
+bool evaluate (const char *, int32_t *);
+
+/* File: format.c --- printf like formatting. */
+
+void expand_format (struct obstack *, int, token_data **);
+
+/* File: freeze.c --- frozen state files. */
+
+void produce_frozen_state (const char *);
+void reload_frozen_state (const char *);
+
+/* Debugging the memory allocator. */
+
+#ifdef WITH_DMALLOC
+# define DMALLOC_FUNC_CHECK
+# include <dmalloc.h>
+#endif
+
+/* Other debug stuff. */
+
+#ifdef DEBUG
+# define DEBUG_INCL 1
+# define DEBUG_INPUT 1
+# define DEBUG_MACRO 1
+# define DEBUG_OUTPUT 1
+# define DEBUG_STKOVF 1
+# define DEBUG_SYM 1
+#endif
+
+/* Convert a possibly-signed character to an unsigned character. This is
+ a bit safer than casting to unsigned char, since it catches some type
+ errors that the cast doesn't. */
+#if HAVE_INLINE
+static inline unsigned char to_uchar (char ch) { return ch; }
+#else
+# define to_uchar(C) ((unsigned char) (C))
+#endif
+
+/* Avoid negative logic when comparing two strings. */
+#define STREQ(a, b) (strcmp (a, b) == 0)
diff --git a/src/macro.c b/src/macro.c
new file mode 100644
index 0000000..e2e2499
--- /dev/null
+++ b/src/macro.c
@@ -0,0 +1,391 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2006-2007, 2009-2011 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/>.
+*/
+
+/* This file contains the functions, that performs the basic argument
+ parsing and macro expansion. */
+
+#include "m4.h"
+
+static void expand_macro (symbol *);
+static void expand_token (struct obstack *, token_type, token_data *, int);
+
+/* Current recursion level in expand_macro (). */
+int expansion_level = 0;
+
+/* The number of the current call of expand_macro (). */
+static int macro_call_id = 0;
+
+/* The shared stack of collected arguments for macro calls; as each
+ argument is collected, it is finished and its location stored in
+ argv_stack. Normally, this stack can be used simultaneously by
+ multiple macro calls; the exception is when an outer macro has
+ generated some text, then calls a nested macro, in which case the
+ nested macro must use a local stack to leave the unfinished text
+ alone. Too bad obstack.h does not provide an easy way to reopen a
+ finished object for further growth, but in practice this does not
+ hurt us too much. */
+static struct obstack argc_stack;
+
+/* The shared stack of pointers to collected arguments for macro
+ calls. This object is never finished; we exploit the fact that
+ obstack_blank is documented to take a negative size to reduce the
+ size again. */
+static struct obstack argv_stack;
+
+/*----------------------------------------------------------------------.
+| This function read all input, and expands each token, one at a time. |
+`----------------------------------------------------------------------*/
+
+void
+expand_input (void)
+{
+ token_type t;
+ token_data td;
+ int line;
+
+ obstack_init (&argc_stack);
+ obstack_init (&argv_stack);
+
+ while ((t = next_token (&td, &line)) != TOKEN_EOF)
+ expand_token ((struct obstack *) NULL, t, &td, line);
+
+ obstack_free (&argc_stack, NULL);
+ obstack_free (&argv_stack, NULL);
+}
+
+
+/*----------------------------------------------------------------.
+| Expand one token, according to its type. Potential macro names |
+| (TOKEN_WORD) are looked up in the symbol table, to see if they |
+| have a macro definition. If they have, they are expanded as |
+| macros, otherwise the text is just copied to the output. |
+`----------------------------------------------------------------*/
+
+static void
+expand_token (struct obstack *obs, token_type t, token_data *td, int line)
+{
+ symbol *sym;
+
+ switch (t)
+ { /* TOKSW */
+ case TOKEN_EOF:
+ case TOKEN_MACDEF:
+ break;
+
+ case TOKEN_OPEN:
+ case TOKEN_COMMA:
+ case TOKEN_CLOSE:
+ case TOKEN_SIMPLE:
+ case TOKEN_STRING:
+ shipout_text (obs, TOKEN_DATA_TEXT (td), strlen (TOKEN_DATA_TEXT (td)),
+ line);
+ break;
+
+ case TOKEN_WORD:
+ sym = lookup_symbol (TOKEN_DATA_TEXT (td), SYMBOL_LOOKUP);
+ if (sym == NULL || SYMBOL_TYPE (sym) == TOKEN_VOID
+ || (SYMBOL_TYPE (sym) == TOKEN_FUNC
+ && SYMBOL_BLIND_NO_ARGS (sym)
+ && peek_token () != TOKEN_OPEN))
+ {
+#ifdef ENABLE_CHANGEWORD
+ shipout_text (obs, TOKEN_DATA_ORIG_TEXT (td),
+ strlen (TOKEN_DATA_ORIG_TEXT (td)), line);
+#else
+ shipout_text (obs, TOKEN_DATA_TEXT (td),
+ strlen (TOKEN_DATA_TEXT (td)), line);
+#endif
+ }
+ else
+ expand_macro (sym);
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad token type in expand_token ()"));
+ abort ();
+ }
+}
+
+
+/*-------------------------------------------------------------------.
+| This function parses one argument to a macro call. It expects the |
+| first left parenthesis, or the separating comma, to have been read |
+| by the caller. It skips leading whitespace, and reads and expands |
+| tokens, until it finds a comma or an right parenthesis at the same |
+| level of parentheses. It returns a flag indicating whether the |
+| argument read is the last for the active macro call. The argument |
+| is built on the obstack OBS, indirectly through expand_token (). |
+`-------------------------------------------------------------------*/
+
+static bool
+expand_argument (struct obstack *obs, token_data *argp)
+{
+ token_type t;
+ token_data td;
+ char *text;
+ int paren_level;
+ const char *file = current_file;
+ int line = current_line;
+
+ TOKEN_DATA_TYPE (argp) = TOKEN_VOID;
+
+ /* Skip leading white space. */
+ do
+ {
+ t = next_token (&td, NULL);
+ }
+ while (t == TOKEN_SIMPLE && isspace (to_uchar (*TOKEN_DATA_TEXT (&td))));
+
+ paren_level = 0;
+
+ while (1)
+ {
+
+ switch (t)
+ { /* TOKSW */
+ case TOKEN_COMMA:
+ case TOKEN_CLOSE:
+ if (paren_level == 0)
+ {
+ /* The argument MUST be finished, whether we want it or not. */
+ obstack_1grow (obs, '\0');
+ text = (char *) obstack_finish (obs);
+
+ if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID)
+ {
+ TOKEN_DATA_TYPE (argp) = TOKEN_TEXT;
+ TOKEN_DATA_TEXT (argp) = text;
+ }
+ return t == TOKEN_COMMA;
+ }
+ /* fallthru */
+ case TOKEN_OPEN:
+ case TOKEN_SIMPLE:
+ text = TOKEN_DATA_TEXT (&td);
+
+ if (*text == '(')
+ paren_level++;
+ else if (*text == ')')
+ paren_level--;
+ expand_token (obs, t, &td, line);
+ break;
+
+ case TOKEN_EOF:
+ /* current_file changed to "" if we see TOKEN_EOF, use the
+ previous value we stored earlier. */
+ M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, line,
+ "ERROR: end of file in argument list"));
+ break;
+
+ case TOKEN_WORD:
+ case TOKEN_STRING:
+ expand_token (obs, t, &td, line);
+ break;
+
+ case TOKEN_MACDEF:
+ if (obstack_object_size (obs) == 0)
+ {
+ TOKEN_DATA_TYPE (argp) = TOKEN_FUNC;
+ TOKEN_DATA_FUNC (argp) = TOKEN_DATA_FUNC (&td);
+ }
+ break;
+
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad token type in expand_argument ()"));
+ abort ();
+ }
+
+ t = next_token (&td, NULL);
+ }
+}
+
+/*-------------------------------------------------------------.
+| Collect all the arguments to a call of the macro SYM. The |
+| arguments are stored on the obstack ARGUMENTS and a table of |
+| pointers to the arguments on the obstack ARGPTR. |
+`-------------------------------------------------------------*/
+
+static void
+collect_arguments (symbol *sym, struct obstack *argptr,
+ struct obstack *arguments)
+{
+ token_data td;
+ token_data *tdp;
+ bool more_args;
+ bool groks_macro_args = SYMBOL_MACRO_ARGS (sym);
+
+ TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
+ TOKEN_DATA_TEXT (&td) = SYMBOL_NAME (sym);
+ tdp = (token_data *) obstack_copy (arguments, &td, sizeof td);
+ obstack_ptr_grow (argptr, tdp);
+
+ if (peek_token () == TOKEN_OPEN)
+ {
+ next_token (&td, NULL); /* gobble parenthesis */
+ do
+ {
+ more_args = expand_argument (arguments, &td);
+
+ if (!groks_macro_args && TOKEN_DATA_TYPE (&td) == TOKEN_FUNC)
+ {
+ TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
+ TOKEN_DATA_TEXT (&td) = (char *) "";
+ }
+ tdp = (token_data *) obstack_copy (arguments, &td, sizeof td);
+ obstack_ptr_grow (argptr, tdp);
+ }
+ while (more_args);
+ }
+}
+
+
+/*-------------------------------------------------------------------.
+| The actual call of a macro is handled by call_macro (). |
+| call_macro () is passed a symbol SYM, whose type is used to call |
+| either a builtin function, or the user macro expansion function |
+| expand_user_macro () (lives in builtin.c). There are ARGC |
+| arguments to the call, stored in the ARGV table. The expansion is |
+| left on the obstack EXPANSION. Macro tracing is also handled |
+| here. |
+`-------------------------------------------------------------------*/
+
+void
+call_macro (symbol *sym, int argc, token_data **argv,
+ struct obstack *expansion)
+{
+ switch (SYMBOL_TYPE (sym))
+ {
+ case TOKEN_FUNC:
+ (*SYMBOL_FUNC (sym)) (expansion, argc, argv);
+ break;
+
+ case TOKEN_TEXT:
+ expand_user_macro (expansion, sym, argc, argv);
+ break;
+
+ case TOKEN_VOID:
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: bad symbol type in call_macro ()"));
+ abort ();
+ }
+}
+
+/*-------------------------------------------------------------------.
+| The macro expansion is handled by expand_macro (). It parses the |
+| arguments, using collect_arguments (), and builds a table of |
+| pointers to the arguments. The arguments themselves are stored on |
+| a local obstack. Expand_macro () uses call_macro () to do the |
+| call of the macro. |
+| |
+| Expand_macro () is potentially recursive, since it calls |
+| expand_argument (), which might call expand_token (), which might |
+| call expand_macro (). |
+`-------------------------------------------------------------------*/
+
+static void
+expand_macro (symbol *sym)
+{
+ struct obstack arguments; /* Alternate obstack if argc_stack is busy. */
+ unsigned argv_base; /* Size of argv_stack on entry. */
+ bool use_argc_stack = true; /* Whether argc_stack is safe. */
+ token_data **argv;
+ int argc;
+ struct obstack *expansion;
+ const char *expanded;
+ bool traced;
+ int my_call_id;
+
+ /* Report errors at the location where the open parenthesis (if any)
+ was found, but after expansion, restore global state back to the
+ location of the close parenthesis. This is safe since we
+ guarantee that macro expansion does not alter the state of
+ current_file/current_line (dnl, include, and sinclude are special
+ cased in the input engine to ensure this fact). */
+ const char *loc_open_file = current_file;
+ int loc_open_line = current_line;
+ const char *loc_close_file;
+ int loc_close_line;
+
+ SYMBOL_PENDING_EXPANSIONS (sym)++;
+ expansion_level++;
+ if (nesting_limit > 0 && expansion_level > nesting_limit)
+ M4ERROR ((EXIT_FAILURE, 0,
+ "recursion limit of %d exceeded, use -L<N> to change it",
+ nesting_limit));
+
+ macro_call_id++;
+ my_call_id = macro_call_id;
+
+ traced = (debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym);
+
+ argv_base = obstack_object_size (&argv_stack);
+ if (obstack_object_size (&argc_stack) > 0)
+ {
+ /* We cannot use argc_stack if this is a nested invocation, and an
+ outer invocation has an unfinished argument being
+ collected. */
+ obstack_init (&arguments);
+ use_argc_stack = false;
+ }
+
+ if (traced && (debug_level & DEBUG_TRACE_CALL))
+ trace_prepre (SYMBOL_NAME (sym), my_call_id);
+
+ collect_arguments (sym, &argv_stack,
+ use_argc_stack ? &argc_stack : &arguments);
+
+ argc = ((obstack_object_size (&argv_stack) - argv_base)
+ / sizeof (token_data *));
+ argv = (token_data **) ((char *) obstack_base (&argv_stack) + argv_base);
+
+ loc_close_file = current_file;
+ loc_close_line = current_line;
+ current_file = loc_open_file;
+ current_line = loc_open_line;
+
+ if (traced)
+ trace_pre (SYMBOL_NAME (sym), my_call_id, argc, argv);
+
+ expansion = push_string_init ();
+ call_macro (sym, argc, argv, expansion);
+ expanded = push_string_finish ();
+
+ if (traced)
+ trace_post (SYMBOL_NAME (sym), my_call_id, argc, expanded);
+
+ current_file = loc_close_file;
+ current_line = loc_close_line;
+
+ --expansion_level;
+ --SYMBOL_PENDING_EXPANSIONS (sym);
+
+ if (SYMBOL_DELETED (sym))
+ free_symbol (sym);
+
+ if (use_argc_stack)
+ obstack_free (&argc_stack, argv[0]);
+ else
+ obstack_free (&arguments, NULL);
+ obstack_blank (&argv_stack, -argc * sizeof (token_data *));
+}
diff --git a/src/output.c b/src/output.c
new file mode 100644
index 0000000..91ff9f0
--- /dev/null
+++ b/src/output.c
@@ -0,0 +1,1017 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2004-2011 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/>.
+*/
+
+#include "m4.h"
+
+#include <limits.h>
+#include <sys/stat.h>
+
+#include "gl_avltree_oset.h"
+#include "gl_xoset.h"
+
+/* Size of initial in-memory buffer size for diversions. Small diversions
+ would usually fit in. */
+#define INITIAL_BUFFER_SIZE 512
+
+/* Maximum value for the total of all in-memory buffer sizes for
+ diversions. */
+#define MAXIMUM_TOTAL_SIZE (512 * 1024)
+
+/* Size of buffer size to use while copying files. */
+#define COPY_BUFFER_SIZE (32 * 512)
+
+/* Output functions. Most of the complexity is for handling cpp like
+ sync lines.
+
+ This code is fairly entangled with the code in input.c, and maybe it
+ belongs there? */
+
+typedef struct temp_dir m4_temp_dir;
+
+/* When part of diversion_table, each struct m4_diversion either
+ represents an open file (zero size, non-NULL u.file), an in-memory
+ buffer (non-zero size, non-NULL u.buffer), or an unused placeholder
+ diversion (zero size, u is NULL, non-zero used indicates that a
+ file has been created). When not part of diversion_table, u.next
+ is a pointer to the free_list chain. */
+
+typedef struct m4_diversion m4_diversion;
+
+struct m4_diversion
+ {
+ union
+ {
+ FILE *file; /* Diversion file on disk. */
+ char *buffer; /* Malloc'd diversion buffer. */
+ m4_diversion *next; /* Free-list pointer */
+ } u;
+ int divnum; /* Which diversion this represents. */
+ int size; /* Usable size before reallocation. */
+ int used; /* Used buffer length, or tmp file exists. */
+ };
+
+/* Table of diversions 1 through INT_MAX. */
+static gl_oset_t diversion_table;
+
+/* Diversion 0 (not part of diversion_table). */
+static m4_diversion div0;
+
+/* Linked list of reclaimed diversion storage. */
+static m4_diversion *free_list;
+
+/* Obstack from which diversion storage is allocated. */
+static struct obstack diversion_storage;
+
+/* Total size of all in-memory buffer sizes. */
+static int total_buffer_size;
+
+/* The number of the currently active diversion. This variable is
+ maintained for the `divnum' builtin function. */
+int current_diversion;
+
+/* Current output diversion, NULL if output is being currently
+ discarded. output_diversion->u is guaranteed non-NULL except when
+ the diversion has never been used; use size to determine if it is a
+ malloc'd buffer or a FILE. output_diversion->used is 0 if u.file
+ is stdout, and non-zero if this is a malloc'd buffer or a temporary
+ diversion file. */
+static m4_diversion *output_diversion;
+
+/* Cache of output_diversion->u.file, only valid when
+ output_diversion->size is 0. */
+static FILE *output_file;
+
+/* Cache of output_diversion->u.buffer + output_diversion->used, only
+ valid when output_diversion->size is non-zero. */
+static char *output_cursor;
+
+/* Cache of output_diversion->size - output_diversion->used, only
+ valid when output_diversion->size is non-zero. */
+static int output_unused;
+
+/* Number of input line we are generating output for. */
+int output_current_line;
+
+/* Temporary directory holding all spilled diversion files. */
+static m4_temp_dir *output_temp_dir;
+
+/* Cache of most recently used spilled diversion files. */
+static FILE *tmp_file1;
+static FILE *tmp_file2;
+
+/* Diversions that own tmp_file, or 0. */
+static int tmp_file1_owner;
+static int tmp_file2_owner;
+
+/* True if tmp_file2 is more recently used. */
+static bool tmp_file2_recent;
+
+
+/* Internal routines. */
+
+/* Callback for comparing list elements ELT1 and ELT2 for order in
+ diversion_table. */
+static int
+cmp_diversion_CB (const void *elt1, const void *elt2)
+{
+ const m4_diversion *d1 = (const m4_diversion *) elt1;
+ const m4_diversion *d2 = (const m4_diversion *) elt2;
+ /* No need to worry about overflow, since we don't create diversions
+ with negative divnum. */
+ return d1->divnum - d2->divnum;
+}
+
+/* Callback for comparing list element ELT against THRESHOLD. */
+static bool
+threshold_diversion_CB (const void *elt, const void *threshold)
+{
+ const m4_diversion *diversion = (const m4_diversion *) elt;
+ /* No need to worry about overflow, since we don't create diversions
+ with negative divnum. */
+ return diversion->divnum >= *(const int *) threshold;
+}
+
+/* Clean up any temporary directory. Designed for use as an atexit
+ handler, where it is not safe to call exit() recursively; so this
+ calls _exit if a problem is encountered. */
+static void
+cleanup_tmpfile (void)
+{
+ /* Close any open diversions. */
+ bool fail = false;
+
+ if (diversion_table)
+ {
+ const void *elt;
+ gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
+ while (gl_oset_iterator_next (&iter, &elt))
+ {
+ m4_diversion *diversion = (m4_diversion *) elt;
+ if (!diversion->size && diversion->u.file
+ && close_stream_temp (diversion->u.file) != 0)
+ {
+ M4ERROR ((0, errno,
+ "cannot clean temporary file for diversion"));
+ fail = true;
+ }
+ }
+ gl_oset_iterator_free (&iter);
+ }
+
+ /* Clean up the temporary directory. */
+ if (cleanup_temp_dir (output_temp_dir) != 0)
+ fail = true;
+ if (fail)
+ _exit (exit_failure);
+}
+
+/* Convert DIVNUM into a temporary file name for use in m4_tmp*. */
+static const char *
+m4_tmpname (int divnum)
+{
+ static char *buffer;
+ static char *tail;
+ if (buffer == NULL)
+ {
+ tail = xasprintf ("%s/m4-%d", output_temp_dir->dir_name, INT_MAX);
+ buffer = (char *) obstack_copy0 (&diversion_storage, tail,
+ strlen (tail));
+ free (tail);
+ tail = strrchr (buffer, '-') + 1;
+ }
+ assert (0 < divnum);
+ sprintf (tail, "%d", divnum);
+ return buffer;
+}
+
+/* Create a temporary file for diversion DIVNUM open for reading and
+ writing in a secure temp directory. The file will be automatically
+ closed and deleted on a fatal signal. The file can be closed and
+ reopened with m4_tmpclose and m4_tmpopen, or moved with
+ m4_tmprename; when finally done with the file, close it with
+ m4_tmpremove. Exits on failure, so the return value is always an
+ open file. */
+static FILE *
+m4_tmpfile (int divnum)
+{
+ const char *name;
+ FILE *file;
+
+ if (output_temp_dir == NULL)
+ {
+ output_temp_dir = create_temp_dir ("m4-", NULL, true);
+ if (output_temp_dir == NULL)
+ M4ERROR ((EXIT_FAILURE, errno,
+ "cannot create temporary file for diversion"));
+ atexit (cleanup_tmpfile);
+ }
+ name = m4_tmpname (divnum);
+ register_temp_file (output_temp_dir, name);
+ file = fopen_temp (name, O_BINARY ? "wb+" : "w+");
+ if (file == NULL)
+ {
+ unregister_temp_file (output_temp_dir, name);
+ M4ERROR ((EXIT_FAILURE, errno,
+ "cannot create temporary file for diversion"));
+ }
+ else if (set_cloexec_flag (fileno (file), true) != 0)
+ M4ERROR ((warning_status, errno,
+ "Warning: cannot protect diversion across forks"));
+ return file;
+}
+
+/* Reopen a temporary file for diversion DIVNUM for reading and
+ writing in a secure temp directory. If REREAD, the file is
+ positioned at offset 0, otherwise the file is positioned at the
+ end. Exits on failure, so the return value is always an open
+ file. */
+static FILE *
+m4_tmpopen (int divnum, bool reread)
+{
+ const char *name;
+ FILE *file;
+
+ if (tmp_file1_owner == divnum)
+ {
+ if (reread && fseeko (tmp_file1, 0, SEEK_SET) != 0)
+ m4_error (EXIT_FAILURE, errno,
+ _("cannot seek within diversion"));
+ tmp_file2_recent = false;
+ return tmp_file1;
+ }
+ else if (tmp_file2_owner == divnum)
+ {
+ if (reread && fseeko (tmp_file2, 0, SEEK_SET) != 0)
+ m4_error (EXIT_FAILURE, errno,
+ _("cannot seek within diversion"));
+ tmp_file2_recent = true;
+ return tmp_file2;
+ }
+ name = m4_tmpname (divnum);
+ /* We need update mode, to avoid truncation. */
+ file = fopen_temp (name, O_BINARY ? "rb+" : "r+");
+ if (file == NULL)
+ M4ERROR ((EXIT_FAILURE, errno,
+ "cannot create temporary file for diversion"));
+ else if (set_cloexec_flag (fileno (file), true) != 0)
+ m4_error (0, errno, _("cannot protect diversion across forks"));
+ /* Update mode starts at the beginning of the stream, but sometimes
+ we want the end. */
+ else if (!reread && fseeko (file, 0, SEEK_END) != 0)
+ m4_error (EXIT_FAILURE, errno,
+ _("cannot seek within diversion"));
+ return file;
+}
+
+/* Close, but don't delete, a temporary FILE for diversion DIVNUM. To
+ reduce the I/O overhead of repeatedly opening and closing the same
+ file, this implementation caches the most recent spilled diversion.
+ On the other hand, keeping every spilled diversion open would run
+ into EMFILE limits. */
+static int
+m4_tmpclose (FILE *file, int divnum)
+{
+ int result = 0;
+ if (divnum != tmp_file1_owner && divnum != tmp_file2_owner)
+ {
+ if (tmp_file2_recent)
+ {
+ if (tmp_file1_owner)
+ result = close_stream_temp (tmp_file1);
+ tmp_file1 = file;
+ tmp_file1_owner = divnum;
+ }
+ else
+ {
+ if (tmp_file2_owner)
+ result = close_stream_temp (tmp_file2);
+ tmp_file2 = file;
+ tmp_file2_owner = divnum;
+ }
+ }
+ return result;
+}
+
+/* Delete a closed temporary FILE for diversion DIVNUM. */
+static int
+m4_tmpremove (int divnum)
+{
+ if (divnum == tmp_file1_owner)
+ {
+ int result = close_stream_temp (tmp_file1);
+ if (result)
+ return result;
+ tmp_file1_owner = 0;
+ }
+ else if (divnum == tmp_file2_owner)
+ {
+ int result = close_stream_temp (tmp_file2);
+ if (result)
+ return result;
+ tmp_file2_owner = 0;
+ }
+ return cleanup_temp_file (output_temp_dir, m4_tmpname (divnum));
+}
+
+/* Transfer the temporary file for diversion OLDNUM to the previously
+ unused diversion NEWNUM. Return an open stream visiting the new
+ temporary file, positioned at the end, or exit on failure. */
+static FILE*
+m4_tmprename (int oldnum, int newnum)
+{
+ /* m4_tmpname reuses its return buffer. */
+ char *oldname = xstrdup (m4_tmpname (oldnum));
+ const char *newname = m4_tmpname (newnum);
+ register_temp_file (output_temp_dir, newname);
+ if (oldnum == tmp_file1_owner)
+ {
+ /* Be careful of mingw, which can't rename an open file. */
+ if (RENAME_OPEN_FILE_WORKS)
+ tmp_file1_owner = newnum;
+ else
+ {
+ if (close_stream_temp (tmp_file1))
+ m4_error (EXIT_FAILURE, errno,
+ _("cannot close temporary file for diversion"));
+ tmp_file1_owner = 0;
+ }
+ }
+ else if (oldnum == tmp_file2_owner)
+ {
+ /* Be careful of mingw, which can't rename an open file. */
+ if (RENAME_OPEN_FILE_WORKS)
+ tmp_file2_owner = newnum;
+ else
+ {
+ if (close_stream_temp (tmp_file2))
+ m4_error (EXIT_FAILURE, errno,
+ _("cannot close temporary file for diversion"));
+ tmp_file2_owner = 0;
+ }
+ }
+ /* Either it is safe to rename an open file, or no one should have
+ oldname open at this point. */
+ if (rename (oldname, newname))
+ m4_error (EXIT_FAILURE, errno,
+ _("cannot create temporary file for diversion"));
+ unregister_temp_file (output_temp_dir, oldname);
+ free (oldname);
+ return m4_tmpopen (newnum, false);
+}
+
+
+/*------------------------.
+| Output initialization. |
+`------------------------*/
+
+void
+output_init (void)
+{
+ diversion_table = gl_oset_create_empty (GL_AVLTREE_OSET, cmp_diversion_CB,
+ NULL);
+ div0.u.file = stdout;
+ output_diversion = &div0;
+ output_file = stdout;
+ obstack_init (&diversion_storage);
+}
+
+void
+output_exit (void)
+{
+ /* Order is important, since we may have registered cleanup_tmpfile
+ as an atexit handler, and it must not traverse stale memory. */
+ gl_oset_t table = diversion_table;
+ if (tmp_file1_owner)
+ m4_tmpremove (tmp_file1_owner);
+ if (tmp_file2_owner)
+ m4_tmpremove (tmp_file2_owner);
+ diversion_table = NULL;
+ gl_oset_free (table);
+ obstack_free (&diversion_storage, NULL);
+}
+
+/*----------------------------------------------------------------.
+| Reorganize in-memory diversion buffers so the current diversion |
+| can accomodate LENGTH more characters without further |
+| reorganization. The current diversion buffer is made bigger if |
+| possible. But to make room for a bigger buffer, one of the |
+| in-memory diversion buffers might have to be flushed to a newly |
+| created temporary file. This flushed buffer might well be the |
+| current one. |
+`----------------------------------------------------------------*/
+
+static void
+make_room_for (int length)
+{
+ int wanted_size;
+ m4_diversion *selected_diversion = NULL;
+
+ /* Compute needed size for in-memory buffer. Diversions in-memory
+ buffers start at 0 bytes, then 512, then keep doubling until it is
+ decided to flush them to disk. */
+
+ output_diversion->used = output_diversion->size - output_unused;
+
+ for (wanted_size = output_diversion->size;
+ wanted_size < output_diversion->used + length;
+ wanted_size = wanted_size == 0 ? INITIAL_BUFFER_SIZE : wanted_size * 2)
+ ;
+
+ /* Check if we are exceeding the maximum amount of buffer memory. */
+
+ if (total_buffer_size - output_diversion->size + wanted_size
+ > MAXIMUM_TOTAL_SIZE)
+ {
+ int selected_used;
+ char *selected_buffer;
+ m4_diversion *diversion;
+ int count;
+ gl_oset_iterator_t iter;
+ const void *elt;
+
+ /* Find out the buffer having most data, in view of flushing it to
+ disk. Fake the current buffer as having already received the
+ projected data, while making the selection. So, if it is
+ selected indeed, we will flush it smaller, before it grows. */
+
+ selected_diversion = output_diversion;
+ selected_used = output_diversion->used + length;
+
+ iter = gl_oset_iterator (diversion_table);
+ while (gl_oset_iterator_next (&iter, &elt))
+ {
+ diversion = (m4_diversion *) elt;
+ if (diversion->used > selected_used)
+ {
+ selected_diversion = diversion;
+ selected_used = diversion->used;
+ }
+ }
+ gl_oset_iterator_free (&iter);
+
+ /* Create a temporary file, write the in-memory buffer of the
+ diversion to this file, then release the buffer. Zero the
+ diversion before doing anything that can exit () (including
+ m4_tmpfile), so that the atexit handler doesn't try to close
+ a garbage pointer as a file. */
+
+ selected_buffer = selected_diversion->u.buffer;
+ total_buffer_size -= selected_diversion->size;
+ selected_diversion->size = 0;
+ selected_diversion->u.file = NULL;
+ selected_diversion->u.file = m4_tmpfile (selected_diversion->divnum);
+
+ if (selected_diversion->used > 0)
+ {
+ count = fwrite (selected_buffer, (size_t) selected_diversion->used,
+ 1, selected_diversion->u.file);
+ if (count != 1)
+ M4ERROR ((EXIT_FAILURE, errno,
+ "ERROR: cannot flush diversion to temporary file"));
+ }
+
+ /* Reclaim the buffer space for other diversions. */
+
+ free (selected_buffer);
+ selected_diversion->used = 1;
+ }
+
+ /* Reload output_file, just in case the flushed diversion was current. */
+
+ if (output_diversion == selected_diversion)
+ {
+ /* The flushed diversion was current indeed. */
+
+ output_file = output_diversion->u.file;
+ output_cursor = NULL;
+ output_unused = 0;
+ }
+ else
+ {
+ /* Close any selected file since it is not the current diversion. */
+ if (selected_diversion)
+ {
+ FILE *file = selected_diversion->u.file;
+ selected_diversion->u.file = NULL;
+ if (m4_tmpclose (file, selected_diversion->divnum) != 0)
+ m4_error (0, errno,
+ _("cannot close temporary file for diversion"));
+ }
+
+ /* The current buffer may be safely reallocated. */
+ {
+ char *buffer = output_diversion->u.buffer;
+ output_diversion->u.buffer = xcharalloc ((size_t) wanted_size);
+ memcpy (output_diversion->u.buffer, buffer, output_diversion->used);
+ free (buffer);
+ }
+
+ total_buffer_size += wanted_size - output_diversion->size;
+ output_diversion->size = wanted_size;
+
+ output_cursor = output_diversion->u.buffer + output_diversion->used;
+ output_unused = wanted_size - output_diversion->used;
+ }
+}
+
+/*--------------------------------------------------------------.
+| Output one character CHAR, when it is known that it goes to a |
+| diversion file or an in-memory diversion buffer. |
+`--------------------------------------------------------------*/
+
+#define OUTPUT_CHARACTER(Char) \
+ if (output_file) \
+ putc ((Char), output_file); \
+ else if (output_unused == 0) \
+ output_character_helper ((Char)); \
+ else \
+ (output_unused--, *output_cursor++ = (Char))
+
+static void
+output_character_helper (int character)
+{
+ make_room_for (1);
+
+ if (output_file)
+ putc (character, output_file);
+ else
+ {
+ *output_cursor++ = character;
+ output_unused--;
+ }
+}
+
+/*-------------------------------------------------------------------.
+| Output one TEXT having LENGTH characters, when it is known that it |
+| goes to a diversion file or an in-memory diversion buffer. |
+`-------------------------------------------------------------------*/
+
+void
+output_text (const char *text, int length)
+{
+ int count;
+
+ if (!output_diversion || !length)
+ return;
+
+ if (!output_file && length > output_unused)
+ make_room_for (length);
+
+ if (output_file)
+ {
+ count = fwrite (text, length, 1, output_file);
+ if (count != 1)
+ M4ERROR ((EXIT_FAILURE, errno, "ERROR: copying inserted file"));
+ }
+ else
+ {
+ memcpy (output_cursor, text, (size_t) length);
+ output_cursor += length;
+ output_unused -= length;
+ }
+}
+
+/*--------------------------------------------------------------------.
+| Add some text into an obstack OBS, taken from TEXT, having LENGTH |
+| characters. If OBS is NULL, output the text to an external file |
+| or an in-memory diversion buffer instead. If OBS is NULL, and |
+| there is no output file, the text is discarded. LINE is the line |
+| where the token starts (not necessarily current_line, in the case |
+| of multiline tokens). |
+| |
+| If we are generating sync lines, the output has to be examined, |
+| because we need to know how much output each input line generates. |
+| In general, sync lines are output whenever a single input lines |
+| generates several output lines, or when several input lines do not |
+| generate any output. |
+`--------------------------------------------------------------------*/
+
+void
+shipout_text (struct obstack *obs, const char *text, int length, int line)
+{
+ static bool start_of_output_line = true;
+ const char *cursor;
+
+ /* If output goes to an obstack, merely add TEXT to it. */
+
+ if (obs != NULL)
+ {
+ obstack_grow (obs, text, length);
+ return;
+ }
+
+ /* Do nothing if TEXT should be discarded. */
+
+ if (output_diversion == NULL)
+ return;
+
+ /* Output TEXT to a file, or in-memory diversion buffer. */
+
+ if (!sync_output)
+ switch (length)
+ {
+
+ /* In-line short texts. */
+
+ case 8: OUTPUT_CHARACTER (*text); text++;
+ case 7: OUTPUT_CHARACTER (*text); text++;
+ case 6: OUTPUT_CHARACTER (*text); text++;
+ case 5: OUTPUT_CHARACTER (*text); text++;
+ case 4: OUTPUT_CHARACTER (*text); text++;
+ case 3: OUTPUT_CHARACTER (*text); text++;
+ case 2: OUTPUT_CHARACTER (*text); text++;
+ case 1: OUTPUT_CHARACTER (*text);
+ case 0:
+ return;
+
+ /* Optimize longer texts. */
+
+ default:
+ output_text (text, length);
+ }
+ else
+ {
+ /* Check for syncline only at the start of a token. Multiline
+ tokens, and tokens that are out of sync but in the middle of
+ the line, must wait until the next raw newline triggers a
+ syncline. */
+ if (start_of_output_line)
+ {
+ start_of_output_line = false;
+ output_current_line++;
+#ifdef DEBUG_OUTPUT
+ xfprintf (stderr, "DEBUG: line %d, cur %d, cur out %d\n",
+ line, current_line, output_current_line);
+#endif
+
+ /* Output a `#line NUM' synchronization directive if needed.
+ If output_current_line was previously given a negative
+ value (invalidated), output `#line NUM "FILE"' instead. */
+
+ if (output_current_line != line)
+ {
+ OUTPUT_CHARACTER ('#');
+ OUTPUT_CHARACTER ('l');
+ OUTPUT_CHARACTER ('i');
+ OUTPUT_CHARACTER ('n');
+ OUTPUT_CHARACTER ('e');
+ OUTPUT_CHARACTER (' ');
+ for (cursor = ntoa (line, 10); *cursor; cursor++)
+ OUTPUT_CHARACTER (*cursor);
+ if (output_current_line < 1 && current_file[0] != '\0')
+ {
+ OUTPUT_CHARACTER (' ');
+ OUTPUT_CHARACTER ('"');
+ for (cursor = current_file; *cursor; cursor++)
+ OUTPUT_CHARACTER (*cursor);
+ OUTPUT_CHARACTER ('"');
+ }
+ OUTPUT_CHARACTER ('\n');
+ output_current_line = line;
+ }
+ }
+
+ /* Output the token, and track embedded newlines. */
+ for (; length-- > 0; text++)
+ {
+ if (start_of_output_line)
+ {
+ start_of_output_line = false;
+ output_current_line++;
+#ifdef DEBUG_OUTPUT
+ xfprintf (stderr, "DEBUG: line %d, cur %d, cur out %d\n",
+ line, current_line, output_current_line);
+#endif
+ }
+ OUTPUT_CHARACTER (*text);
+ if (*text == '\n')
+ start_of_output_line = true;
+ }
+ }
+}
+
+/* Functions for use by diversions. */
+
+/*------------------------------------------------------------------.
+| Make a file for diversion DIVNUM, and install it in the diversion |
+| table. Grow the size of the diversion table as needed. |
+`------------------------------------------------------------------*/
+
+/* The number of possible diversions is limited only by memory and
+ available file descriptors (each overflowing diversion uses one). */
+
+void
+make_diversion (int divnum)
+{
+ m4_diversion *diversion = NULL;
+
+ if (current_diversion == divnum)
+ return;
+
+ if (output_diversion)
+ {
+ if (!output_diversion->size && !output_diversion->u.file)
+ {
+ assert (!output_diversion->used);
+ if (!gl_oset_remove (diversion_table, output_diversion))
+ assert (false);
+ output_diversion->u.next = free_list;
+ free_list = output_diversion;
+ }
+ else if (output_diversion->size)
+ output_diversion->used = output_diversion->size - output_unused;
+ else if (output_diversion->used)
+ {
+ FILE *file = output_diversion->u.file;
+ output_diversion->u.file = NULL;
+ if (m4_tmpclose (file, output_diversion->divnum) != 0)
+ m4_error (0, errno,
+ _("cannot close temporary file for diversion"));
+ }
+ output_diversion = NULL;
+ output_file = NULL;
+ output_cursor = NULL;
+ output_unused = 0;
+ }
+
+ current_diversion = divnum;
+
+ if (divnum < 0)
+ return;
+
+ if (divnum == 0)
+ diversion = &div0;
+ else
+ {
+ const void *elt;
+ if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
+ &divnum, &elt))
+ {
+ m4_diversion *temp = (m4_diversion *) elt;
+ if (temp->divnum == divnum)
+ diversion = temp;
+ }
+ }
+ if (diversion == NULL)
+ {
+ /* First time visiting this diversion. */
+ if (free_list)
+ {
+ diversion = free_list;
+ free_list = diversion->u.next;
+ }
+ else
+ {
+ diversion = (m4_diversion *) obstack_alloc (&diversion_storage,
+ sizeof *diversion);
+ diversion->size = 0;
+ diversion->used = 0;
+ }
+ diversion->u.file = NULL;
+ diversion->divnum = divnum;
+ gl_oset_add (diversion_table, diversion);
+ }
+
+ output_diversion = diversion;
+ if (output_diversion->size)
+ {
+ output_cursor = output_diversion->u.buffer + output_diversion->used;
+ output_unused = output_diversion->size - output_diversion->used;
+ }
+ else
+ {
+ if (!output_diversion->u.file && output_diversion->used)
+ output_diversion->u.file = m4_tmpopen (output_diversion->divnum,
+ false);
+ output_file = output_diversion->u.file;
+ }
+ output_current_line = -1;
+}
+
+/*-------------------------------------------------------------------.
+| Insert a FILE into the current output file, in the same manner |
+| diversions are handled. This allows files to be included, without |
+| having them rescanned by m4. |
+`-------------------------------------------------------------------*/
+
+void
+insert_file (FILE *file)
+{
+ static char buffer[COPY_BUFFER_SIZE];
+ size_t length;
+
+ /* Optimize out inserting into a sink. */
+ if (!output_diversion)
+ return;
+
+ /* Insert output by big chunks. */
+ while (1)
+ {
+ length = fread (buffer, 1, sizeof buffer, file);
+ if (ferror (file))
+ M4ERROR ((EXIT_FAILURE, errno, "error reading inserted file"));
+ if (length == 0)
+ break;
+ output_text (buffer, length);
+ }
+}
+
+/*-------------------------------------------------------------------.
+| Insert DIVERSION (but not div0) into the current output file. The |
+| diversion is NOT placed on the expansion obstack, because it must |
+| not be rescanned. When the file is closed, it is deleted by the |
+| system. |
+`-------------------------------------------------------------------*/
+
+static void
+insert_diversion_helper (m4_diversion *diversion)
+{
+ /* Effectively undivert only if an output stream is active. */
+ if (output_diversion)
+ {
+ if (diversion->size)
+ {
+ if (!output_diversion->u.file)
+ {
+ /* Transferring diversion metadata is faster than
+ copying contents. */
+ assert (!output_diversion->used && output_diversion != &div0
+ && !output_file);
+ output_diversion->u.buffer = diversion->u.buffer;
+ output_diversion->size = diversion->size;
+ output_cursor = diversion->u.buffer + diversion->used;
+ output_unused = diversion->size - diversion->used;
+ diversion->u.buffer = NULL;
+ }
+ else
+ {
+ /* Avoid double-charging the total in-memory size when
+ transferring from one in-memory diversion to
+ another. */
+ total_buffer_size -= diversion->size;
+ output_text (diversion->u.buffer, diversion->used);
+ }
+ }
+ else if (!output_diversion->u.file)
+ {
+ /* Transferring diversion metadata is faster than copying
+ contents. */
+ assert (!output_diversion->used && output_diversion != &div0
+ && !output_file);
+ output_diversion->u.file = m4_tmprename (diversion->divnum,
+ output_diversion->divnum);
+ output_diversion->used = 1;
+ output_file = output_diversion->u.file;
+ diversion->u.file = NULL;
+ diversion->size = 1;
+ }
+ else
+ {
+ if (!diversion->u.file)
+ diversion->u.file = m4_tmpopen (diversion->divnum, true);
+ insert_file (diversion->u.file);
+ }
+
+ output_current_line = -1;
+ }
+
+ /* Return all space used by the diversion. */
+ if (diversion->size)
+ {
+ if (!output_diversion)
+ total_buffer_size -= diversion->size;
+ free (diversion->u.buffer);
+ diversion->size = 0;
+ }
+ else
+ {
+ if (diversion->u.file)
+ {
+ FILE *file = diversion->u.file;
+ diversion->u.file = NULL;
+ if (m4_tmpclose (file, diversion->divnum) != 0)
+ m4_error (0, errno,
+ _("cannot clean temporary file for diversion"));
+ }
+ if (m4_tmpremove (diversion->divnum) != 0)
+ M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
+ }
+ diversion->used = 0;
+ gl_oset_remove (diversion_table, diversion);
+ diversion->u.next = free_list;
+ free_list = diversion;
+}
+
+/*------------------------------------------------------------------.
+| Insert diversion number DIVNUM into the current output file. The |
+| diversion is NOT placed on the expansion obstack, because it must |
+| not be rescanned. When the file is closed, it is deleted by the |
+| system. |
+`------------------------------------------------------------------*/
+
+void
+insert_diversion (int divnum)
+{
+ const void *elt;
+
+ /* Do not care about nonexistent diversions, and undiverting stdout
+ or self is a no-op. */
+ if (divnum <= 0 || current_diversion == divnum)
+ return;
+ if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
+ &divnum, &elt))
+ {
+ m4_diversion *diversion = (m4_diversion *) elt;
+ if (diversion->divnum == divnum)
+ insert_diversion_helper (diversion);
+ }
+}
+
+/*----------------------------------------------------------------.
+| Get back all diversions. This is done just before exiting from |
+| main, and from m4_undivert (), if called without arguments. |
+`----------------------------------------------------------------*/
+
+void
+undivert_all (void)
+{
+ const void *elt;
+ gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
+ while (gl_oset_iterator_next (&iter, &elt))
+ {
+ m4_diversion *diversion = (m4_diversion *) elt;
+ if (diversion->divnum != current_diversion)
+ insert_diversion_helper (diversion);
+ }
+ gl_oset_iterator_free (&iter);
+}
+
+/*-------------------------------------------------------------.
+| Produce all diversion information in frozen format on FILE. |
+`-------------------------------------------------------------*/
+
+void
+freeze_diversions (FILE *file)
+{
+ int saved_number;
+ int last_inserted;
+ gl_oset_iterator_t iter;
+ const void *elt;
+
+ saved_number = current_diversion;
+ last_inserted = 0;
+ make_diversion (0);
+ output_file = file; /* kludge in the frozen file */
+
+ iter = gl_oset_iterator (diversion_table);
+ while (gl_oset_iterator_next (&iter, &elt))
+ {
+ m4_diversion *diversion = (m4_diversion *) elt;
+ if (diversion->size || diversion->used)
+ {
+ if (diversion->size)
+ xfprintf (file, "D%d,%d\n", diversion->divnum, diversion->used);
+ else
+ {
+ struct stat file_stat;
+ diversion->u.file = m4_tmpopen (diversion->divnum, true);
+ if (fstat (fileno (diversion->u.file), &file_stat) < 0)
+ M4ERROR ((EXIT_FAILURE, errno, "cannot stat diversion"));
+ if (file_stat.st_size < 0
+ || (file_stat.st_size + 0UL
+ != (unsigned long int) file_stat.st_size))
+ M4ERROR ((EXIT_FAILURE, 0, "diversion too large"));
+ xfprintf (file, "D%d,%lu\n", diversion->divnum,
+ (unsigned long int) file_stat.st_size);
+ }
+
+ insert_diversion_helper (diversion);
+ putc ('\n', file);
+
+ last_inserted = diversion->divnum;
+ }
+ }
+ gl_oset_iterator_free (&iter);
+
+ /* Save the active diversion number, if not already. */
+
+ if (saved_number != last_inserted)
+ xfprintf (file, "D%d,0\n\n", saved_number);
+}
diff --git a/src/path.c b/src/path.c
new file mode 100644
index 0000000..0e3c6e5
--- /dev/null
+++ b/src/path.c
@@ -0,0 +1,205 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1993, 2004, 2006-2011 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/>.
+*/
+
+/* Handling of path search of included files via the builtins "include"
+ and "sinclude". */
+
+#include "m4.h"
+
+struct includes
+{
+ struct includes *next; /* next directory to search */
+ const char *dir; /* directory */
+ int len;
+};
+
+typedef struct includes includes;
+
+static includes *dir_list; /* the list of path directories */
+static includes *dir_list_end; /* the end of same */
+static int dir_max_length; /* length of longest directory name */
+
+
+void
+include_init (void)
+{
+ dir_list = NULL;
+ dir_list_end = NULL;
+ dir_max_length = 0;
+}
+
+void
+include_env_init (void)
+{
+ char *path;
+ char *path_end;
+ char *env_path;
+
+ if (no_gnu_extensions)
+ return;
+
+ env_path = getenv ("M4PATH");
+ if (env_path == NULL)
+ return;
+
+ env_path = xstrdup (env_path);
+ path = env_path;
+
+ do
+ {
+ path_end = strchr (path, ':');
+ if (path_end)
+ *path_end = '\0';
+ add_include_directory (path);
+ path = path_end + 1;
+ }
+ while (path_end);
+ free (env_path);
+}
+
+void
+add_include_directory (const char *dir)
+{
+ includes *incl;
+
+ if (no_gnu_extensions)
+ return;
+
+ if (*dir == '\0')
+ dir = ".";
+
+ incl = (includes *) xmalloc (sizeof (struct includes));
+ incl->next = NULL;
+ incl->len = strlen (dir);
+ incl->dir = xstrdup (dir);
+
+ if (incl->len > dir_max_length) /* remember len of longest directory */
+ dir_max_length = incl->len;
+
+ if (dir_list_end == NULL)
+ dir_list = incl;
+ else
+ dir_list_end->next = incl;
+ dir_list_end = incl;
+
+#ifdef DEBUG_INCL
+ xfprintf (stderr, "add_include_directory (%s);\n", dir);
+#endif
+}
+
+/* Attempt to open FILE; if it opens, verify that it is not a
+ directory, and ensure it does not leak across execs. */
+static FILE *
+m4_fopen (const char *file)
+{
+ FILE *fp = fopen (file, "r");
+ if (fp)
+ {
+ struct stat st;
+ int fd = fileno (fp);
+ if (fstat (fd, &st) == 0 && S_ISDIR (st.st_mode))
+ {
+ fclose (fp);
+ errno = EISDIR;
+ return NULL;
+ }
+ if (set_cloexec_flag (fd, true) != 0)
+ M4ERROR ((warning_status, errno,
+ "Warning: cannot protect input file across forks"));
+ }
+ return fp;
+}
+
+/* Search for FILE, first in `.', then according to -I options. If
+ successful, return the open file, and if RESULT is not NULL, set
+ *RESULT to a malloc'd string that represents the file found with
+ respect to the current working directory. */
+
+FILE *
+m4_path_search (const char *file, char **result)
+{
+ FILE *fp;
+ includes *incl;
+ char *name; /* buffer for constructed name */
+ int e;
+
+ if (result)
+ *result = NULL;
+
+ /* Reject empty file. */
+ if (!*file)
+ {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ /* Look in current working directory first. */
+ fp = m4_fopen (file);
+ if (fp != NULL)
+ {
+ if (result)
+ *result = xstrdup (file);
+ return fp;
+ }
+
+ /* If file not found, and filename absolute, fail. */
+ if (IS_ABSOLUTE_FILE_NAME (file) || no_gnu_extensions)
+ return NULL;
+ e = errno;
+
+ for (incl = dir_list; incl != NULL; incl = incl->next)
+ {
+ name = file_name_concat (incl->dir, file, NULL);
+
+#ifdef DEBUG_INCL
+ xfprintf (stderr, "m4_path_search (%s) -- trying %s\n", file, name);
+#endif
+
+ fp = m4_fopen (name);
+ if (fp != NULL)
+ {
+ if (debug_level & DEBUG_TRACE_PATH)
+ DEBUG_MESSAGE2 ("path search for `%s' found `%s'", file, name);
+ if (result)
+ *result = name;
+ else
+ free (name);
+ return fp;
+ }
+ free (name);
+ }
+ errno = e;
+ return fp;
+}
+
+#ifdef DEBUG_INCL
+
+static void M4_GNUC_UNUSED
+include_dump (void)
+{
+ includes *incl;
+
+ xfprintf (stderr, "include_dump:\n");
+ for (incl = dir_list; incl != NULL; incl = incl->next)
+ xfprintf (stderr, "\t%s\n", incl->dir);
+}
+
+#endif /* DEBUG_INCL */
diff --git a/src/symtab.c b/src/symtab.c
new file mode 100644
index 0000000..4965027
--- /dev/null
+++ b/src/symtab.c
@@ -0,0 +1,401 @@
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2003, 2006-2011 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/>.
+*/
+
+/* This file handles all the low level work around the symbol table. The
+ symbol table is a simple chained hash table. Each symbol is described
+ by a struct symbol, which is placed in the hash table based upon the
+ symbol name. Symbols that hash to the same entry in the table are
+ kept on a list, sorted by name. As a special case, to facilitate the
+ "pushdef" and "popdef" builtins, a symbol can be several times in the
+ symbol table, one for each definition. Since the name is the same,
+ all the entries for the symbol will be on the same list, and will
+ also, because the list is sorted, be adjacent. All the entries for a
+ name are simply ordered on the list by age. The current definition
+ will then always be the first found. */
+
+#include "m4.h"
+#include <limits.h>
+
+#ifdef DEBUG_SYM
+/* When evaluating hash table performance, this profiling code shows
+ how many collisions were encountered. */
+
+struct profile
+{
+ int entry; /* Number of times lookup_symbol called with this mode. */
+ int comparisons; /* Number of times strcmp was called. */
+ int misses; /* Number of times strcmp did not return 0. */
+ long long bytes; /* Number of bytes compared. */
+};
+
+static struct profile profiles[5];
+static symbol_lookup current_mode;
+
+/* On exit, show a profile of symbol table performance. */
+static void
+show_profile (void)
+{
+ int i;
+ for (i = 0; i < 5; i++)
+ {
+ xfprintf(stderr, "m4: lookup mode %d called %d times, %d compares, "
+ "%d misses, %lld bytes\n",
+ i, profiles[i].entry, profiles[i].comparisons,
+ profiles[i].misses, profiles[i].bytes);
+ }
+}
+
+/* Like strcmp (S1, S2), but also track profiling statistics. */
+static int
+profile_strcmp (const char *s1, const char *s2)
+{
+ int i = 1;
+ int result;
+ while (*s1 && *s1 == *s2)
+ {
+ s1++;
+ s2++;
+ i++;
+ }
+ result = (unsigned char) *s1 - (unsigned char) *s2;
+ profiles[current_mode].comparisons++;
+ if (result != 0)
+ profiles[current_mode].misses++;
+ profiles[current_mode].bytes += i;
+ return result;
+}
+
+# define strcmp profile_strcmp
+#endif /* DEBUG_SYM */
+
+
+/*------------------------------------------------------------------.
+| Initialise the symbol table, by allocating the necessary storage, |
+| and zeroing all the entries. |
+`------------------------------------------------------------------*/
+
+/* Pointer to symbol table. */
+symbol **symtab;
+
+void
+symtab_init (void)
+{
+ size_t i;
+ symbol **s;
+
+ s = symtab = (symbol **) xnmalloc (hash_table_size, sizeof (symbol *));
+
+ for (i = 0; i < hash_table_size; i++)
+ s[i] = NULL;
+
+#ifdef DEBUG_SYM
+ {
+ int e = atexit(show_profile);
+ if (e != 0)
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: unable to show symtab profile"));
+ }
+#endif /* DEBUG_SYM */
+}
+
+/*--------------------------------------------------.
+| Return a hashvalue for a string, from GNU-emacs. |
+`--------------------------------------------------*/
+
+static size_t
+hash (const char *s)
+{
+ register size_t val = 0;
+
+ register const char *ptr = s;
+ register char ch;
+
+ while ((ch = *ptr++) != '\0')
+ val = (val << 7) + (val >> (sizeof (val) * CHAR_BIT - 7)) + ch;
+ return val;
+}
+
+/*--------------------------------------------.
+| Free all storage associated with a symbol. |
+`--------------------------------------------*/
+
+void
+free_symbol (symbol *sym)
+{
+ if (SYMBOL_PENDING_EXPANSIONS (sym) > 0)
+ SYMBOL_DELETED (sym) = true;
+ else
+ {
+ free (SYMBOL_NAME (sym));
+ if (SYMBOL_TYPE (sym) == TOKEN_TEXT)
+ free (SYMBOL_TEXT (sym));
+ free (sym);
+ }
+}
+
+/*-------------------------------------------------------------------.
+| Search in, and manipulation of the symbol table, are all done by |
+| lookup_symbol (). It basically hashes NAME to a list in the |
+| symbol table, and searches this list for the first occurrence of a |
+| symbol with the name. |
+| |
+| The MODE parameter determines what lookup_symbol () will do. It |
+| can either just do a lookup, do a lookup and insert if not |
+| present, do an insertion even if the name is already in the list, |
+| delete the first occurrence of the name on the list, or delete all |
+| occurrences of the name on the list. |
+`-------------------------------------------------------------------*/
+
+symbol *
+lookup_symbol (const char *name, symbol_lookup mode)
+{
+ size_t h;
+ int cmp = 1;
+ symbol *sym, *prev;
+ symbol **spp;
+
+#if DEBUG_SYM
+ current_mode = mode;
+ profiles[mode].entry++;
+#endif /* DEBUG_SYM */
+
+ h = hash (name);
+ sym = symtab[h % hash_table_size];
+
+ for (prev = NULL; sym != NULL; prev = sym, sym = sym->next)
+ {
+ cmp = strcmp (SYMBOL_NAME (sym), name);
+ if (cmp >= 0)
+ break;
+ }
+
+ /* If just searching, return status of search. */
+
+ if (mode == SYMBOL_LOOKUP)
+ return cmp == 0 ? sym : NULL;
+
+ /* Symbol not found. */
+
+ spp = (prev != NULL) ? &prev->next : &symtab[h % hash_table_size];
+
+ switch (mode)
+ {
+
+ case SYMBOL_INSERT:
+
+ /* If the name was found in the table, check whether it is still in
+ use by a pending expansion. If so, replace the table element with
+ a new one; if not, just return the symbol. If not found, just
+ insert the name, and return the new symbol. */
+
+ if (cmp == 0 && sym != NULL)
+ {
+ if (SYMBOL_PENDING_EXPANSIONS (sym) > 0)
+ {
+ symbol *old = sym;
+ SYMBOL_DELETED (old) = true;
+
+ sym = (symbol *) xmalloc (sizeof (symbol));
+ SYMBOL_TYPE (sym) = TOKEN_VOID;
+ SYMBOL_TRACED (sym) = SYMBOL_TRACED (old);
+ SYMBOL_NAME (sym) = xstrdup (name);
+ SYMBOL_SHADOWED (sym) = false;
+ SYMBOL_MACRO_ARGS (sym) = false;
+ SYMBOL_BLIND_NO_ARGS (sym) = false;
+ SYMBOL_DELETED (sym) = false;
+ SYMBOL_PENDING_EXPANSIONS (sym) = 0;
+
+ SYMBOL_NEXT (sym) = SYMBOL_NEXT (old);
+ SYMBOL_NEXT (old) = NULL;
+ (*spp) = sym;
+ }
+ return sym;
+ }
+ /* Fall through. */
+
+ case SYMBOL_PUSHDEF:
+
+ /* Insert a name in the symbol table. If there is already a symbol
+ with the name, insert this in front of it, and mark the old
+ symbol as "shadowed". */
+
+ sym = (symbol *) xmalloc (sizeof (symbol));
+ SYMBOL_TYPE (sym) = TOKEN_VOID;
+ SYMBOL_TRACED (sym) = false;
+ SYMBOL_NAME (sym) = xstrdup (name);
+ SYMBOL_SHADOWED (sym) = false;
+ SYMBOL_MACRO_ARGS (sym) = false;
+ SYMBOL_BLIND_NO_ARGS (sym) = false;
+ SYMBOL_DELETED (sym) = false;
+ SYMBOL_PENDING_EXPANSIONS (sym) = 0;
+
+ SYMBOL_NEXT (sym) = *spp;
+ (*spp) = sym;
+
+ if (mode == SYMBOL_PUSHDEF && cmp == 0)
+ {
+ SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) = true;
+ SYMBOL_TRACED (sym) = SYMBOL_TRACED (SYMBOL_NEXT (sym));
+ }
+ return sym;
+
+ case SYMBOL_DELETE:
+ case SYMBOL_POPDEF:
+
+ /* Delete occurrences of symbols with NAME. SYMBOL_DELETE kills
+ all definitions, SYMBOL_POPDEF kills only the first.
+ However, if the last instance of a symbol is marked for
+ tracing, reinsert a placeholder in the table. And if the
+ definition is still in use, let the caller free the memory
+ after it is done with the symbol. */
+
+ if (cmp != 0 || sym == NULL)
+ return NULL;
+ {
+ bool traced = false;
+ if (SYMBOL_NEXT (sym) != NULL
+ && SYMBOL_SHADOWED (SYMBOL_NEXT (sym))
+ && mode == SYMBOL_POPDEF)
+ {
+ SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) = false;
+ SYMBOL_TRACED (SYMBOL_NEXT (sym)) = SYMBOL_TRACED (sym);
+ }
+ else
+ traced = SYMBOL_TRACED (sym);
+ do
+ {
+ *spp = SYMBOL_NEXT (sym);
+ free_symbol (sym);
+ sym = *spp;
+ }
+ while (*spp != NULL && SYMBOL_SHADOWED (*spp)
+ && mode == SYMBOL_DELETE);
+ if (traced)
+ {
+ sym = (symbol *) xmalloc (sizeof (symbol));
+ SYMBOL_TYPE (sym) = TOKEN_VOID;
+ SYMBOL_TRACED (sym) = true;
+ SYMBOL_NAME (sym) = xstrdup (name);
+ SYMBOL_SHADOWED (sym) = false;
+ SYMBOL_MACRO_ARGS (sym) = false;
+ SYMBOL_BLIND_NO_ARGS (sym) = false;
+ SYMBOL_DELETED (sym) = false;
+ SYMBOL_PENDING_EXPANSIONS (sym) = 0;
+
+ SYMBOL_NEXT (sym) = *spp;
+ (*spp) = sym;
+ }
+ }
+ return NULL;
+
+ case SYMBOL_LOOKUP:
+ default:
+ M4ERROR ((warning_status, 0,
+ "INTERNAL ERROR: invalid mode to symbol_lookup ()"));
+ abort ();
+ }
+}
+
+/*-----------------------------------------------------------------.
+| The following function is used for the cases where we want to do |
+| something to each and every symbol in the table. The function |
+| hack_all_symbols () traverses the symbol table, and calls a |
+| specified function FUNC for each symbol in the table. FUNC is |
+| called with a pointer to the symbol, and the DATA argument. |
+| |
+| FUNC may safely call lookup_symbol with mode SYMBOL_POPDEF or |
+| SYMBOL_LOOKUP, but any other mode can break the iteration. |
+`-----------------------------------------------------------------*/
+
+void
+hack_all_symbols (hack_symbol *func, void *data)
+{
+ size_t h;
+ symbol *sym;
+ symbol *next;
+
+ for (h = 0; h < hash_table_size; h++)
+ {
+ /* We allow func to call SYMBOL_POPDEF, which can invalidate
+ sym, so we must grab the next element to traverse before
+ calling func. */
+ for (sym = symtab[h]; sym != NULL; sym = next)
+ {
+ next = SYMBOL_NEXT (sym);
+ func (sym, data);
+ }
+ }
+}
+
+#ifdef DEBUG_SYM
+
+static void symtab_print_list (int i);
+
+static void M4_GNUC_UNUSED
+symtab_debug (void)
+{
+ token_data td;
+ const char *text;
+ symbol *s;
+ int delete;
+ static int i;
+
+ while (next_token (&td, NULL) == TOKEN_WORD)
+ {
+ text = TOKEN_DATA_TEXT (&td);
+ if (*text == '_')
+ {
+ delete = 1;
+ text++;
+ }
+ else
+ delete = 0;
+
+ s = lookup_symbol (text, SYMBOL_LOOKUP);
+
+ if (s == NULL)
+ xprintf ("Name `%s' is unknown\n", text);
+
+ lookup_symbol (text, delete ? SYMBOL_DELETE : SYMBOL_INSERT);
+ }
+ symtab_print_list (i++);
+}
+
+static void
+symtab_print_list (int i)
+{
+ symbol *sym;
+ size_t h;
+
+ xprintf ("Symbol dump #%d:\n", i);
+ for (h = 0; h < hash_table_size; h++)
+ for (sym = symtab[h]; sym != NULL; sym = sym->next)
+ xprintf ("\tname %s, bucket %lu, addr %p, next %p, "
+ "flags%s%s%s, pending %d\n",
+ SYMBOL_NAME (sym),
+ (unsigned long int) h, sym, SYMBOL_NEXT (sym),
+ SYMBOL_TRACED (sym) ? " traced" : "",
+ SYMBOL_SHADOWED (sym) ? " shadowed" : "",
+ SYMBOL_DELETED (sym) ? " deleted" : "",
+ SYMBOL_PENDING_EXPANSIONS (sym));
+}
+
+#endif /* DEBUG_SYM */