diff options
Diffstat (limited to 'modules/gnu.c')
-rw-r--r-- | modules/gnu.c | 1077 |
1 files changed, 0 insertions, 1077 deletions
diff --git a/modules/gnu.c b/modules/gnu.c deleted file mode 100644 index 334ab67d..00000000 --- a/modules/gnu.c +++ /dev/null @@ -1,1077 +0,0 @@ -/* GNU m4 -- A simple macro processor - Copyright (C) 2000, 2004-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, unles 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 "modules/m4.h" -#include "quotearg.h" -#include "spawn-pipe.h" -#include "wait-process.h" - -/* 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 (__file__, false, false, false, 0, 0 ) \ - BUILTIN (__line__, false, false, false, 0, 0 ) \ - BUILTIN (__program__, false, false, false, 0, 0 ) \ - BUILTIN (builtin, true, true, false, 1, -1 ) \ - BUILTIN (changeresyntax,false,true, false, 1, 1 ) \ - BUILTIN (changesyntax,false, true, false, 1, -1 ) \ - BUILTIN (debugfile, false, false, false, 0, 1 ) \ - BUILTIN (debuglen, false, true, false, 1, 1 ) \ - BUILTIN (debugmode, false, false, false, 0, 1 ) \ - BUILTIN (esyscmd, false, true, true, 1, 1 ) \ - BUILTIN (format, false, true, false, 1, -1 ) \ - BUILTIN (indir, true, true, false, 1, -1 ) \ - BUILTIN (mkdtemp, false, true, false, 1, 1 ) \ - BUILTIN (patsubst, false, true, true, 2, 4 ) \ - BUILTIN (regexp, false, true, true, 2, 4 ) \ - BUILTIN (renamesyms, false, true, false, 2, 3 ) \ - BUILTIN (m4modules, false, false, false, 0, 0 ) \ - BUILTIN (m4symbols, true, false, false, 0, -1 ) \ - BUILTIN (syncoutput, false, true, false, 1, 1 ) \ - - -/* 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 }, -}; - - -/* A table for mapping m4 symbol names to simple expansion text. */ -static const m4_macro m4_macro_table[] = -{ - /* name text min max */ -#if UNIX - { "__unix__", "", 0, 0 }, -#endif -#if W32_NATIVE - { "__windows__", "", 0, 0 }, -#endif -#if OS2 - { "__os2__", "", 0, 0 }, -#endif - { "__gnu__", "", 0, 0 }, - { "__m4_version__", VERSION,0, 0 }, - - { NULL, NULL, 0, 0 }, -}; - - -void -include_gnu (m4 *context, m4_module *module, m4_obstack *obs) -{ - m4_install_builtins (context, module, m4_builtin_table); - m4_install_macros (context, module, m4_macro_table); -} - - - -/* Regular expressions. Reuse re_registers among multiple - re_pattern_buffer allocations to reduce malloc usage. */ - -/* Maybe this is worth making runtime tunable. Too small, and nothing - gets cached because the working set of active regex is larger than - the cache, and we are always swapping out entries. Too large, and - the time spent searching the cache for a match overtakes the time - saved by caching. For now, this size proved reasonable for the - typical working set of Autoconf 2.62. */ -#define REGEX_CACHE_SIZE 16 - -/* Structure for using a compiled regex, as well as making it easier - to cache frequently used expressions. */ -typedef struct { - unsigned count; /* usage counter */ - int resyntax; /* flavor of regex */ - size_t len; /* length of string */ - char *str; /* copy of compiled string */ - struct re_pattern_buffer *pat; /* compiled regex, allocated */ - struct re_registers regs; /* match registers, reused */ -} m4_pattern_buffer; - -/* Storage for the cache of regular expressions. */ -static m4_pattern_buffer regex_cache[REGEX_CACHE_SIZE]; - -/* Compile a REGEXP of length LEN using the RESYNTAX flavor, and - return the buffer. On error, report the problem on behalf of - CALLER, and return NULL. */ - -static m4_pattern_buffer * -regexp_compile (m4 *context, const m4_call_info *caller, const char *regexp, - size_t len, int resyntax) -{ - /* regex_cache is guaranteed to start life 0-initialized, which - works in the algorithm below. - - FIXME - this method is not reentrant, since re_compile_pattern - mallocs memory, depends on the global variable re_syntax_options - for its syntax (but at least the compiled regex remembers its - syntax even if the global variable changes later), and since we - use a static variable. To be reentrant, we would need a mutex in - this method, and move the storage for regex_cache into context. */ - - const char *msg; /* error message from re_compile_pattern */ - int i; /* iterator */ - m4_pattern_buffer *victim; /* cache slot to replace */ - unsigned victim_count; /* track which victim to replace */ - struct re_pattern_buffer *pat;/* newly compiled regex */ - - /* First, check if REGEXP is already cached with the given RESYNTAX. - If so, increase its use count and return it. */ - for (i = 0; i < REGEX_CACHE_SIZE; i++) - if (len == regex_cache[i].len && resyntax == regex_cache[i].resyntax - && regex_cache[i].str && memcmp (regexp, regex_cache[i].str, len) == 0) - { - regex_cache[i].count++; - return ®ex_cache[i]; - } - - /* Next, check if REGEXP can be compiled. */ - pat = (struct re_pattern_buffer *) xzalloc (sizeof *pat); - re_set_syntax (resyntax); - msg = re_compile_pattern (regexp, len, pat); - - if (msg != NULL) - { - m4_warn (context, 0, caller, _("bad regular expression %s: %s"), - quotearg_style_mem (locale_quoting_style, regexp, len), msg); - regfree (pat); - free (pat); - return NULL; - } - /* Use a fastmap for speed; it is freed by regfree. */ - pat->fastmap = xcharalloc (UCHAR_MAX + 1); - - /* Now, find a victim slot. Decrease the count of all entries, then - prime the count of the victim slot at REGEX_CACHE_SIZE. This - way, frequently used entries and newly created entries are least - likely to be victims next time we have a cache miss. */ - victim = regex_cache; - victim_count = victim->count; - if (victim_count) - victim->count--; - for (i = 1; i < REGEX_CACHE_SIZE; i++) - { - if (regex_cache[i].count < victim_count) - { - victim_count = regex_cache[i].count; - victim = ®ex_cache[i]; - } - if (regex_cache[i].count) - regex_cache[i].count--; - } - victim->count = REGEX_CACHE_SIZE; - victim->resyntax = resyntax; - victim->len = len; - if (victim->str) - { - free (victim->str); - regfree (victim->pat); - free (victim->pat); - } - victim->str = xstrdup (regexp); - victim->pat = pat; - re_set_registers (pat, &victim->regs, victim->regs.num_regs, - victim->regs.start, victim->regs.end); - return victim; -} - - -/* Wrap up GNU Regex re_search call to work with an m4_pattern_buffer. - If NO_SUB, then storing matches in buf->regs is not necessary. */ - -static regoff_t -regexp_search (m4_pattern_buffer *buf, const char *string, const int size, - const int start, const int range, bool no_sub) -{ - return re_search (buf->pat, string, size, start, range, - no_sub ? NULL : &buf->regs); -} - - -/* Function to perform substitution by regular expressions. Used by - the builtins regexp, patsubst and renamesyms. The changed text is - placed on the obstack OBS. The substitution is REPL of length - REPL_LEN, with \& substituted by this part of VICTIM matched by the - last whole regular expression, and \N substituted by the text - matched by the Nth parenthesized sub-expression in BUF. Any - warnings are issued on behalf of CALLER. BUF may be NULL for the - empty regex. */ - -static void -substitute (m4 *context, m4_obstack *obs, const m4_call_info *caller, - const char *victim, const char *repl, size_t repl_len, - m4_pattern_buffer *buf) -{ - int ch; - while (1) - { - const char *backslash = (char *) memchr (repl, '\\', repl_len); - if (!backslash) - { - obstack_grow (obs, repl, repl_len); - return; - } - obstack_grow (obs, repl, backslash - repl); - repl_len -= backslash - repl + 1; - if (!repl_len) - { - m4_warn (context, 0, caller, - _("trailing \\ ignored in replacement")); - return; - } - repl = backslash + 1; - ch = *repl++; - repl_len--; - switch (ch) - { - case '&': - if (buf) - obstack_grow (obs, victim + buf->regs.start[0], - buf->regs.end[0] - buf->regs.start[0]); - break; - - case '1': case '2': case '3': case '4': case '5': case '6': - case '7': case '8': case '9': - ch -= '0'; - if (!buf || buf->pat->re_nsub < ch) - m4_warn (context, 0, caller, _("sub-expression %d not present"), - ch); - else if (buf->regs.end[ch] > 0) - obstack_grow (obs, victim + buf->regs.start[ch], - buf->regs.end[ch] - buf->regs.start[ch]); - break; - - default: - obstack_1grow (obs, ch); - break; - } - } -} - - -/* For each match against REGEXP of length REGEXP_LEN (precompiled in - BUF as returned by regexp_compile) in VICTIM of length LEN, - substitute REPLACE of length REPL_LEN. Non-matching characters are - copied verbatim, and the result copied to the obstack. Errors are - reported on behalf of CALLER. Return true if a substitution was - made. If OPTIMIZE is set, don't worry about copying the input if - no changes are made. */ - -static bool -regexp_substitute (m4 *context, m4_obstack *obs, const m4_call_info *caller, - const char *victim, size_t len, const char *regexp, - size_t regexp_len, m4_pattern_buffer *buf, - const char *replace, size_t repl_len, bool optimize) -{ - regoff_t matchpos = 0; /* start position of match */ - size_t offset = 0; /* current match offset */ - bool subst = !optimize; /* if a substitution has been made */ - - while (offset <= len) - { - matchpos = regexp_search (buf, victim, len, offset, len - offset, - false); - - 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) - m4_error (context, 0, 0, caller, - _("problem matching regular expression %s"), - quotearg_style_mem (locale_quoting_style, regexp, - regexp_len)); - else if (offset < len && subst) - obstack_grow (obs, victim + offset, len - 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 (context, obs, caller, victim, replace, repl_len, buf); - subst = true; - - /* 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 = buf->regs.end[0]; - if (buf->regs.start[0] == buf->regs.end[0]) - { - if (offset < len) - obstack_1grow (obs, victim[offset]); - offset++; - } - } - - return subst; -} - - - -/** - * __file__ - **/ -M4BUILTIN_HANDLER (__file__) -{ - m4_shipout_string (context, obs, m4_get_current_file (context), SIZE_MAX, - true); -} - - -/** - * __line__ - **/ -M4BUILTIN_HANDLER (__line__) -{ - m4_shipout_int (obs, m4_get_current_line (context)); -} - - -/** - * __program__ - **/ -M4BUILTIN_HANDLER (__program__) -{ - m4_shipout_string (context, obs, m4_get_program_name (), SIZE_MAX, true); -} - - -/* 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. A - special form allows one to retrieve the special token that defn - would normally return, even if that builtin is not currently - defined and hence can't be passed to defn. */ - -/** - * builtin(MACRO, [...]) - * builtin(defn(`builtin'), MACRO) - **/ -M4BUILTIN_HANDLER (builtin) -{ - const m4_call_info *me = m4_arg_info (argv); - const char *name; - size_t len; - m4_symbol_value *value = NULL; - - if (!m4_is_arg_text (argv, 1)) - { - assert (m4_is_arg_func (argv, 1)); - if (m4_arg_func (argv, 1) == builtin_builtin) - { - if (m4_bad_argc (context, argc, me, 2, 2, false)) - return; - if (!m4_is_arg_text (argv, 2)) - { - m4_warn (context, 0, me, _("invalid macro name ignored")); - return; - } - name = M4ARG (2); - len = M4ARGLEN (2); - if (len == strlen (name)) - value = m4_builtin_find_by_name (context, NULL, name); - if (value) - { - m4_push_builtin (context, obs, value); - free (value); - } - else if (m4_is_debug_bit (context, M4_DEBUG_TRACE_DEREF)) - m4_warn (context, 0, me, _("undefined builtin %s"), - quotearg_style_mem (locale_quoting_style, name, len)); - } - else - m4_warn (context, 0, me, _("invalid macro name ignored")); - } - else - { - name = M4ARG (1); - len = M4ARGLEN (1); - if (len == strlen (name)) - value = m4_builtin_find_by_name (context, NULL, name); - if (value == NULL) - { - if (m4_is_debug_bit (context, M4_DEBUG_TRACE_DEREF)) - m4_warn (context, 0, me, _("undefined builtin %s"), - quotearg_style_mem (locale_quoting_style, name, len)); - } - else - { - const m4_builtin *bp = m4_get_symbol_value_builtin (value); - m4_macro_args *new_argv; - bool flatten = (bp->flags & M4_BUILTIN_FLATTEN_ARGS) != 0; - new_argv = m4_make_argv_ref (context, argv, name, len, flatten, - false); - if (!m4_bad_argc (context, argc - 1, m4_arg_info (new_argv), - bp->min_args, bp->max_args, - (bp->flags & M4_BUILTIN_SIDE_EFFECT) != 0)) - bp->func (context, obs, argc - 1, new_argv); - free (value); - } - } -} - - -/* Change the current regexp syntax to SPEC of length LEN, or report - failure on behalf of CALLER. Currently this affects the builtins: - `patsubst', `regexp' and `renamesyms'. */ - -static int -m4_resyntax_encode_safe (m4 *context, const m4_call_info *caller, - const char *spec, size_t len) -{ - int resyntax; - - if (strlen (spec) < len) - resyntax = -1; - else - resyntax = m4_regexp_syntax_encode (spec); - - if (resyntax < 0) - m4_warn (context, 0, caller, _("bad syntax-spec: %s"), - quotearg_style_mem (locale_quoting_style, spec, len)); - - return resyntax; -} - - -/** - * changeresyntax(RESYNTAX-SPEC) - **/ -M4BUILTIN_HANDLER (changeresyntax) -{ - int resyntax = m4_resyntax_encode_safe (context, m4_arg_info (argv), - M4ARG (1), M4ARGLEN (1)); - - if (resyntax >= 0) - m4_set_regexp_syntax_opt (context, resyntax); -} - - -/* Change the current input syntax. The function m4_set_syntax () - lives in syntax.c. Any changes to comment delimiters and quotes - made here will be overridden by a call to `changecom' or - `changequote'. */ - -/** - * changesyntax(SYNTAX-SPEC, ...) - **/ -M4BUILTIN_HANDLER (changesyntax) -{ - const m4_call_info *me = m4_arg_info (argv); - M4_MODULE_IMPORT (m4, m4_expand_ranges); - - if (m4_expand_ranges) - { - size_t i; - for (i = 1; i < argc; i++) - { - size_t len = M4ARGLEN (i); - const char *spec; - char key; - char action; - - if (!len) - { - m4_reset_syntax (M4SYNTAX); - continue; - } - spec = M4ARG (i); - key = *spec++; - len--; - action = len ? *spec : '\0'; - switch (action) - { - case '-': - case '+': - case '=': - spec++; - len--; - break; - case '\0': - if (!len) - break; - /* fall through */ - default: - action = '='; - break; - } - if (len) - spec = m4_expand_ranges (spec, &len, m4_arg_scratch (context)); - if (m4_set_syntax (M4SYNTAX, key, action, spec, len) < 0) - m4_warn (context, 0, me, _("undefined syntax code: %s"), - quotearg_style_mem (locale_quoting_style, &key, 1)); - } - } - else - assert (!"Unable to import from m4 module"); -} - - -/* Specify the destination of the debugging output. With one argument, the - argument is taken as a file name, with no arguments, revert to stderr. */ - -/** - * debugfile([FILENAME]) - **/ -M4BUILTIN_HANDLER (debugfile) -{ - const m4_call_info *me = m4_arg_info (argv); - - if (argc == 1) - m4_debug_set_output (context, me, NULL); - else if (m4_get_safer_opt (context) && !m4_arg_empty (argv, 1)) - m4_error (context, 0, 0, me, _("disabled by --safer")); - else - { - const char *str = M4ARG (1); - size_t len = M4ARGLEN (1); - if (strlen (str) < len) - m4_warn (context, 0, me, _("argument %s truncated"), - quotearg_style_mem (locale_quoting_style, str, len)); - if (!m4_debug_set_output (context, me, str)) - m4_warn (context, errno, me, _("cannot set debug file %s"), - quotearg_style (locale_quoting_style, str)); - } -} - - -/* On-the-fly control of debug length. It takes one integer - argument. */ - -/** - * debuglen(LEN) - **/ -M4BUILTIN_HANDLER (debuglen) -{ - int i; - size_t s; - if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1), - &i)) - return; - /* FIXME - make m4_numeric_arg more powerful - we want to accept - suffixes, and limit the result to size_t. */ - s = i <= 0 ? SIZE_MAX : i; - m4_set_max_debug_arg_length_opt (context, s); -} - -/* 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. */ - -/** - * debugmode([FLAGS]) - **/ -M4BUILTIN_HANDLER (debugmode) -{ - const char* mode = M4ARG (1); - size_t len = M4ARGLEN (1); - if (argc == 1) - m4_set_debug_level_opt (context, 0); - else if (m4_debug_decode (context, mode, len) < 0) - m4_warn (context, 0, m4_arg_info (argv), - _("bad debug flags: %s"), - quotearg_style_mem (locale_quoting_style, mode, len)); -} - - -/* Same as the sysymd builtin from m4.c module, but expand to the - output of SHELL-COMMAND. */ - -/** - * esyscmd(SHELL-COMMAND) - **/ - -M4BUILTIN_HANDLER (esyscmd) -{ - const m4_call_info *me = m4_arg_info (argv); - const char *cmd = M4ARG (1); - size_t len = M4ARGLEN (1); - M4_MODULE_IMPORT (m4, m4_set_sysval); - M4_MODULE_IMPORT (m4, m4_sysval_flush); - - if (m4_set_sysval && m4_sysval_flush) - { - pid_t child; - int fd; - FILE *pin; - int status; - int sig_status; - const char *prog_args[4] = { "sh", "-c" }; - const char *caller; - - if (m4_get_safer_opt (context)) - { - m4_error (context, 0, 0, me, _("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; - caller = m4_info_name (me); - errno = 0; - child = create_pipe_in (caller, M4_SYSCMD_SHELL, (char **) prog_args, - NULL, false, true, false, &fd); - if (child == -1) - { - m4_error (context, 0, errno, me, _("cannot run command %s"), - quotearg_style (locale_quoting_style, cmd)); - m4_set_sysval (127); - return; - } -#if OS2 - /* On OS/2 kLIBC, fdopen() creates a stream in a mode of a file - descriptor. So incldue "t" to open stream in a text mode explicitly. */ - pin = fdopen (fd, "rt"); -#else - pin = fdopen (fd, "r"); -#endif - if (!pin) - { - m4_error (context, 0, errno, me, _("cannot run command %s"), - quotearg_style (locale_quoting_style, cmd)); - m4_set_sysval (127); - close (fd); - return; - } - while (1) - { - size_t avail = obstack_room (obs); - if (!avail) - { - int ch = getc (pin); - if (ch == EOF) - break; - obstack_1grow (obs, ch); - } - else - { - size_t len = fread (obstack_next_free (obs), 1, avail, pin); - if (len <= 0) - break; - obstack_blank_fast (obs, len); - } - } - if (ferror (pin) || fclose (pin)) - m4_error (context, EXIT_FAILURE, errno, me, - _("cannot read pipe to command %s"), - quotearg_style (locale_quoting_style, cmd)); - errno = 0; - status = wait_subprocess (child, caller, false, true, true, false, - &sig_status); - if (sig_status) - { - assert (status == 127); - m4_set_sysval (sig_status << 8); - } - else - { - if (status == 127 && errno) - m4_error (context, 0, errno, me, _("cannot run command %s"), - quotearg_style (locale_quoting_style, cmd)); - m4_set_sysval (status); - } - } - else - assert (!"Unable to import from m4 module"); -} - - -/* Frontend for printf like formatting. The function format () lives in - the file format.c. */ - -#include "format.c" - -/** - * format(FORMAT-STRING, [...]) - **/ -M4BUILTIN_HANDLER (format) -{ - format (context, obs, argc, argv); -} - - -/* 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. */ - -/** - * indir(MACRO, [...]) - **/ -M4BUILTIN_HANDLER (indir) -{ - const m4_call_info *me = m4_arg_info (argv); - if (!m4_is_arg_text (argv, 1)) - m4_warn (context, 0, me, _("invalid macro name ignored")); - else - { - const char *name = M4ARG (1); - size_t len = M4ARGLEN (1); - m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, name, len); - - if (symbol == NULL) - { - if (m4_is_debug_bit (context, M4_DEBUG_TRACE_DEREF)) - m4_warn (context, 0, me, _("undefined macro %s"), - quotearg_style_mem (locale_quoting_style, name, len)); - } - else - { - m4_macro_args *new_argv; - m4_symbol_value *value = m4_get_symbol_value (symbol); - new_argv = m4_make_argv_ref (context, argv, name, len, - m4_symbol_flatten_args (symbol), - m4_get_symbol_traced (symbol)); - m4_trace_prepare (context, m4_arg_info (new_argv), value); - m4_macro_call (context, value, obs, new_argv); - } - } -} - - -/* The builtin "mkdtemp" allows creation of temporary directories. */ - -/** - * mkdtemp(TEMPLATE) - **/ -M4BUILTIN_HANDLER (mkdtemp) -{ - M4_MODULE_IMPORT (m4, m4_make_temp); - - if (m4_make_temp) - m4_make_temp (context, obs, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1), - true); - else - assert (!"Unable to import from m4 module"); -} - - -/* Substitute all matches of a regexp occurring in a string. Each - match of the second argument (a regexp) in the first argument is - changed to the optional third argument, with \& substituted by the - matched text, and \N substituted by the text matched by the Nth - parenthesized sub-expression. The optional fourth argument changes - the regex flavor. */ - -/** - * patsubst(VICTIM, REGEXP, [REPLACEMENT], [RESYNTAX]) - **/ -M4BUILTIN_HANDLER (patsubst) -{ - const m4_call_info *me = m4_arg_info (argv); - const char *pattern; /* regular expression */ - const char *replace; /* replacement */ - m4_pattern_buffer *buf; /* compiled regular expression */ - int resyntax; - - resyntax = m4_get_regexp_syntax_opt (context); - if (argc >= 5) /* additional args ignored */ - { - resyntax = m4_resyntax_encode_safe (context, me, M4ARG (4), - M4ARGLEN (4)); - if (resyntax < 0) - return; - } - - /* The empty regex matches everywhere, but if there is no - replacement, we need not waste time with it. */ - if (m4_arg_empty (argv, 2) && m4_arg_empty (argv, 3)) - { - m4_push_arg (context, obs, argv, 1); - return; - } - - pattern = M4ARG (2); - replace = M4ARG (3); - - buf = regexp_compile (context, me, pattern, M4ARGLEN (2), resyntax); - if (!buf) - return; - - regexp_substitute (context, obs, me, M4ARG (1), M4ARGLEN (1), pattern, - M4ARGLEN (2), buf, replace, M4ARGLEN (3), false); -} - - -/* 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 there is no match. Given a third - argument, a match is substituted according to this argument. The - optional fourth argument changes the regex flavor. */ - -/** - * regexp(VICTIM, REGEXP, RESYNTAX) - * regexp(VICTIM, REGEXP, [REPLACEMENT], [RESYNTAX]) - **/ -M4BUILTIN_HANDLER (regexp) -{ - const m4_call_info *me = m4_arg_info (argv); - const char *victim; /* string to search */ - const char *pattern; /* regular expression */ - const char *replace; /* optional replacement string */ - m4_pattern_buffer *buf; /* compiled regular expression */ - regoff_t startpos; /* start position of match */ - size_t len; /* length of first argument */ - int resyntax; - - pattern = M4ARG (2); - replace = M4ARG (3); - resyntax = m4_get_regexp_syntax_opt (context); - - if (argc == 4) - { - resyntax = m4_regexp_syntax_encode (replace); - - /* The first case is the most difficult, because the empty string - is a valid RESYNTAX, yet we want `regexp(aab, a*, )' to return - an empty string as per M4 1.4.x. */ - - if (m4_arg_empty (argv, 3) || (resyntax < 0)) - /* regexp(VICTIM, REGEXP, REPLACEMENT) */ - resyntax = m4_get_regexp_syntax_opt (context); - else - /* regexp(VICTIM, REGEXP, RESYNTAX) */ - replace = NULL; - } - else if (argc >= 5) - { - /* regexp(VICTIM, REGEXP, REPLACEMENT, RESYNTAX) */ - resyntax = m4_resyntax_encode_safe (context, me, M4ARG (4), - M4ARGLEN (4)); - if (resyntax < 0) - return; - } - else - /* regexp(VICTIM, REGEXP) */ - replace = NULL; - - if (m4_arg_empty (argv, 2)) - { - /* The empty regex matches everything. */ - if (replace) - substitute (context, obs, me, M4ARG (1), replace, M4ARGLEN (3), NULL); - else - m4_shipout_int (obs, 0); - return; - } - - buf = regexp_compile (context, me, pattern, M4ARGLEN (2), resyntax); - if (!buf) - return; - - victim = M4ARG (1); - len = M4ARGLEN (1); - startpos = regexp_search (buf, victim, len, 0, len, replace == NULL); - - if (startpos == -2) - { - m4_error (context, 0, 0, me, _("problem matching regular expression %s"), - quotearg_style_mem (locale_quoting_style, pattern, - M4ARGLEN (2))); - return; - } - - if (replace == NULL) - m4_shipout_int (obs, startpos); - else if (startpos >= 0) - substitute (context, obs, me, victim, replace, M4ARGLEN (3), buf); -} - - -/* Rename all current symbols that match REGEXP according to the - REPLACEMENT specification. */ - -/** - * renamesyms(REGEXP, REPLACEMENT, [RESYNTAX]) - **/ -M4BUILTIN_HANDLER (renamesyms) -{ - M4_MODULE_IMPORT (m4, m4_dump_symbols); - - if (m4_dump_symbols) - { - const m4_call_info *me = m4_arg_info (argv); - const char *regexp; /* regular expression string */ - size_t regexp_len; - const char *replace; /* replacement expression string */ - size_t replace_len; - - m4_pattern_buffer *buf; /* compiled regular expression */ - - m4_dump_symbol_data data; - - int resyntax; - - regexp = M4ARG (1); - regexp_len = M4ARGLEN (1); - replace = M4ARG (2); - replace_len = M4ARGLEN (2); - - resyntax = m4_get_regexp_syntax_opt (context); - if (argc >= 4) - { - resyntax = m4_resyntax_encode_safe (context, me, M4ARG (3), - M4ARGLEN (3)); - if (resyntax < 0) - return; - } - - buf = regexp_compile (context, me, regexp, regexp_len, resyntax); - if (!buf) - return; - - data.obs = m4_arg_scratch (context); - m4_dump_symbols (context, &data, 1, argv, false); - - for (; data.size > 0; --data.size, data.base++) - { - const m4_string *key = &data.base[0]; - - if (regexp_substitute (context, data.obs, me, key->str, key->len, - regexp, regexp_len, buf, replace, replace_len, - true)) - { - size_t newlen = obstack_object_size (data.obs); - m4_symbol_rename (M4SYMTAB, key->str, key->len, - (char *) obstack_finish (data.obs), newlen); - } - } - } - else - assert (!"Unable to import from m4 module"); -} - - -/** - * m4modules() - **/ -M4BUILTIN_HANDLER (m4modules) -{ - /* The expansion of this builtin is a comma separated list of - loaded modules. */ - m4_module *module = m4_module_next (context, NULL); - - if (module) - do - { - m4_shipout_string (context, obs, m4_get_module_name (module), SIZE_MAX, - true); - - if ((module = m4_module_next (context, module))) - obstack_1grow (obs, ','); - } - while (module); -} - - -/* Implementation of "m4symbols". It builds up a table of pointers to - symbols, sorts it and ships out the symbol names. */ - -/** - * m4symbols([...]) - **/ -M4BUILTIN_HANDLER (m4symbols) -{ - M4_MODULE_IMPORT (m4, m4_dump_symbols); - - if (m4_dump_symbols) - { - m4_dump_symbol_data data; - - data.obs = m4_arg_scratch (context); - m4_dump_symbols (context, &data, argc, argv, false); - - for (; data.size > 0; --data.size, data.base++) - { - m4_shipout_string (context, obs, data.base->str, data.base->len, - true); - if (data.size > 1) - obstack_1grow (obs, ','); - } - } - else - assert (!"Unable to import from m4 module"); -} - - -/* This contains macro which implements syncoutput() which takes one arg - 1, on, yes - turn on sync lines - 0, off, no, blank - turn off sync lines - everything else is silently ignored */ - -/** - * syncoutput(TRUTH) - **/ -M4BUILTIN_HANDLER (syncoutput) -{ - bool value = m4_get_syncoutput_opt (context); - value = m4_parse_truth_arg (context, m4_arg_info (argv), M4ARG (1), - M4ARGLEN (1), value); - m4_set_syncoutput_opt (context, value); -} |