diff options
Diffstat (limited to 'modules/m4.c')
-rw-r--r-- | modules/m4.c | 1324 |
1 files changed, 0 insertions, 1324 deletions
diff --git a/modules/m4.c b/modules/m4.c deleted file mode 100644 index ed06942b..00000000 --- a/modules/m4.c +++ /dev/null @@ -1,1324 +0,0 @@ -/* GNU m4 -- A simple macro processor - Copyright (C) 2000, 2002-2004, 2006-2010, 2013-2014, 2017 Free - Software Foundation, Inc. - - This file is part of GNU M4. - - GNU M4 is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - GNU M4 is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <config.h> - -/* Build using only the exported interfaces, unless NDEBUG is set, in - which case use private symbols to speed things up as much as possible. */ -#ifndef NDEBUG -# include <m4/m4module.h> -#else -# include "m4private.h" -#endif - -#include "execute.h" -#include "memchr2.h" -#include "memcmp2.h" -#include "quotearg.h" -#include "stdlib--.h" -#include "tempname.h" -#include "unistd--.h" - -#include <modules/m4.h> - -extern void m4_set_sysval (int); -extern void m4_sysval_flush (m4 *, bool); -extern void m4_dump_symbols (m4 *, m4_dump_symbol_data *, size_t, - m4_macro_args *, bool); -extern const char *m4_expand_ranges (const char *, size_t *, m4_obstack *); -extern void m4_make_temp (m4 *, m4_obstack *, const m4_call_info *, - const char *, size_t, bool); - -/* Maintain each of the builtins implemented in this modules along - with their details in a single table for easy maintenance. - - function macros blind side minargs maxargs */ -#define builtin_functions \ - BUILTIN (changecom, false, false, false, 0, 2 ) \ - BUILTIN (changequote, false, false, false, 0, 2 ) \ - BUILTIN (decr, false, true, true, 1, 1 ) \ - BUILTIN (define, true, true, false, 1, 2 ) \ - BUILTIN (defn, true, true, false, 1, -1 ) \ - BUILTIN (divert, false, false, false, 0, 2 ) \ - BUILTIN (divnum, false, false, false, 0, 0 ) \ - BUILTIN (dnl, false, false, false, 0, 0 ) \ - BUILTIN (dumpdef, true, false, false, 0, -1 ) \ - BUILTIN (errprint, false, true, false, 1, -1 ) \ - BUILTIN (eval, false, true, true, 1, 3 ) \ - BUILTIN (ifdef, true, true, false, 2, 3 ) \ - BUILTIN (ifelse, true, true, false, 1, -1 ) \ - BUILTIN (include, false, true, false, 1, 1 ) \ - BUILTIN (incr, false, true, true, 1, 1 ) \ - BUILTIN (index, false, true, true, 2, 3 ) \ - BUILTIN (len, false, true, true, 1, 1 ) \ - BUILTIN (m4exit, false, false, false, 0, 1 ) \ - BUILTIN (m4wrap, true, true, false, 1, -1 ) \ - BUILTIN (maketemp, false, true, false, 1, 1 ) \ - BUILTIN (mkstemp, false, true, false, 1, 1 ) \ - BUILTIN (popdef, true, true, false, 1, -1 ) \ - BUILTIN (pushdef, true, true, false, 1, 2 ) \ - BUILTIN (shift, true, true, false, 1, -1 ) \ - BUILTIN (sinclude, false, true, false, 1, 1 ) \ - BUILTIN (substr, false, true, true, 2, 4 ) \ - BUILTIN (syscmd, false, true, true, 1, 1 ) \ - BUILTIN (sysval, false, false, false, 0, 0 ) \ - BUILTIN (traceoff, true, false, false, 0, -1 ) \ - BUILTIN (traceon, true, false, false, 0, -1 ) \ - BUILTIN (translit, false, true, true, 2, 3 ) \ - BUILTIN (undefine, true, true, false, 1, -1 ) \ - BUILTIN (undivert, false, false, false, 0, -1 ) \ - - -typedef intmax_t number; -typedef uintmax_t unumber; - -static void include (m4 *context, m4_obstack *obs, size_t argc, - m4_macro_args *argv, bool silent); -static int dumpdef_cmp_CB (const void *s1, const void *s2); -static void * dump_symbol_CB (m4_symbol_table *, const char *, size_t, - m4_symbol *symbol, void *userdata); -static const char *ntoa (number value, int radix); -static void numb_obstack (m4_obstack *obs, number value, - int radix, int min); - - -/* Generate prototypes for each builtin handler function. */ -#define BUILTIN(handler, macros, blind, side, min, max) M4BUILTIN (handler) - builtin_functions -#undef BUILTIN - - -/* Generate a table for mapping m4 symbol names to handler functions. */ -static const m4_builtin m4_builtin_table[] = -{ -#define BUILTIN(handler, macros, blind, side, min, max) \ - M4BUILTIN_ENTRY (handler, #handler, macros, blind, side, min, max) - - builtin_functions -#undef BUILTIN - - { NULL, NULL, 0, 0, 0 }, -}; - - -void -include_m4 (m4 *context, m4_module *module, m4_obstack *obs) -{ - m4_install_builtins (context, module, m4_builtin_table); -} - - - -/* 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 builtin_MACRONAME (m4_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. */ - -M4BUILTIN_HANDLER (define) -{ - const m4_call_info *me = m4_arg_info (argv); - - if (m4_is_arg_text (argv, 1)) - { - m4_symbol_value *value = m4_symbol_value_create (); - - if (m4_symbol_value_copy (context, value, m4_arg_symbol (argv, 2))) - m4_warn (context, 0, me, _("cannot concatenate builtins")); - m4_symbol_define (M4SYMTAB, M4ARG (1), M4ARGLEN (1), value); - } - else - m4_warn (context, 0, me, _("invalid macro name ignored")); -} - -M4BUILTIN_HANDLER (undefine) -{ - size_t i; - for (i = 1; i < argc; i++) - if (m4_symbol_value_lookup (context, argv, i, true)) - m4_symbol_delete (M4SYMTAB, M4ARG (i), M4ARGLEN (i)); -} - -M4BUILTIN_HANDLER (pushdef) -{ - const m4_call_info *me = m4_arg_info (argv); - - if (m4_is_arg_text (argv, 1)) - { - m4_symbol_value *value = m4_symbol_value_create (); - - if (m4_symbol_value_copy (context, value, m4_arg_symbol (argv, 2))) - m4_warn (context, 0, me, _("cannot concatenate builtins")); - m4_symbol_pushdef (M4SYMTAB, M4ARG (1), M4ARGLEN (1), value); - } - else - m4_warn (context, 0, me, _("invalid macro name ignored")); -} - -M4BUILTIN_HANDLER (popdef) -{ - size_t i; - for (i = 1; i < argc; i++) - if (m4_symbol_value_lookup (context, argv, i, true)) - m4_symbol_popdef (M4SYMTAB, M4ARG (i), M4ARGLEN (i)); -} - - - -/* --- CONDITIONALS OF M4 --- */ - - -M4BUILTIN_HANDLER (ifdef) -{ - m4_push_arg (context, obs, argv, - m4_symbol_value_lookup (context, argv, 1, false) ? 2 : 3); -} - -M4BUILTIN_HANDLER (ifelse) -{ - const m4_call_info *me = m4_arg_info (argv); - size_t i; - - /* The valid ranges of argc for ifelse is discontinuous, we cannot - rely on the regular mechanisms. */ - if (argc == 2 || m4_bad_argc (context, argc, me, 3, -1, false)) - return; - else if (argc % 3 == 0) - /* Diagnose excess arguments if 5, 8, 11, etc., actual arguments. */ - m4_bad_argc (context, argc, me, 0, argc - 2, false); - - i = 1; - argc--; - - while (true) - { - if (m4_arg_equal (context, argv, i, i + 1)) - { - m4_push_arg (context, obs, argv, i + 2); - return; - } - switch (argc) - { - case 3: - return; - - case 4: - case 5: - m4_push_arg (context, obs, argv, i + 3); - return; - - default: - argc -= 3; - i += 3; - } - } -} - - -/* qsort comparison routine, for sorting the table made in m4_dumpdef (). */ -static int -dumpdef_cmp_CB (const void *s1, const void *s2) -{ - const m4_string *a = (const m4_string *) s1; - const m4_string *b = (const m4_string *) s2; - return memcmp2 (a->str, a->len, b->str, b->len); -} - -/* The function m4_dump_symbols () is for use by "dumpdef". It builds up a - table of all defined symbol names. */ -static void * -dump_symbol_CB (m4_symbol_table *ignored M4_GNUC_UNUSED, const char *name, - size_t len, m4_symbol *symbol, void *userdata) -{ - m4_dump_symbol_data *symbol_data = (m4_dump_symbol_data *) userdata; - m4_string *key; - - assert (name); - assert (symbol); - assert (!m4_is_symbol_value_void (m4_get_symbol_value (symbol))); - - if (symbol_data->size == 0) - { - char *base; - size_t offset = obstack_object_size (symbol_data->obs); - obstack_blank (symbol_data->obs, sizeof *symbol_data->base); - symbol_data->size = (obstack_room (symbol_data->obs) - / sizeof *symbol_data->base); - base = (char *) obstack_base (symbol_data->obs) + offset; - symbol_data->base = (m4_string *) base; - } - else - { - obstack_blank_fast (symbol_data->obs, sizeof *symbol_data->base); - symbol_data->size--; - } - - /* Safe to cast away const, since m4_dump_symbols adds it back. */ - key = (m4_string *) symbol_data->base++; - key->str = (char *) name; - key->len = len; - return NULL; -} - -/* If there are no arguments, build a sorted list of all defined - symbols, otherwise, only the specified symbols. */ -void -m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, size_t argc, - m4_macro_args *argv, bool complain) -{ - assert (obstack_object_size (data->obs) == 0); - data->size = obstack_room (data->obs) / sizeof *data->base; - data->base = (m4_string *) obstack_base (data->obs); - - if (argc == 1) - m4_symtab_apply (M4SYMTAB, false, dump_symbol_CB, data); - else - { - size_t i; - m4_symbol *symbol; - - for (i = 1; i < argc; i++) - { - symbol = m4_symbol_value_lookup (context, argv, i, complain); - if (symbol) - dump_symbol_CB (NULL, M4ARG (i), M4ARGLEN (i), symbol, data); - } - } - - data->size = obstack_object_size (data->obs) / sizeof *data->base; - data->base = (m4_string *) obstack_finish (data->obs); - /* Safe to cast away const, since we don't modify entries. */ - qsort ((m4_string *) data->base, data->size, sizeof *data->base, - dumpdef_cmp_CB); -} - - -/* Implementation of "dumpdef" itself. It builds up a table of pointers to - symbols, sorts it and prints the sorted table. */ -M4BUILTIN_HANDLER (dumpdef) -{ - m4_dump_symbol_data data; - const m4_string_pair *quotes = NULL; - bool stack = m4_is_debug_bit (context, M4_DEBUG_TRACE_STACK); - size_t arg_length = m4_get_max_debug_arg_length_opt (context); - bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE); - FILE *output = (m4_is_debug_bit (context, M4_DEBUG_TRACE_OUTPUT_DUMPDEF) - ? stderr : m4_get_debug_file (context)); - - if (!output) - return; - if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE)) - quotes = m4_get_syntax_quotes (M4SYNTAX); - data.obs = m4_arg_scratch (context); - m4_dump_symbols (context, &data, argc, argv, true); - m4_sysval_flush (context, false); - - for (; data.size > 0; --data.size, data.base++) - { - m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, data.base->str, - data.base->len); - char *value; - size_t len; - assert (symbol); - - /* TODO - add debugmode(b) option to control quoting style. */ - obstack_grow (obs, data.base->str, data.base->len); - obstack_1grow (obs, ':'); - obstack_1grow (obs, '\t'); - m4_symbol_print (context, symbol, obs, quotes, stack, arg_length, - module); - obstack_1grow (obs, '\n'); - len = obstack_object_size (obs); - value = (char *) obstack_finish (obs); - fwrite (value, 1, len, output); - obstack_free (obs, value); - } -} - -/* 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. */ -M4BUILTIN_HANDLER (defn) -{ - const m4_call_info *me = m4_arg_info (argv); - size_t i; - - for (i = 1; i < argc; i++) - { - m4_symbol *symbol = m4_symbol_value_lookup (context, argv, i, true); - - if (!symbol) - ; - else if (m4_is_symbol_text (symbol)) - m4_shipout_string (context, obs, m4_get_symbol_text (symbol), - m4_get_symbol_len (symbol), true); - else if (m4_is_symbol_func (symbol)) - m4_push_builtin (context, obs, m4_get_symbol_value (symbol)); - else if (m4_is_symbol_placeholder (symbol)) - m4_warn (context, 0, me, - _("%s: builtin %s requested by frozen file not found"), - quotearg_n_mem (2, M4ARG (i), M4ARGLEN (i)), - quotearg_style (locale_quoting_style, - m4_get_symbol_placeholder (symbol))); - else - { - assert (!"Bad token data type in m4_defn"); - abort (); - } - } -} - - -/* This section contains macros to handle the builtins "syscmd" - and "sysval". */ - -/* Exit code from last "syscmd" command. */ -/* FIXME - we should preserve this value across freezing. See - http://lists.gnu.org/archive/html/bug-m4/2006-06/msg00059.html - for ideas on how do to that. */ -static int m4_sysval = 0; - -void -m4_set_sysval (int value) -{ - m4_sysval = value; -} - -/* Flush a given output STREAM. If REPORT, also print an error - message and clear the stream error bit. */ -static void -sysval_flush_helper (m4 *context, FILE *stream, bool report) -{ - if (fflush (stream) == EOF && report) - { - m4_error (context, 0, errno, NULL, _("write error")); - clearerr (stream); - } -} - -/* Flush all user output streams, prior to doing something that can - could lose unflushed data or interleave debug and normal output - incorrectly. If REPORT, then print an error message on failure and - clear the stream error bit; otherwise a subsequent ferror can track - that an error occurred. */ -void -m4_sysval_flush (m4 *context, bool report) -{ - FILE *debug_file = m4_get_debug_file (context); - - if (debug_file != stdout) - sysval_flush_helper (context, stdout, report); - if (debug_file != stderr) - /* If we have problems with stderr, we can't really report that - problem to stderr. The closeout module will ensure the exit - status reflects the problem, though. */ - fflush (stderr); - if (debug_file != NULL) - sysval_flush_helper (context, debug_file, report); - /* POSIX requires that if m4 doesn't consume all input, but stdin is - opened on a seekable file, that the file pointer be left at the - next character on exit (but places no restrictions on the file - pointer location on a non-seekable file). It also requires that - fflush() followed by fseeko() on an input file set the underlying - file pointer, and gnulib guarantees these semantics. However, - fflush() on a non-seekable file can lose buffered data, which we - might otherwise want to process after syscmd. Hence, we must - check whether stdin is seekable. We must also be tolerant of - operating with stdin closed, so we don't report any failures in - this attempt. The stdio-safer module and friends are essential, - so that if stdin was closed, this lseek is not on some other file - that we have since opened. */ - if (lseek (STDIN_FILENO, 0, SEEK_CUR) >= 0 - && fflush (stdin) == 0) - { - fseeko (stdin, 0, SEEK_CUR); - } -} - -M4BUILTIN_HANDLER (syscmd) -{ - const m4_call_info *me = m4_arg_info (argv); - const char *cmd = M4ARG (1); - size_t len = M4ARGLEN (1); - int status; - int sig_status; - const char *prog_args[4] = { "sh", "-c" }; - - if (m4_get_safer_opt (context)) - { - m4_error (context, 0, 0, m4_arg_info (argv), _("disabled by --safer")); - return; - } - if (strlen (cmd) != len) - m4_warn (context, 0, me, _("argument %s truncated"), - quotearg_style_mem (locale_quoting_style, cmd, len)); - - /* Optimize the empty command. */ - if (!*cmd) - { - m4_set_sysval (0); - return; - } - m4_sysval_flush (context, false); -#if W32_NATIVE - if (strstr (M4_SYSCMD_SHELL, "cmd")) - { - prog_args[0] = "cmd"; - prog_args[1] = "/c"; - } -#endif - prog_args[2] = cmd; - errno = 0; - status = execute (m4_info_name (me), M4_SYSCMD_SHELL, (char **) prog_args, - false, false, false, false, true, false, &sig_status); - if (sig_status) - { - assert (status == 127); - m4_sysval = sig_status << 8; - } - else - { - if (status == 127 && errno) - m4_warn (context, errno, me, _("cannot run command %s"), - quotearg_style (locale_quoting_style, cmd)); - m4_sysval = status; - } -} - - -M4BUILTIN_HANDLER (sysval) -{ - m4_shipout_int (obs, m4_sysval); -} - - -M4BUILTIN_HANDLER (incr) -{ - int value; - - if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1), - &value)) - return; - - m4_shipout_int (obs, value + 1); -} - -M4BUILTIN_HANDLER (decr) -{ - int value; - - if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1), - &value)) - return; - - m4_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. */ -M4BUILTIN_HANDLER (divert) -{ - int i = 0; - - if (argc >= 2 && !m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), - M4ARGLEN (1), &i)) - return; - m4_make_diversion (context, i); - m4_divert_text (context, NULL, M4ARG (2), M4ARGLEN (2), - m4_get_current_line (context)); -} - -/* Expand to the current diversion number. */ -M4BUILTIN_HANDLER (divnum) -{ - m4_shipout_int (obs, m4_get_current_diversion (context)); -} - -/* Bring back the diversion given by the argument list. If none is - specified, bring back all diversions. GNU specific is the option - of undiverting the named file, by passing a non-numeric argument to - undivert (). */ - -M4BUILTIN_HANDLER (undivert) -{ - size_t i = 0; - const m4_call_info *me = m4_arg_info (argv); - - if (argc == 1) - m4_undivert_all (context); - else - for (i = 1; i < argc; i++) - { - const char *str = M4ARG (i); - size_t len = M4ARGLEN (i); - char *endp; - int diversion = strtol (str, &endp, 10); - if (endp - str == len && !isspace ((unsigned char) *str)) - m4_insert_diversion (context, diversion); - else if (m4_get_posixly_correct_opt (context)) - m4_warn (context, 0, me, _("non-numeric argument %s"), - quotearg_style_mem (locale_quoting_style, str, len)); - else if (strlen (str) != len) - m4_warn (context, 0, me, _("invalid file name %s"), - quotearg_style_mem (locale_quoting_style, str, len)); - else - { - char *filepath = m4_path_search (context, str, NULL); - FILE *fp = m4_fopen (context, filepath, "r"); - - free (filepath); - if (fp != NULL) - { - m4_insert_file (context, fp); - if (fclose (fp) == EOF) - m4_error (context, 0, errno, me, _("error undiverting %s"), - quotearg_style (locale_quoting_style, str)); - } - else - m4_error (context, 0, errno, me, _("cannot undivert %s"), - quotearg_style (locale_quoting_style, str)); - } - } -} - - -/* This section contains various macros, which does not fall into - any specific group. These are "dnl", "shift", "changequote", - "changecom" and "changesyntax" */ - -/* Delete all subsequent whitespace from input. The function skip_line () - lives in input.c. */ -M4BUILTIN_HANDLER (dnl) -{ - m4_skip_line (context, m4_arg_info (argv)); -} - -/* Shift all arguments one to the left, discarding the first argument. - Each output argument is quoted with the current quotes. */ -M4BUILTIN_HANDLER (shift) -{ - m4_push_args (context, obs, argv, true, true); -} - -/* Change the current quotes. The function set_quotes () lives in - syntax.c. */ -M4BUILTIN_HANDLER (changequote) -{ - m4_set_quotes (M4SYNTAX, - (argc >= 2) ? M4ARG (1) : NULL, M4ARGLEN (1), - (argc >= 3) ? M4ARG (2) : NULL, M4ARGLEN (2)); -} - -/* Change the current comment delimiters. The function set_comment () - lives in syntax.c. */ -M4BUILTIN_HANDLER (changecom) -{ - m4_set_comment (M4SYNTAX, - (argc >= 2) ? M4ARG (1) : NULL, M4ARGLEN (1), - (argc >= 3) ? M4ARG (2) : NULL, M4ARGLEN (2)); -} - - -/* 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. */ - -static void -include (m4 *context, m4_obstack *obs, size_t argc, m4_macro_args *argv, bool silent) -{ - const m4_call_info *me = m4_arg_info (argv); - const char *arg = M4ARG (1); - size_t len = M4ARGLEN (1); - - if (strlen (arg) != len) - m4_warn (context, 0, me, _("argument %s truncated"), - quotearg_style_mem (locale_quoting_style, arg, len)); - m4_load_filename (context, me, arg, obs, silent); -} - -/* Include a file, complaining in case of errors. */ -M4BUILTIN_HANDLER (include) -{ - include (context, obs, argc, argv, false); -} - -/* Include a file, ignoring errors. */ -M4BUILTIN_HANDLER (sinclude) -{ - include (context, obs, argc, argv, true); -} - - -/* More miscellaneous builtins -- "maketemp", "errprint". */ - -/* Add trailing `X' to PATTERN of length LEN as necessary, then - securely create the temporary file system object. If DIR, create a - directory instead of a file. Report errors on behalf of CALLER. If - successful, output the quoted resulting name on OBS. */ -void -m4_make_temp (m4 *context, m4_obstack *obs, const m4_call_info *caller, - const char *pattern, size_t len, bool dir) -{ - int fd; - int i; - char *name; - const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX); - - if (m4_get_safer_opt (context)) - { - m4_error (context, 0, 0, caller, _("disabled by --safer")); - return; - } - - /* Guarantee that there are six trailing 'X' characters, even if the - user forgot to supply them. Output must be quoted if - successful. */ - assert (obstack_object_size (obs) == 0); - obstack_grow (obs, quotes->str1, quotes->len1); - if (strlen (pattern) < len) - { - m4_warn (context, 0, caller, _("argument %s truncated"), - quotearg_style_mem (locale_quoting_style, pattern, len)); - len = strlen (pattern); - } - obstack_grow (obs, pattern, len); - for (i = 0; len > 0 && i < 6; i++) - if (pattern[--len] != 'X') - break; - obstack_grow0 (obs, "XXXXXX", 6 - i); - name = (char *) obstack_base (obs) + quotes->len1; - - /* Make the temporary object. */ - errno = 0; - fd = gen_tempname (name, 0, 0, dir ? GT_DIR : GT_FILE); - if (fd < 0) - { - /* This use of _() will need to change if xgettext ever changes - its undocumented behavior of parsing both string options. */ - - m4_warn (context, errno, caller, - _(dir ? "cannot create directory from template %s" - : "cannot create file from template %s"), - quotearg_style (locale_quoting_style, pattern)); - obstack_free (obs, obstack_finish (obs)); - } - else - { - if (!dir) - close (fd); - /* Remove NUL, then finish quote. */ - obstack_blank_fast (obs, -1); - obstack_grow (obs, quotes->str2, quotes->len2); - } -} - -/* Use the first argument as at template for a temporary file name. */ -M4BUILTIN_HANDLER (maketemp) -{ - const m4_call_info *me = m4_arg_info (argv); - m4_warn (context, 0, me, _("recommend using mkstemp instead")); - if (m4_get_posixly_correct_opt (context)) - { - /* 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. - - 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 = M4ARG (1); - size_t len = M4ARGLEN (1); - size_t i; - m4_obstack *scratch = m4_arg_scratch (context); - size_t pid_len = obstack_printf (scratch, "%lu", - (unsigned long) getpid ()); - char *pid = (char *) obstack_copy0 (scratch, "", 0); - - for (i = len; i > 1; i--) - if (str[i - 1] != 'X') - break; - obstack_grow (obs, str, i); - if (len - i < pid_len) - obstack_grow (obs, pid + pid_len - (len - i), len - i); - else - obstack_printf (obs, "%.*d%s", (int) (len - i - pid_len), 0, pid); - } - else - m4_make_temp (context, obs, me, M4ARG (1), M4ARGLEN (1), false); -} - -/* Use the first argument as a template for a temporary file name. */ -M4BUILTIN_HANDLER (mkstemp) -{ - m4_make_temp (context, obs, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1), - false); -} - -/* Print all arguments on standard error. */ -M4BUILTIN_HANDLER (errprint) -{ - size_t i; - - m4_sysval_flush (context, false); - /* The close_stdin module makes it safe to skip checking the return - values here. */ - fwrite (M4ARG (1), 1, M4ARGLEN (1), stderr); - for (i = 2; i < m4_arg_argc (argv); i++) - { - fputc (' ', stderr); - fwrite (M4ARG (i), 1, M4ARGLEN (i), stderr); - } - fflush (stderr); -} - - -/* 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 exitcode specified by the first argument, 0 if no - arguments are present. */ -M4BUILTIN_HANDLER (m4exit) -{ - const m4_call_info *me = m4_arg_info (argv); - int exit_code = EXIT_SUCCESS; - - /* Warn on bad arguments, but still exit. */ - if (argc >= 2 && !m4_numeric_arg (context, me, M4ARG (1), M4ARGLEN (1), - &exit_code)) - exit_code = EXIT_FAILURE; - if (exit_code < 0 || exit_code > 255) - { - m4_warn (context, 0, me, _("exit status out of range: `%d'"), exit_code); - exit_code = EXIT_FAILURE; - } - - /* Ensure that atexit handlers see correct nonzero status. */ - if (exit_code != EXIT_SUCCESS) - m4_set_exit_failure (exit_code); - - /* Change debug stream back to stderr, to force flushing debug - stream and detect any errors. */ - m4_debug_set_output (context, me, NULL); - m4_sysval_flush (context, true); - - /* Check for saved error. */ - if (exit_code == 0 && m4_get_exit_status (context) != 0) - exit_code = m4_get_exit_status (context); - 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. */ -M4BUILTIN_HANDLER (m4wrap) -{ - m4_wrap_args (context, argv); -} - -/* 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. */ - -M4BUILTIN_HANDLER (traceon) -{ - const m4_call_info *me = m4_arg_info (argv); - size_t i; - - if (argc == 1) - m4_set_debug_level_opt (context, (m4_get_debug_level_opt (context) - | M4_DEBUG_TRACE_ALL)); - else - for (i = 1; i < argc; i++) - if (m4_is_arg_text (argv, i)) - m4_set_symbol_name_traced (M4SYMTAB, M4ARG (i), M4ARGLEN (i), true); - else - m4_warn (context, 0, me, _("invalid macro name ignored")); -} - -/* Disable tracing of all specified macros, or all, if none is specified. */ -M4BUILTIN_HANDLER (traceoff) -{ - const m4_call_info *me = m4_arg_info (argv); - size_t i; - - if (argc == 1) - m4_set_debug_level_opt (context, (m4_get_debug_level_opt (context) - & ~M4_DEBUG_TRACE_ALL)); - else - for (i = 1; i < argc; i++) - if (m4_is_arg_text (argv, i)) - m4_set_symbol_name_traced (M4SYMTAB, M4ARG (i), M4ARGLEN (i), false); - else - m4_warn (context, 0, me, _("invalid macro name ignored")); -} - - -/* 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. */ -M4BUILTIN_HANDLER (len) -{ - m4_shipout_int (obs, M4ARGLEN (1)); -} - -/* The macro expands to the first index of the second argument in the - first argument. As an extension, start the search at the index - indicated by the third argument. */ -M4BUILTIN_HANDLER (index) -{ - const char *haystack = M4ARG (1); - size_t haystack_len = M4ARGLEN (1); - const char *needle = M4ARG (2); - const char *result = NULL; - int offset = 0; - int retval = -1; - - if (!m4_arg_empty (argv, 3) && !m4_numeric_arg (context, m4_arg_info (argv), - M4ARG (3), M4ARGLEN (3), - &offset)) - return; - if (offset < 0) - { - offset += haystack_len; - if (offset < 0) - offset = 0; - } - else if (haystack_len < offset) - { - m4_shipout_int (obs, -1); - return; - } - - /* Rely on the optimizations guaranteed by gnulib's memmem - module. */ - result = (char *) memmem (haystack + offset, haystack_len - offset, needle, - M4ARGLEN (2)); - if (result) - retval = result - haystack; - - m4_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 or empty, the substring extends to the end of the first - argument. As an extension, negative arguments are treated as - indices relative to the string length. Also, if a fourth argument - is supplied, the original string is output with the selected - substring replaced by the argument. */ -M4BUILTIN_HANDLER (substr) -{ - const m4_call_info *me = m4_arg_info (argv); - const char *str = M4ARG (1); - int start = 0; - int end; - int length; - - if (argc <= 2) - { - m4_push_arg (context, obs, argv, 1); - return; - } - - length = M4ARGLEN (1); - if (!m4_arg_empty (argv, 2) - && !m4_numeric_arg (context, me, M4ARG (2), M4ARGLEN (2), &start)) - return; - if (start < 0) - start += length; - - if (m4_arg_empty (argv, 3)) - end = length; - else - { - if (!m4_numeric_arg (context, me, M4ARG (3), M4ARGLEN (3), &end)) - return; - if (end < 0) - end += length; - else - end += start; - } - - if (5 <= argc) - { - /* Replacement text provided. */ - if (end < start) - end = start; - if (end < 0 || length < start) - { - m4_warn (context, 0, me, _("substring out of range")); - return; - } - if (start < 0) - start = 0; - if (length < end) - end = length; - obstack_grow (obs, str, start); - m4_push_arg (context, obs, argv, 4); - obstack_grow (obs, str + end, length - end); - return; - } - - if (start < 0) - start = 0; - if (length < end) - end = length; - if (end <= start) - return; - - obstack_grow (obs, str + start, end - start); -} - - -/* Any ranges in string S of length *LEN are expanded, using OBS for - scratch space, and the expansion returned. *LEN is set to the - expanded length. 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. */ -const char * -m4_expand_ranges (const char *s, size_t *len, m4_obstack *obs) -{ - unsigned char from; - unsigned char to; - const char *end = s + *len; - - assert (obstack_object_size (obs) == 0); - assert (s != end); - from = *s++; - obstack_1grow (obs, from); - - for ( ; s != end; from = *s++) - { - if (*s == '-') - { - if (++s == end) - { - /* trailing dash */ - obstack_1grow (obs, '-'); - break; - } - to = *s; - if (from <= to) - { - while (from++ < to) - obstack_1grow (obs, from); - } - else - { - while (--from >= to) - obstack_1grow (obs, from); - } - } - else - obstack_1grow (obs, *s); - } - *len = obstack_object_size (obs); - 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. */ -M4BUILTIN_HANDLER (translit) -{ - const char *data; - const char *from; - const char *to; - size_t from_len; - size_t to_len; - char map[UCHAR_MAX + 1]; - char found[UCHAR_MAX + 1]; - unsigned char ch; - - enum { ASIS, REPLACE, DELETE }; - - if (m4_arg_empty (argv, 1) || m4_arg_empty (argv, 2)) - { - m4_push_arg (context, obs, argv, 1); - return; - } - - from = M4ARG (2); - from_len = M4ARGLEN (2); - - to = M4ARG (3); - to_len = M4ARGLEN (3); - if (memchr (to, '-', to_len) != NULL) - to = m4_expand_ranges (to, &to_len, m4_arg_scratch (context)); - - /* 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_len <= 2) - { - const char *p; - size_t len = M4ARGLEN (1); - int second = from[from_len / 2]; - data = M4ARG (1); - while ((p = (char *) memchr2 (data, from[0], second, len))) - { - obstack_grow (obs, data, p - data); - len -= p - data + 1; - data = p + 1; - if (*p == from[0] && to_len) - obstack_1grow (obs, to[0]); - else if (*p == second && 1 < to_len) - obstack_1grow (obs, to[1]); - } - obstack_grow (obs, data, len); - return; - } - - if (memchr (from, '-', from_len) != NULL) - from = m4_expand_ranges (from, &from_len, m4_arg_scratch (context)); - - /* Calling memchr(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); - while (from_len--) - { - ch = *from++; - if (found[ch] == ASIS) - { - if (to_len) - { - found[ch] = REPLACE; - map[ch] = *to; - } - else - found[ch] = DELETE; - } - if (to_len) - { - to++; - to_len--; - } - } - - data = M4ARG (1); - from_len = M4ARGLEN (1); - while (from_len--) - { - ch = *data++; - switch (found[ch]) - { - case ASIS: - obstack_1grow (obs, ch); - break; - case REPLACE: - obstack_1grow (obs, map[ch]); - break; - case DELETE: - break; - default: - assert (!"translit"); - abort (); - } - } -} - - - -/* The rest of this file contains the functions to evaluate integer - * expressions for the "eval" macro. `number' should be at least 32 bits. - */ -#define numb_set(ans, x) ((ans) = (x)) -#define numb_set_si(ans, si) (*(ans) = (number) (si)) - -#define numb_ZERO ((number) 0) -#define numb_ONE ((number) 1) - -#define numb_init(x) ((x) = numb_ZERO) -#define numb_fini(x) - -#define numb_incr(n) ((n) += numb_ONE) -#define numb_decr(n) ((n) -= numb_ONE) - -#define numb_zerop(x) ((x) == numb_ZERO) -#define numb_positivep(x) ((x) > numb_ZERO) -#define numb_negativep(x) ((x) < numb_ZERO) - -#define numb_eq(x, y) ((x) = ((x) == (y))) -#define numb_ne(x, y) ((x) = ((x) != (y))) -#define numb_lt(x, y) ((x) = ((x) < (y))) -#define numb_le(x, y) ((x) = ((x) <= (y))) -#define numb_gt(x, y) ((x) = ((x) > (y))) -#define numb_ge(x, y) ((x) = ((x) >= (y))) - -#define numb_lnot(x) ((x) = (!(x))) -#define numb_lior(x, y) ((x) = ((x) || (y))) -#define numb_land(x, y) ((x) = ((x) && (y))) - -#define numb_not(c, x) (*(x) = ~ *(x)) -#define numb_eor(c, x, y) (*(x) = *(x) ^ *(y)) -#define numb_ior(c, x, y) (*(x) = *(x) | *(y)) -#define numb_and(c, x, y) (*(x) = *(x) & *(y)) - -#define numb_plus(x, y) ((x) = ((x) + (y))) -#define numb_minus(x, y) ((x) = ((x) - (y))) -#define numb_negate(x) ((x) = (- (x))) - -#define numb_times(x, y) ((x) = ((x) * (y))) -/* Be careful of x86 SIGFPE. */ -#define numb_ratio(x, y) \ - (((y) == -1) ? (numb_negate (x)) : ((x) /= (y))) -#define numb_divide(x, y) \ - ((*(y) == -1) ? (numb_negate (*(y))) : (*(x) /= *(y))) -#define numb_modulo(c, x, y) \ - ((*(y) == -1) ? (*(x) = numb_ZERO) : (*(x) %= *(y))) -/* numb_invert is only used in the context of x**-y, which integral math - does not support. */ -#define numb_invert(x) return NEGATIVE_EXPONENT - -/* Minimize undefined C behavior (shifting by a negative number, - shifting by the width or greater, left shift overflow, or right - shift of a negative number). Implement Java wrap-around semantics, - with implicit masking of shift amount. This code assumes that the - implementation-defined overflow when casting unsigned to signed is - a silent twos-complement wrap-around. */ -#define shift_mask (sizeof (number) * CHAR_BIT - 1) -#define numb_lshift(c, x, y) \ - (*(x) = (number) ((unumber) *(x) << (*(y) & shift_mask))) -#define numb_rshift(c, x, y) \ - (*(x) = (number) (*(x) < 0 \ - ? ~(~(unumber) *(x) >> (*(y) & shift_mask)) \ - : (unumber) *(x) >> (*(y) & shift_mask))) -#define numb_urshift(c, x, y) \ - (*(x) = (number) ((unumber) *(x) >> (*(y) & shift_mask))) - - -/* The function ntoa () converts VALUE to a signed ASCII representation in - radix RADIX. Radix must be between 2 and 36, inclusive. */ -static const char * -ntoa (number value, int radix) -{ - /* Digits for number to ASCII conversions. */ - static char const ntoa_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - - bool negative; - unumber uvalue; - /* Sized for radix 2, plus sign and trailing NUL. */ - static char str[sizeof value * CHAR_BIT + 2]; - char *s = &str[sizeof str]; - - *--s = '\0'; - - if (value < 0) - { - negative = true; - uvalue = (unumber) -value; - } - else - { - negative = false; - uvalue = (unumber) value; - } - - do - { - *--s = ntoa_digits[uvalue % radix]; - uvalue /= radix; - } - while (uvalue > 0); - - if (negative) - *--s = '-'; - return s; -} - -static void -numb_obstack (m4_obstack *obs, number value, int radix, int min) -{ - const char *s; - size_t len; - unumber uvalue; - - if (radix == 1) - { - if (value < 0) - { - obstack_1grow (obs, '-'); - uvalue = -value; - } - else - uvalue = value; - if (uvalue < min) - { - obstack_blank (obs, min - uvalue); - memset ((char *) obstack_next_free (obs) - (min - uvalue), '0', - min - uvalue); - } - obstack_blank (obs, uvalue); - memset ((char *) obstack_next_free (obs) - uvalue, '1', uvalue); - return; - } - - s = ntoa (value, radix); - - if (*s == '-') - { - obstack_1grow (obs, '-'); - s++; - } - len = strlen (s); - if (len < min) - { - min -= len; - obstack_blank (obs, min); - memset ((char *) obstack_next_free (obs) - min, '0', min); - } - obstack_grow (obs, s, len); -} - - -static void -numb_initialise (void) -{ - ; -} - -/* This macro defines the top level code for the "eval" builtin. The - actual work is done in the function m4_evaluate (), which lives in - evalparse.c. */ -#define m4_evaluate builtin_eval -#include "evalparse.c" |