aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/bison/m4/src/builtin.c
diff options
context:
space:
mode:
authoraxc <axc@yandex-team.ru>2022-02-10 16:47:35 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:47:35 +0300
commit1f5217043ad70f25dc35e75b3bd261a1e23d045e (patch)
tree11bf68c1fa5272d3d3446cbd5a0ff96ed9d75788 /contrib/tools/bison/m4/src/builtin.c
parent69505a07cbb096113e85aa02e7d136cac4aa826c (diff)
downloadydb-1f5217043ad70f25dc35e75b3bd261a1e23d045e.tar.gz
Restoring authorship annotation for <axc@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/tools/bison/m4/src/builtin.c')
-rw-r--r--contrib/tools/bison/m4/src/builtin.c4514
1 files changed, 2257 insertions, 2257 deletions
diff --git a/contrib/tools/bison/m4/src/builtin.c b/contrib/tools/bison/m4/src/builtin.c
index 01ede38017..3b867da712 100644
--- a/contrib/tools/bison/m4/src/builtin.c
+++ b/contrib/tools/bison/m4/src/builtin.c
@@ -1,2258 +1,2258 @@
-/* GNU m4 -- A simple macro processor
-
- Copyright (C) 1989-1994, 2000, 2004, 2006-2013 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 "progname.h"
+/* GNU m4 -- A simple macro processor
+
+ Copyright (C) 1989-1994, 2000, 2004, 2006-2013 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 "progname.h"
#include <contrib/tools/bison/gnulib/src/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 * M4_GNUC_PURE
-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 * M4_GNUC_PURE
-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);
- 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;
- }
- }
-}
+#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 * M4_GNUC_PURE
+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 * M4_GNUC_PURE
+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);
+ 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;
+ }
+ }
+}