diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/freeze.c | 1000 | ||||
-rw-r--r-- | src/m4.h | 43 | ||||
-rw-r--r-- | src/main.c | 762 | ||||
-rw-r--r-- | src/stackovf.c | 454 |
4 files changed, 0 insertions, 2259 deletions
diff --git a/src/freeze.c b/src/freeze.c deleted file mode 100644 index 426e34b5..00000000 --- a/src/freeze.c +++ /dev/null @@ -1,1000 +0,0 @@ -/* GNU m4 -- A simple macro processor - Copyright (C) 1989-1994, 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/>. -*/ - -/* This module handles frozen files. */ - -#include <config.h> - -#include "m4.h" - -#include "binary-io.h" -#include "close-stream.h" -#include "quotearg.h" -#include "verify.h" -#include "xmemdup0.h" - -static void produce_mem_dump (FILE *, const char *, size_t); -static void produce_resyntax_dump (m4 *, FILE *); -static void produce_syntax_dump (FILE *, m4_syntax_table *, char); -static void produce_module_dump (m4 *, FILE *, m4_module *); -static void produce_symbol_dump (m4 *, FILE *, m4_symbol_table *); -static void *dump_symbol_CB (m4_symbol_table *, const char *, - size_t, m4_symbol *, void *); -static void issue_expect_message (m4 *, int); -static int decode_char (m4 *, FILE *, bool *); - - -/* Dump an ASCII-encoded representation of LEN bytes at MEM to FILE. - MEM may contain embedded NUL characters. */ -static void -produce_mem_dump (FILE *file, const char *mem, size_t len) -{ - char *quoted = quotearg_style_mem (escape_quoting_style, mem, len); - /* Any errors will be detected by ferror later. */ - fwrite (quoted, strlen (quoted), 1, file); -} - - -/* Produce the 'R14\nPOSIX_EXTENDED\n' frozen file dump of the current - default regular expression syntax. Note that it would be a little - faster to use the encoded syntax in this format as used by re_compile(), - but the representation of RE_SYNTAX_POSIX_EXTENDED may change in - future (or alternative) implementations of re_compile, so we use an - unencoded representation here. */ - -static void -produce_resyntax_dump (m4 *context, FILE *file) -{ - int code = m4_get_regexp_syntax_opt (context); - - /* Don't dump default syntax code (`0' for GNU_EMACS). */ - if (code) - { - const char *resyntax = m4_regexp_syntax_decode (code); - - if (!resyntax) - m4_error (context, EXIT_FAILURE, 0, NULL, - _("invalid regexp syntax code `%d'"), code); - - /* No need to use produce_mem_dump, since we know all resyntax - names are already ASCII-encoded. */ - xfprintf (file, "R%zu\n%s\n", strlen (resyntax), resyntax); - } -} - -static void -produce_syntax_dump (FILE *file, m4_syntax_table *syntax, char ch) -{ - char buf[UCHAR_MAX + 1]; - int code = m4_syntax_code (ch); - int count = 0; - int i; - - for (i = 0; i < UCHAR_MAX + 1; ++i) - if (m4_has_syntax (syntax, i, code) && code != syntax->orig[i]) - buf[count++] = i; - - /* If code falls in M4_SYNTAX_MASKS, then we must treat it - specially, since it will not be found in syntax->orig. */ - if (count == 1 - && ((code == M4_SYNTAX_RQUOTE && *buf == *DEF_RQUOTE) - || (code == M4_SYNTAX_ECOMM && *buf == *DEF_ECOMM))) - return; - - if (count || (code & M4_SYNTAX_MASKS)) - { - xfprintf (file, "S%c%d\n", ch, count); - produce_mem_dump (file, buf, count); - fputc ('\n', file); - } -} - -/* Store the debug mode in textual format. */ -static void -produce_debugmode_state (FILE *file, int flags) -{ - /* This code tracks the number of bits in M4_DEBUG_TRACE_VERBOSE. */ - char str[15]; - int offset = 0; - verify ((1 << (sizeof str - 1)) - 1 == M4_DEBUG_TRACE_VERBOSE); - if (flags & M4_DEBUG_TRACE_ARGS) - str[offset++] = 'a'; - if (flags & M4_DEBUG_TRACE_EXPANSION) - str[offset++] = 'e'; - if (flags & M4_DEBUG_TRACE_QUOTE) - str[offset++] = 'q'; - if (flags & M4_DEBUG_TRACE_ALL) - str[offset++] = 't'; - if (flags & M4_DEBUG_TRACE_LINE) - str[offset++] = 'l'; - if (flags & M4_DEBUG_TRACE_FILE) - str[offset++] = 'f'; - if (flags & M4_DEBUG_TRACE_PATH) - str[offset++] = 'p'; - if (flags & M4_DEBUG_TRACE_CALL) - str[offset++] = 'c'; - if (flags & M4_DEBUG_TRACE_INPUT) - str[offset++] = 'i'; - if (flags & M4_DEBUG_TRACE_CALLID) - str[offset++] = 'x'; - if (flags & M4_DEBUG_TRACE_MODULE) - str[offset++] = 'm'; - if (flags & M4_DEBUG_TRACE_STACK) - str[offset++] = 's'; - if (flags & M4_DEBUG_TRACE_DEREF) - str[offset++] = 'd'; - if (flags & M4_DEBUG_TRACE_OUTPUT_DUMPDEF) - str[offset++] = 'o'; - str[offset] = '\0'; - if (offset) - xfprintf (file, "d%d\n%s\n", offset, str); -} - -/* The modules must be dumped in the order in which they will be - reloaded from the frozen file. We store handles in a push - down stack, so we need to dump them in the reverse order to that. */ -static void -produce_module_dump (m4 *context, FILE *file, m4_module *module) -{ - const char *name = m4_get_module_name (module); - size_t len = strlen (name); - - module = m4_module_next (context, module); - if (module) - produce_module_dump (context, file, module); - - xfprintf (file, "M%zu\n", len); - produce_mem_dump (file, name, len); - fputc ('\n', file); -} - -/* Process all entries in one bucket, from the last to the first. - This order ensures that, at reload time, pushdef's will be - executed with the oldest definitions first. */ -static void -produce_symbol_dump (m4 *context, FILE *file, m4_symbol_table *symtab) -{ - if (m4_symtab_apply (symtab, true, dump_symbol_CB, file)) - assert (false); -} - -/* Given a stack of symbol values starting with VALUE, destructively - reverse the stack and return the pointer to what was previously the - last value in the stack. VALUE may be NULL. The symbol table that - owns the value stack should not be modified or consulted until this - is called again to undo the effect. */ -static m4_symbol_value * -reverse_symbol_value_stack (m4_symbol_value *value) -{ - m4_symbol_value *result = NULL; - m4_symbol_value *next; - while (value) - { - next = VALUE_NEXT (value); - VALUE_NEXT (value) = result; - result = value; - value = next; - } - return result; -} - -/* Dump the stack of values for SYMBOL, with name SYMBOL_NAME and - length LEN, located in SYMTAB. USERDATA is interpreted as the - FILE* to dump to. */ -static void * -dump_symbol_CB (m4_symbol_table *symtab, const char *symbol_name, size_t len, - m4_symbol *symbol, void *userdata) -{ - FILE *file = (FILE *) userdata; - m4_symbol_value *value; - m4_symbol_value *last; - - last = value = reverse_symbol_value_stack (m4_get_symbol_value (symbol)); - while (value) - { - m4_module *module = VALUE_MODULE (value); - const char *module_name = module ? m4_get_module_name (module) : NULL; - size_t module_len = module_name ? strlen (module_name) : 0; - - if (m4_is_symbol_value_text (value)) - { - const char *text = m4_get_symbol_value_text (value); - size_t text_len = m4_get_symbol_value_len (value); - xfprintf (file, "T%zu,%zu", len, text_len); - if (module) - xfprintf (file, ",%zu", module_len); - fputc ('\n', file); - - produce_mem_dump (file, symbol_name, len); - fputc ('\n', file); - produce_mem_dump (file, text, text_len); - fputc ('\n', file); - if (module) - { - produce_mem_dump (file, module_name, module_len); - fputc ('\n', file); - } - } - else if (m4_is_symbol_value_func (value)) - { - const m4_builtin *bp = m4_get_symbol_value_builtin (value); - size_t bp_len; - if (bp == NULL) - assert (!"INTERNAL ERROR: builtin not found in builtin table!"); - bp_len = strlen (bp->name); - - xfprintf (file, "F%zu,%zu", len, bp_len); - if (module) - xfprintf (file, ",%zu", module_len); - fputc ('\n', file); - - produce_mem_dump (file, symbol_name, len); - fputc ('\n', file); - produce_mem_dump (file, bp->name, bp_len); - fputc ('\n', file); - if (module) - { - produce_mem_dump (file, module_name, module_len); - fputc ('\n', file); - } - } - else if (m4_is_symbol_value_placeholder (value)) - ; /* Nothing to do for a builtin we couldn't reload earlier. */ - else - assert (!"dump_symbol_CB"); - value = VALUE_NEXT (value); - } - reverse_symbol_value_stack (last); - if (m4_get_symbol_traced (symbol)) - xfprintf (file, "t%zu\n%s\n", len, symbol_name); - return NULL; -} - -/* Produce a frozen state to the given file NAME. */ -void -produce_frozen_state (m4 *context, const char *name) -{ - FILE *file = fopen (name, O_BINARY ? "wb" : "w"); - const char *str; - const m4_string_pair *pair; - - if (!file) - { - m4_error (context, 0, errno, NULL, _("cannot open %s"), - quotearg_style (locale_quoting_style, name)); - return; - } - - /* Write a recognizable header. */ - - xfprintf (file, "# This is a frozen state file generated by GNU %s %s\n", - PACKAGE, VERSION); - fputs ("V2\n", file); - - /* Dump quote delimiters. */ - pair = m4_get_syntax_quotes (M4SYNTAX); - if (STRNEQ (pair->str1, DEF_LQUOTE) || STRNEQ (pair->str2, DEF_RQUOTE)) - { - xfprintf (file, "Q%zu,%zu\n", pair->len1, pair->len2); - produce_mem_dump (file, pair->str1, pair->len1); - fputc ('\n', file); - produce_mem_dump (file, pair->str2, pair->len2); - fputc ('\n', file); - } - - /* Dump comment delimiters. */ - pair = m4_get_syntax_comments (M4SYNTAX); - if (STRNEQ (pair->str1, DEF_BCOMM) || STRNEQ (pair->str2, DEF_ECOMM)) - { - xfprintf (file, "C%zu,%zu\n", pair->len1, pair->len2); - produce_mem_dump (file, pair->str1, pair->len1); - fputc ('\n', file); - produce_mem_dump (file, pair->str2, pair->len2); - fputc ('\n', file); - } - - /* Dump regular expression syntax. */ - produce_resyntax_dump (context, file); - - /* Dump syntax table. */ - str = "I@WLBOD${}SA(),RE"; - while (*str) - produce_syntax_dump (file, M4SYNTAX, *str++); - - /* Dump debugmode state. */ - produce_debugmode_state (file, m4_get_debug_level_opt (context)); - - /* Dump all loaded modules. */ - produce_module_dump (context, file, m4_module_next (context, NULL)); - - /* Dump all symbols. */ - produce_symbol_dump (context, file, M4SYMTAB); - - /* Let diversions be issued from output.c module, its cleaner to have this - piece of code there. */ - m4_freeze_diversions (context, file); - - /* All done. */ - - fputs ("# End of frozen state file\n", file); - if (close_stream (file) != 0) - m4_error (context, EXIT_FAILURE, errno, NULL, - _("unable to create frozen state")); -} - -/* Issue a message saying that some character is an EXPECTED character. */ -static void -issue_expect_message (m4 *context, int expected) -{ - if (expected == '\n') - m4_error (context, EXIT_FAILURE, 0, NULL, - _("expecting line feed in frozen file")); - else - m4_error (context, EXIT_FAILURE, 0, NULL, - _("expecting character `%c' in frozen file"), expected); -} - - -/* Reload frozen state. */ - -/* Read the next character from the IN stream. Various escape - sequences are converted, and returned. EOF is returned if the end - of file is reached whilst reading the character, or on an - unrecognized escape sequence. */ - -static int -decode_char (m4 *context, FILE *in, bool *advance_line) -{ - int ch = getc (in); - int next; - int value = 0; - - if (*advance_line) - { - m4_set_current_line (context, m4_get_current_line (context) + 1); - *advance_line = false; - } - - while (ch == '\\') - { - ch = getc (in); - switch (ch) - { - case 'a': return '\a'; - case 'b': return '\b'; - case 'f': return '\f'; - case 'n': return '\n'; - case 'r': return '\r'; - case 't': return '\t'; - case 'v': return '\v'; - case '\\': return '\\'; - - case '\n': - ch = getc (in); - m4_set_current_line (context, m4_get_current_line (context) + 1); - continue; - - case 'x': case 'X': - next = getc (in); - if (next >= '0' && next <= '9') - ch = (next - '0') * 16; - else if (next >= 'a' && next <= 'f') - ch = (next - 'a' + 10) * 16; - else if (next >= 'A' && next <= 'F') - ch = (next - 'A' + 10) * 16; - else - return EOF; - next = getc (in); - if (next >= '0' && next <= '9') - ch += next - '0'; - else if (next >= 'a' && next <= 'f') - ch += next - 'a' + 10; - else if (next >= 'A' && next <= 'F') - ch += next - 'A' + 10; - else - return EOF; - return ch; - case '0': case '1': case '2': case '3': - value = ch - '0'; - ch = getc (in); - /* fall through */ - case '4': case '5': case '6': case '7': - if (ch >= '0' && ch <= '7') - { - value = value * 8 + ch - '0'; - ch = getc (in); - } - else - { - ungetc (ch, in); - return value; - } - if (ch >= '0' && ch <= '7') - value = value * 8 + ch - '0'; - else - ungetc (ch, in); - return value; - - default: - return EOF; - } - } - - if (ch == '\n') - *advance_line = true; - return ch; -} - - -/* Reload state from the given file NAME. We are seeking speed, - here. */ - -void -reload_frozen_state (m4 *context, const char *name) -{ - FILE *file = NULL; - char *filepath; - int version; - int character; - int operation; - char syntax; - char *string[3]; - size_t allocated[3]; - int number[3] = {0}; - bool advance_line = true; - -#define GET_CHARACTER \ - do \ - { \ - if (advance_line) \ - { \ - m4_set_current_line (context, \ - m4_get_current_line (context) + 1); \ - advance_line = false; \ - } \ - character = getc (file); \ - if (character == '\n') \ - advance_line = true; \ - } \ - while (0) - -#define GET_NUMBER(Number, AllowNeg) \ - do \ - { \ - unsigned int n = 0; \ - while (isdigit (character) && n <= INT_MAX / 10) \ - { \ - n = 10 * n + character - '0'; \ - GET_CHARACTER; \ - } \ - if (((AllowNeg) ? INT_MIN: INT_MAX) < n \ - || isdigit (character)) \ - m4_error (context, EXIT_FAILURE, 0, NULL, \ - _("integer overflow in frozen file")); \ - (Number) = n; \ - } \ - while (0) - -#define GET_STRING(File, Buf, BufSize, StrLen, UseChar) \ - do \ - { \ - size_t len = (StrLen); \ - char *p; \ - int ch; \ - if (UseChar) \ - { \ - ungetc (character, File); \ - if (advance_line) \ - { \ - assert (character == '\n'); \ - advance_line = false; \ - } \ - } \ - CHECK_ALLOCATION ((Buf), (BufSize), len); \ - p = (Buf); \ - while (len-- > 0) \ - { \ - ch = (version > 1 \ - ? decode_char (context, File, &advance_line) \ - : getc (File)); \ - if (ch == EOF) \ - m4_error (context, EXIT_FAILURE, 0, NULL, \ - _("premature end of frozen file")); \ - *p++ = ch; \ - } \ - *p = '\0'; \ - GET_CHARACTER; \ - while (version > 1 && character == '\\') \ - { \ - GET_CHARACTER; \ - VALIDATE ('\n'); \ - GET_CHARACTER; \ - } \ - } \ - while (0) - -#define VALIDATE(Expected) \ - do \ - { \ - if (character != (Expected)) \ - issue_expect_message (context, (Expected)); \ - } \ - while (0) - -#define CHECK_ALLOCATION(Where, Allocated, Needed) \ - do \ - { \ - if ((Needed) + 1 > (Allocated)) \ - { \ - free (Where); \ - (Allocated) = (Needed) + 1; \ - (Where) = xcharalloc (Allocated); \ - } \ - } \ - while (0) - - /* Skip comments (`#' at beginning of line) and blank lines, setting - character to the next directive or to EOF. */ - -#define GET_DIRECTIVE \ - do \ - { \ - GET_CHARACTER; \ - if (character == '#') \ - { \ - while (character != EOF && character != '\n') \ - GET_CHARACTER; \ - VALIDATE ('\n'); \ - } \ - } \ - while (character == '\n') - - filepath = m4_path_search (context, name, NULL); - file = m4_fopen (context, filepath, "r"); - if (file == NULL) - m4_error (context, EXIT_FAILURE, errno, NULL, _("cannot open %s"), - quotearg_style (locale_quoting_style, name)); - m4_set_current_file (context, name); - - allocated[0] = 100; - string[0] = xcharalloc (allocated[0]); - allocated[1] = 100; - string[1] = xcharalloc (allocated[1]); - allocated[2] = 100; - string[2] = xcharalloc (allocated[2]); - - /* Validate format version. Accept both `1' (m4 1.3 and 1.4.x) and - `2' (m4 2.0). */ - GET_DIRECTIVE; - VALIDATE ('V'); - GET_CHARACTER; - GET_NUMBER (version, false); - switch (version) - { - case 2: - break; - case 1: - m4__module_open (context, "m4", NULL); - if (m4_get_posixly_correct_opt (context)) - m4__module_open (context, "traditional", NULL); - else - m4__module_open (context, "gnu", NULL); - /* Disable { and } categories, since ${11} was not supported in - 1.4.x. */ - m4_set_syntax (M4SYNTAX, 'O', '+', "{}", 2); - break; - default: - if (version > 2) - m4_error (context, EXIT_MISMATCH, 0, NULL, - _("frozen file version %d greater than max supported of 2"), - version); - else - m4_error (context, EXIT_FAILURE, 0, NULL, - _("ill-formed frozen file, version directive expected")); - } - VALIDATE ('\n'); - - GET_DIRECTIVE; - while (character != EOF) - { - switch (character) - { - default: - m4_error (context, EXIT_FAILURE, 0, NULL, - _("ill-formed frozen file, unknown directive %c"), - character); - - case 'd': - /* Set debugmode flags. */ - if (version < 2) - { - /* 'd' operator is not supported in format version 1. */ - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, version 2 directive `%c' encountered"), 'd'); - } - - GET_CHARACTER; - GET_NUMBER (number[0], false); - VALIDATE ('\n'); - GET_STRING (file, string[0], allocated[0], number[0], false); - VALIDATE ('\n'); - - if (m4_debug_decode (context, string[0], number[0]) < 0) - m4_error (context, EXIT_FAILURE, 0, NULL, - _("unknown debug mode %s"), - quotearg_style_mem (locale_quoting_style, string[0], - number[0])); - break; - - case 'F': - GET_CHARACTER; - - /* Get string lengths. */ - - GET_NUMBER (number[0], false); - VALIDATE (','); - GET_CHARACTER; - GET_NUMBER (number[1], false); - - if (character == ',') - { - if (version > 1) - { - /* 'F' operator accepts an optional third argument for - format versions 2 or later. */ - GET_CHARACTER; - GET_NUMBER (number[2], false); - } - else - /* 3 argument 'F' operations are invalid for format - version 1. */ - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, version 2 directive `%c' encountered"), 'F'); - } - else - { - number[2] = 0; - } - - VALIDATE ('\n'); - - - /* Get string contents. */ - - GET_STRING (file, string[0], allocated[0], number[0], false); - if (version > 1) - { - VALIDATE ('\n'); - GET_CHARACTER; - } - GET_STRING (file, string[1], allocated[1], number[1], true); - if (version > 1 && number[2]) - { - VALIDATE ('\n'); - GET_CHARACTER; - } - GET_STRING (file, string[2], allocated[2], number[2], true); - VALIDATE ('\n'); - - /* Enter a macro having a builtin function as a definition. */ - { - m4_module *module = NULL; - m4_symbol_value *token; - - // Builtins cannot contain a NUL byte. - if (strlen (string[1]) < number[1]) - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, invalid builtin %s encountered"), - quotearg_style_mem (locale_quoting_style, string[1], - number[1])); - if (number[2] > 0) - { - if (strlen (string[2]) < number[2]) - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, invalid module %s encountered"), - quotearg_style_mem (locale_quoting_style, - string[2], number[2])); - module = m4__module_find (context, string[2]); - } - token = m4_builtin_find_by_name (context, module, string[1]); - - if (token == NULL) - { - token = (m4_symbol_value *) xzalloc (sizeof *token); - m4_set_symbol_value_placeholder (token, xstrdup (string[1])); - VALUE_MODULE (token) = module; - VALUE_MIN_ARGS (token) = 0; - VALUE_MAX_ARGS (token) = -1; - } - m4_symbol_pushdef (M4SYMTAB, string[0], number[0], token); - } - break; - - case 'M': - - /* Load a module, but *without* perturbing the symbol table. - Note that any expansion from loading the module which would - have been seen when loading it originally is discarded - when loading it from a frozen file. */ - - if (version < 2) - { - /* 'M' operator is not supported in format version 1. */ - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, version 2 directive `%c' encountered"), 'M'); - } - - GET_CHARACTER; - GET_NUMBER (number[0], false); - VALIDATE ('\n'); - GET_STRING (file, string[0], allocated[0], number[0], false); - VALIDATE ('\n'); - - if (strlen (string[0]) < number[0]) - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, invalid module %s encountered"), - quotearg_style_mem (locale_quoting_style, - string[0], number[0])); - m4__module_open (context, string[0], NULL); - - break; - - case 'R': - - if (version < 2) - { - /* 'R' operator is not supported in format version 1. */ - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, version 2 directive `%c' encountered"), 'R'); - } - - GET_CHARACTER; - GET_NUMBER (number[0], false); - VALIDATE ('\n'); - GET_STRING (file, string[0], allocated[0], number[0], false); - VALIDATE ('\n'); - - m4_set_regexp_syntax_opt (context, - m4_regexp_syntax_encode (string[0])); - if (m4_get_regexp_syntax_opt (context) < 0 - || strlen (string[0]) < number[0]) - { - m4_error (context, EXIT_FAILURE, 0, NULL, - _("bad syntax-spec %s"), - quotearg_style_mem (locale_quoting_style, string[0], - number[0])); - } - - break; - - case 'S': - - if (version < 2) - { - /* 'S' operator is not supported in format version 1. */ - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, version 2 directive `%c' encountered"), 'S'); - } - - GET_CHARACTER; - syntax = character; - GET_CHARACTER; - GET_NUMBER (number[0], false); - VALIDATE ('\n'); - GET_STRING (file, string[0], allocated[0], number[0], false); - - /* Syntax under M4_SYNTAX_MASKS is handled specially; all - other characters are additive. */ - if ((m4_set_syntax (M4SYNTAX, syntax, - (m4_syntax_code (syntax) & M4_SYNTAX_MASKS - ? '=' : '+'), string[0], number[0]) < 0) - && (syntax != '\0')) - { - m4_error (context, 0, 0, NULL, - _("undefined syntax code %c"), syntax); - } - break; - - case 't': - /* Trace a macro name. */ - if (version < 2) - { - /* 't' operator is not supported in format version 1. */ - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, version 2 directive `%c' encountered"), 't'); - } - - GET_CHARACTER; - GET_NUMBER (number[0], false); - VALIDATE ('\n'); - GET_STRING (file, string[0], allocated[0], number[0], false); - VALIDATE ('\n'); - - m4_set_symbol_name_traced (M4SYMTAB, string[0], number[0], true); - - break; - - case 'C': - case 'D': - case 'Q': - operation = character; - GET_CHARACTER; - - /* Get string lengths. */ - - if (operation == 'D' && character == '-') - { - /* Accept a negative diversion number. */ - GET_CHARACTER; - GET_NUMBER (number[0], true); - number[0] = -number[0]; - } - else - GET_NUMBER (number[0], false); - VALIDATE (','); - GET_CHARACTER; - GET_NUMBER (number[1], false); - VALIDATE ('\n'); - - /* Get string contents. */ - if (operation != 'D') - { - GET_STRING (file, string[0], allocated[0], number[0], false); - if (version > 1) - { - VALIDATE ('\n'); - GET_CHARACTER; - } - } - else - GET_CHARACTER; - GET_STRING (file, string[1], allocated[1], number[1], true); - VALIDATE ('\n'); - - /* Act according to operation letter. */ - - switch (operation) - { - case 'C': - - /* Change comment strings. */ - - m4_set_comment (M4SYNTAX, string[0], number[0], string[1], - number[1]); - break; - - case 'D': - - /* Select a diversion and add a string to it. */ - - m4_make_diversion (context, number[0]); - if (number[1] > 0) - m4_output_text (context, string[1], number[1]); - break; - - case 'Q': - - /* Change quote strings. */ - - m4_set_quotes (M4SYNTAX, string[0], number[0], string[1], - number[1]); - break; - - default: - - /* Cannot happen. */ - - break; - } - break; - - case 'T': - GET_CHARACTER; - - /* Get string lengths. */ - - GET_NUMBER (number[0], false); - VALIDATE (','); - GET_CHARACTER; - GET_NUMBER (number[1], false); - - if (character == ',') - { - if (version > 1) - { - /* 'T' operator accepts an optional third argument for - format versions 2 or later. */ - GET_CHARACTER; - GET_NUMBER (number[2], false); - } - else - { - /* 3 argument 'T' operations are invalid for format - version 1. */ - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, version 2 directive `%c' encountered"), 'T'); - } - } - else - number[2] = 0; - - VALIDATE ('\n'); - - /* Get string contents. */ - GET_STRING (file, string[0], allocated[0], number[0], false); - if (version > 1) - { - VALIDATE ('\n'); - GET_CHARACTER; - } - GET_STRING (file, string[1], allocated[1], number[1], true); - if (version > 1 && number[2]) - { - VALIDATE ('\n'); - GET_CHARACTER; - } - GET_STRING (file, string[2], allocated[2], number[2], true); - VALIDATE ('\n'); - - /* Enter a macro having an expansion text as a definition. */ - { - m4_symbol_value *token; - m4_module *module = NULL; - - token = (m4_symbol_value *) xzalloc (sizeof *token); - if (number[2] > 0) - { - if (strlen (string[2]) < number[2]) - m4_error (context, EXIT_FAILURE, 0, NULL, _("\ -ill-formed frozen file, invalid module %s encountered"), - quotearg_style_mem (locale_quoting_style, - string[2], number[2])); - module = m4__module_find (context, string[2]); - } - - m4_set_symbol_value_text (token, xmemdup0 (string[1], number[1]), - number[1], 0); - VALUE_MODULE (token) = module; - VALUE_MAX_ARGS (token) = -1; - - m4_symbol_pushdef (M4SYMTAB, string[0], number[0], token); - } - break; - - } - GET_DIRECTIVE; - } - - free (string[0]); - free (string[1]); - free (string[2]); - if (close_stream (file) != 0) - m4_error (context, EXIT_FAILURE, errno, NULL, - _("unable to read frozen state")); - m4_set_current_file (context, NULL); - m4_set_current_line (context, 0); - -#undef GET_STRING -#undef GET_CHARACTER -#undef GET_NUMBER -#undef VALIDATE -#undef CHECK_ALLOCATION -#undef GET_DIRECTIVE -} diff --git a/src/m4.h b/src/m4.h deleted file mode 100644 index acd8fa9b..00000000 --- a/src/m4.h +++ /dev/null @@ -1,43 +0,0 @@ -/* GNU m4 -- A simple macro processor - Copyright (C) 1989-1994, 2006-2007, 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/>. -*/ - -#ifndef M4_H -#define M4_H - -#include <signal.h> - -#include "m4private.h" - -#include "gettext.h" - -/* Error handling. */ -#ifdef USE_STACKOVF -void setup_stackovf_trap (char *const *, char *const *, - void (*handler) (void)); -void stackovf_exit (void); -#endif - - -/* File: freeze.c --- frozen state files. */ - -void produce_frozen_state (m4 *context, const char *); -void reload_frozen_state (m4 *context, const char *); - -#endif /* M4_H */ diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 4b897e34..00000000 --- a/src/main.c +++ /dev/null @@ -1,762 +0,0 @@ -/* GNU m4 -- A simple macro processor - - Copyright (C) 1989-1994, 1999-2000, 2003-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> - -#include <locale.h> - -#include "m4.h" - -#include "closein.h" -#include "configmake.h" -#include "getopt.h" -#include "propername.h" -#include "quotearg.h" -#include "version-etc.h" -#include "xstrtol.h" - -#define AUTHORS \ - proper_name_utf8 ("Rene' Seindal", "Ren\xc3\xa9 Seindal"), \ - proper_name ("Gary V. Vaughan"), \ - proper_name ("Eric Blake") - -typedef struct deferred -{ - struct deferred *next; - int code; /* deferred optchar */ - const char *value; -} deferred; - - -/* Error handling functions. */ - -#ifdef USE_STACKOVF - -/* Tell user stack overflowed and abort. */ -static void -stackovf_handler (void) -{ - /* FIXME - calling gettext and error inside a signal handler is dangerous, - since these functions invoke functions that are not signal-safe. We - are sort of justified by the fact that we will exit and never return, - but this should really be fixed. */ - error (EXIT_FAILURE, 0, _("stack overflow (infinite define recursion?)")); -} - -#endif /* USE_STACKOVF */ - - - -/* Print a usage message and exit with STATUS. */ -static void -usage (int status) -{ - if (status != EXIT_SUCCESS) - xfprintf (stderr, _("Try `%s --help' for more information.\n"), - m4_get_program_name ()); - else - { - xprintf (_("Usage: %s [OPTION]... [FILE]...\n"), m4_get_program_name ()); - fputs (_("\ -Process macros in FILEs.\n\ -If no FILE or if FILE is `-', standard input is read. If no FILE, and both\n\ -standard input and standard error are terminals, -i is implied.\n\ -"), stdout); - puts (""); - fputs (_("\ -Mandatory or optional arguments to long options are mandatory or optional\n\ -for short options too.\n\ -\n\ -Operation modes:\n\ - --help display this help and exit\n\ - --version output version information and exit\n\ -"), stdout); - fputs (_("\ - -b, --batch buffer output, process interrupts\n\ - -c, --discard-comments do not copy comments to the output\n\ - -E, --fatal-warnings once: warnings become errors, twice: stop\n\ - execution at first error\n\ - -i, --interactive unbuffer output, ignore interrupts\n\ - -P, --prefix-builtins force a `m4_' prefix to all builtins\n\ - -Q, --quiet, --silent suppress some warnings for builtins\n\ - -r, --regexp-syntax[=SPEC] set default regexp syntax to SPEC [GNU_M4]\n\ - --safer disable potentially unsafe builtins\n\ - -W, --warnings enable all warnings\n\ -"), stdout); - puts (""); - fputs (_("\ -SPEC is any one of:\n\ - AWK, BASIC, BSD_M4, ED, EMACS, EXTENDED, GNU_AWK, GNU_EGREP, GNU_M4,\n\ - GREP, POSIX_AWK, POSIX_EGREP, MINIMAL, MINIMAL_BASIC, SED.\n\ -"), stdout); - puts (""); - fputs (_("\ -Preprocessor features:\n\ - -B, --prepend-include=DIR add DIR to include path before `.'\n\ - -D, --define=NAME[=VALUE] define NAME as having VALUE, or empty\n\ - --import-environment import all environment variables as macros\n\ - -I, --include=DIR add DIR to include path after `.'\n\ -"), stdout); - fputs (_("\ - --popdef=NAME popdef NAME\n\ - -p, --pushdef=NAME[=VALUE] pushdef NAME as having VALUE, or empty\n\ - -s, --synclines short for --syncoutput=1\n\ - --syncoutput[=STATE] set generation of `#line NUM \"FILE\"' lines\n\ - to STATE (0=off, 1=on, default 0)\n\ - -U, --undefine=NAME undefine NAME\n\ -"), stdout); - puts (""); - fputs (_("\ -Limits control:\n\ - -g, --gnu override -G to re-enable GNU extensions\n\ - -G, --traditional, --posix suppress all GNU extensions\n\ - -L, --nesting-limit=NUMBER change artificial nesting limit [1024]\n\ -"), stdout); - puts (""); - fputs (_("\ -Frozen state files:\n\ - -F, --freeze-state=FILE produce a frozen state on FILE at end\n\ - -R, --reload-state=FILE reload a frozen state from FILE at start\n\ -"), stdout); - puts (""); - fputs (_("\ -Debugging:\n\ - -d, --debug[=[-|+]FLAGS], --debugmode[=[-|+]FLAGS]\n\ - set debug level (no FLAGS implies `+adeq')\n\ - --debugfile[=FILE] redirect debug and trace output to FILE\n\ - (default stderr, discard if empty string)\n\ - -l, --debuglen=NUM restrict macro tracing size\n\ - -t, --trace=NAME, --traceon=NAME\n\ - trace NAME when it is defined\n\ - --traceoff=NAME no longer trace NAME\n\ -"), stdout); - puts (""); - fputs (_("\ -FLAGS is any of:\n\ - a show actual arguments in trace\n\ - c show collection line in trace\n\ - d warn when dereferencing undefined macros (default on unless -E)\n\ - e show expansion in trace\n\ - f include current input file name in trace and debug\n\ - i show changes in input files in debug\n\ - l include current input line number in trace and debug\n\ -"), stdout); - fputs (_("\ - m show module information in trace, debug, and dumpdef\n\ - o output dumpdef to stderr rather than debug file\n\ - p show results of path searches in debug\n\ - q quote values in dumpdef and trace, useful with a or e\n\ - s show full stack of pushdef values in dumpdef\n\ - t trace all macro calls, regardless of per-macro traceon state\n\ - x include unique macro call id in trace, useful with c\n\ - V shorthand for all of the above flags\n\ -"), stdout); - puts (""); - fputs (_("\ -If defined, the environment variable `M4PATH' is a colon-separated list\n\ -of directories included after any specified by `-I' or `-B'. The\n\ -environment variable `POSIXLY_CORRECT' implies -G -Q; otherwise GNU\n\ -extensions are enabled by default.\n\ -"), stdout); - puts (""); - fputs (_("\ -Exit status is 0 for success, 1 for failure, 63 for frozen file version\n\ -mismatch, or whatever value was passed to the m4exit macro.\n\ -"), stdout); - emit_bug_reporting_address (); - } - exit (status); -} - -/* For long options that have no equivalent short option, use a - non-character as a pseudo short option, starting with CHAR_MAX + 1. */ -enum -{ - ARGLENGTH_OPTION = CHAR_MAX + 1, /* not quite -l, because of message */ - DEBUGFILE_OPTION, /* no short opt */ - ERROR_OUTPUT_OPTION, /* not quite -o, because of message */ - HASHSIZE_OPTION, /* not quite -H, because of message */ - IMPORT_ENVIRONMENT_OPTION, /* no short opt */ - POPDEF_OPTION, /* no short opt */ - PREPEND_INCLUDE_OPTION, /* not quite -B, because of message */ - SAFER_OPTION, /* -S still has old no-op semantics */ - SYNCOUTPUT_OPTION, /* not quite -s, because of opt arg */ - TRACEOFF_OPTION, /* no short opt */ - WORD_REGEXP_OPTION, /* deprecated, used to be -W */ - - HELP_OPTION, /* no short opt */ - VERSION_OPTION /* no short opt */ -}; - -/* Decode options and launch execution. */ -static const struct option long_options[] = -{ - {"batch", no_argument, NULL, 'b'}, - {"debug", optional_argument, NULL, 'd'}, - {"debuglen", required_argument, NULL, 'l'}, - {"debugmode", optional_argument, NULL, 'd'}, - {"define", required_argument, NULL, 'D'}, - {"discard-comments", no_argument, NULL, 'c'}, - {"fatal-warnings", no_argument, NULL, 'E'}, - {"freeze-state", required_argument, NULL, 'F'}, - {"gnu", no_argument, NULL, 'g'}, - {"include", required_argument, NULL, 'I'}, - {"interactive", no_argument, NULL, 'i'}, - {"nesting-limit", required_argument, NULL, 'L'}, - {"posix", no_argument, NULL, 'G'}, - {"prefix-builtins", no_argument, NULL, 'P'}, - {"pushdef", required_argument, NULL, 'p'}, - {"quiet", no_argument, NULL, 'Q'}, - {"regexp-syntax", optional_argument, NULL, 'r'}, - {"reload-state", required_argument, NULL, 'R'}, - {"silent", no_argument, NULL, 'Q'}, - {"synclines", no_argument, NULL, 's'}, - {"trace", required_argument, NULL, 't'}, - {"traceon", required_argument, NULL, 't'}, - {"traditional", no_argument, NULL, 'G'}, - {"undefine", required_argument, NULL, 'U'}, - {"warnings", no_argument, NULL, 'W'}, - - {"arglength", required_argument, NULL, ARGLENGTH_OPTION}, - {"debugfile", optional_argument, NULL, DEBUGFILE_OPTION}, - {"hashsize", required_argument, NULL, HASHSIZE_OPTION}, - {"error-output", required_argument, NULL, ERROR_OUTPUT_OPTION}, - {"import-environment", no_argument, NULL, IMPORT_ENVIRONMENT_OPTION}, - {"popdef", required_argument, NULL, POPDEF_OPTION}, - {"prepend-include", required_argument, NULL, PREPEND_INCLUDE_OPTION}, - {"safer", no_argument, NULL, SAFER_OPTION}, - {"syncoutput", optional_argument, NULL, SYNCOUTPUT_OPTION}, - {"traceoff", required_argument, NULL, TRACEOFF_OPTION}, - {"word-regexp", required_argument, NULL, WORD_REGEXP_OPTION}, - - {"help", no_argument, NULL, HELP_OPTION}, - {"version", no_argument, NULL, VERSION_OPTION}, - - { NULL, 0, NULL, 0 }, -}; - -/* POSIX requires only -D, -U, and -s; and says that the first two - must be recognized when interspersed with file names. Traditional - behavior also handles -s between files. Starting OPTSTRING with - '-' forces getopt_long to hand back file names as arguments to opt - '\1', rather than reordering the command line. */ -#define OPTSTRING "-B:D:EF:GH:I:L:PQR:S:T:U:Wbcd::egil:o:p:r::st:" - -/* For determining whether to be interactive. */ -enum interactive_choice -{ - INTERACTIVE_UNKNOWN, /* Still processing arguments, no -b or -i yet */ - INTERACTIVE_YES, /* -i specified last */ - INTERACTIVE_NO /* -b specified last */ -}; - -/* Convert OPT to size_t, reporting an error using long option index - OI or short option character OPTCHAR if it does not fit. */ -static size_t -size_opt (char const *opt, int oi, int optchar) -{ - unsigned long int size; - strtol_error status = xstrtoul (opt, NULL, 10, &size, "kKmMgGtTPEZY0"); - if (SIZE_MAX < size && status == LONGINT_OK) - status = LONGINT_OVERFLOW; - if (status != LONGINT_OK) - xstrtol_fatal (status, oi, optchar, long_options, opt); - return size; -} - -/* Process a command line file NAME. */ -static bool -process_file (m4 *context, const char *name) -{ - bool new_input = true; - - if (STREQ (name, "-")) - /* TRANSLATORS: This is a short name for `standard input', used - when a command line file was given as `-'. */ - m4_push_file (context, stdin, _("stdin"), false); - else - new_input = m4_load_filename (context, NULL, name, NULL, false); - - if (new_input) - m4_macro_expand_input (context); - - return new_input; -} - - -/* Main entry point. Parse arguments, load modules, then parse input. */ -int -main (int argc, char *const *argv, char *const *envp) -{ - deferred *head = NULL; /* head of deferred argument list */ - deferred *tail = NULL; - deferred *defn; - size_t size; /* for parsing numeric option arguments */ - - bool import_environment = false; /* true to import environment */ - bool seen_file = false; - const char *debugfile = NULL; - const char *frozen_file_to_read = NULL; - const char *frozen_file_to_write = NULL; - enum interactive_choice interactive = INTERACTIVE_UNKNOWN; - - m4 *context; - - int exit_status; - - /* Initialize gnulib error module. */ - m4_set_program_name (argv[0]); - atexit (close_stdin); - - setlocale (LC_ALL, ""); -#ifdef ENABLE_NLS - textdomain (PACKAGE); -#endif - - context = m4_create (); - -#ifdef USE_STACKOVF - setup_stackovf_trap (argv, envp, stackovf_handler); -#endif - - if (getenv ("POSIXLY_CORRECT")) - { - m4_set_posixly_correct_opt (context, true); - m4_set_suppress_warnings_opt (context, true); - } - set_quoting_style (NULL, escape_quoting_style); - set_char_quoting (NULL, ':', 1); - - /* First, we decode the arguments, to size up tables and stuff. - Avoid lasting side effects; for example 'm4 --debugfile=oops - --help' must not create the file `oops'. */ - while (1) - { - int oi = -1; - int optchar = getopt_long (argc, (char **) argv, OPTSTRING, - long_options, &oi); - if (optchar == -1) - break; - - switch (optchar) - { - default: - usage (EXIT_FAILURE); - - case 'H': - case HASHSIZE_OPTION: - /* -H was supported in 1.4.x, but is a no-op now. FIXME - - remove support for -H after 2.0. */ - error (0, 0, _("warning: `%s' is deprecated"), - optchar == 'H' ? "-H" : "--hashsize"); - break; - - case 'S': - case 'T': - /* Compatibility junk: options that other implementations - support, but which we ignore as no-ops and don't list in - --help. */ - error (0, 0, _("warning: `-%c' is deprecated"), - optchar); - break; - - case WORD_REGEXP_OPTION: - /* Supported in 1.4.x as -W, but no longer present. */ - error (0, 0, _("warning: `%s' is deprecated"), "--word-regexp"); - break; - - case 's': - optchar = SYNCOUTPUT_OPTION; - optarg = "1"; - /* fall through */ - case 'D': - case 'U': - case 'p': - case 'r': - case 't': - case POPDEF_OPTION: - case SYNCOUTPUT_OPTION: - case TRACEOFF_OPTION: - defer: - /* Arguments that cannot be handled until later are accumulated. */ - - defn = (deferred *) xmalloc (sizeof *defn); - defn->code = optchar; - defn->value = optarg; - defn->next = NULL; - - if (head == NULL) - head = defn; - else - tail->next = defn; - tail = defn; - break; - - case '\1': - seen_file = true; - goto defer; - - case 'B': - /* In 1.4.x, -B<num> was a no-op option for compatibility with - Solaris m4. Warn if optarg is all numeric. FIXME - - silence this warning after 2.0. */ - if (isdigit (to_uchar (*optarg))) - { - char *end; - errno = 0; - strtol (optarg, &end, 10); - if (*end == '\0' && errno == 0) - error (0, 0, _("warning: recommend using `-B ./%s' instead"), - optarg); - } - /* fall through */ - case PREPEND_INCLUDE_OPTION: - m4_add_include_directory (context, optarg, true); - break; - - case 'E': - m4_debug_decode (context, "-d", SIZE_MAX); - if (m4_get_fatal_warnings_opt (context)) - m4_set_warnings_exit_opt (context, true); - else - m4_set_fatal_warnings_opt (context, true); - break; - - case 'F': - frozen_file_to_write = optarg; - break; - - case 'G': - m4_set_posixly_correct_opt (context, true); - break; - - case 'I': - m4_add_include_directory (context, optarg, false); - break; - - case 'L': - size = size_opt (optarg, oi, optchar); - if (!size) - size = SIZE_MAX; - m4_set_nesting_limit_opt (context, size); - break; - - case 'P': - m4_set_prefix_builtins_opt (context, true); - break; - - case 'Q': - m4_set_suppress_warnings_opt (context, true); - break; - - case 'R': - frozen_file_to_read = optarg; - break; - - case 'W': - /* FIXME - should W take an optional argument, to allow -Wall, - -Wnone, -Werror, -Wcategory, -Wno-category? If so, then have - -W == -Wall. */ - m4_set_suppress_warnings_opt (context, false); - break; - - case 'b': - interactive = INTERACTIVE_NO; - break; - - case 'c': - m4_set_discard_comments_opt (context, true); - break; - - case 'd': - /* Staggered handling of 'd', since -dm is useful prior to - first file and prior to reloading, but other -d must also - have effect between files. */ - if (seen_file || frozen_file_to_read) - goto defer; - if (m4_debug_decode (context, optarg, SIZE_MAX) < 0) - error (0, 0, _("bad debug flags: %s"), - quotearg_style (locale_quoting_style, optarg)); - break; - - case 'e': - error (0, 0, _("warning: `%s' is deprecated, use `%s' instead"), - "-e", "-i"); - /* fall through */ - case 'i': - interactive = INTERACTIVE_YES; - break; - - case 'g': - m4_set_posixly_correct_opt (context, false); - break; - - case ARGLENGTH_OPTION: - error (0, 0, _("warning: `%s' is deprecated, use `%s' instead"), - "--arglength", "--debuglen"); - /* fall through */ - case 'l': - size = size_opt (optarg, oi, optchar); - if (!size) - size = SIZE_MAX; - m4_set_max_debug_arg_length_opt (context, size); - break; - - case DEBUGFILE_OPTION: - /* Staggered handling of '--debugfile', since it is useful - prior to first file and prior to reloading, but other - uses must also have effect between files. */ - if (seen_file || frozen_file_to_read) - goto defer; - debugfile = optarg; - break; - - case 'o': - case ERROR_OUTPUT_OPTION: - /* FIXME: -o is inconsistent with other tools' use of - -o/--output for creating an output file instead of using - stdout, and --error-output is misnamed since it does not - affect error messages to stderr. Change the meaning of -o - after 2.1. */ - error (0, 0, _("warning: `%s' is deprecated, use `%s' instead"), - optchar == 'o' ? "-o" : "--error-output", "--debugfile"); - /* Don't call m4_debug_set_output here, as it has side effects. */ - debugfile = optarg; - break; - - case IMPORT_ENVIRONMENT_OPTION: - import_environment = true; - break; - - case SAFER_OPTION: - m4_set_safer_opt (context, true); - break; - - case VERSION_OPTION: - version_etc (stdout, PACKAGE, PACKAGE_NAME, VERSION, AUTHORS, NULL); - exit (EXIT_SUCCESS); - break; - - case HELP_OPTION: - usage (EXIT_SUCCESS); - break; - } - } - - /* Do the basic initializations. */ - if (debugfile && !m4_debug_set_output (context, NULL, debugfile)) - m4_error (context, 0, errno, NULL, _("cannot set debug file %s"), - quotearg_style (locale_quoting_style, debugfile)); - m4_input_init (context); - m4_output_init (context); - - if (frozen_file_to_read) - reload_frozen_state (context, frozen_file_to_read); - else - { - m4_module_load (context, "m4", NULL); - if (m4_get_posixly_correct_opt (context)) - m4_module_load (context, "traditional", NULL); - else - m4_module_load (context, "gnu", NULL); - } - - /* Import environment variables as macros. The definition are - prepended to the macro definition list, so -U can override - environment variables. */ - - if (import_environment) - { - char *const *env; - - for (env = envp; *env != NULL; env++) - { - defn = (deferred *) xmalloc (sizeof *defn); - defn->code = 'D'; - defn->value = *env; - defn->next = head; - head = defn; - } - } - - /* Handle deferred command line macro definitions. Must come after - initialization of the symbol table. */ - defn = head; - while (defn != NULL) - { - deferred *next; - const char *arg = defn->value; - - switch (defn->code) - { - case 'D': - case 'p': - { - m4_symbol_value *value = m4_symbol_value_create (); - - const char *str = strchr (arg, '='); - size_t len = str ? str - arg : strlen (arg); - - m4_set_symbol_value_text (value, xstrdup (str ? str + 1 : ""), - str ? strlen (str + 1) : 0, 0); - - if (defn->code == 'D') - m4_symbol_define (M4SYMTAB, arg, len, value); - else - m4_symbol_pushdef (M4SYMTAB, arg, len, value); - } - break; - - case 'U': - m4_symbol_delete (M4SYMTAB, arg, strlen (arg)); - break; - - case 'd': - if (m4_debug_decode (context, arg, SIZE_MAX) < 0) - error (0, 0, _("bad debug flags: %s"), - quotearg_style (locale_quoting_style, arg)); - break; - - case 'r': - m4_set_regexp_syntax_opt (context, m4_regexp_syntax_encode (arg)); - if (m4_get_regexp_syntax_opt (context) < 0) - m4_error (context, EXIT_FAILURE, 0, NULL, - _("bad syntax-spec: %s"), - quotearg_style (locale_quoting_style, arg)); - break; - - case 't': - m4_set_symbol_name_traced (M4SYMTAB, arg, strlen (arg), true); - break; - - case '\1': - if (process_file (context, arg)) - seen_file = true; - break; - - case DEBUGFILE_OPTION: - if (!m4_debug_set_output (context, NULL, arg)) - m4_error (context, 0, errno, NULL, _("cannot set debug file %s"), - quotearg_style (locale_quoting_style, - arg ? arg : _("stderr"))); - break; - - case POPDEF_OPTION: - { - size_t len = strlen (arg); - if (m4_symbol_lookup (M4SYMTAB, arg, len)) - m4_symbol_popdef (M4SYMTAB, arg, len); - } - break; - - case SYNCOUTPUT_OPTION: - { - bool previous = m4_get_syncoutput_opt (context); - m4_call_info info = {0}; - info.name = "--syncoutput"; - info.name_len = strlen (info.name); - m4_set_syncoutput_opt (context, - m4_parse_truth_arg (context, &info, arg, - SIZE_MAX, previous)); - } - break; - - case TRACEOFF_OPTION: - m4_set_symbol_name_traced (M4SYMTAB, arg, strlen (arg), false); - break; - - default: - assert (!"INTERNAL ERROR: bad code in deferred arguments"); - abort (); - } - - next = defn->next; - free (defn); - defn = next; - } - - - /* Interactive if specified, or if no input files and stdin and - stderr are terminals, to match sh behavior. Interactive mode - means unbuffered output, and interrupts ignored. */ - - m4_set_interactive_opt (context, (interactive == INTERACTIVE_YES - || (interactive == INTERACTIVE_UNKNOWN - && optind == argc && !seen_file - && isatty (STDIN_FILENO) - && isatty (STDERR_FILENO)))); - if (m4_get_interactive_opt (context)) - { - signal (SIGINT, SIG_IGN); - setbuf (stdout, NULL); - } - else - signal (SIGPIPE, SIG_DFL); - - - /* Handle remaining input files. Each file is pushed on the input, - and the input read. */ - - if (optind == argc && !seen_file) - process_file (context, "-"); - else - for (; optind < argc; optind++) - process_file (context, argv[optind]); - - /* Now handle wrapup text. - FIXME - when -F is in effect, should wrapped text be frozen? */ - while (m4_pop_wrapup (context)) - m4_macro_expand_input (context); - - if (frozen_file_to_write) - produce_frozen_state (context, frozen_file_to_write); - else - { - m4_make_diversion (context, 0); - m4_undivert_all (context); - } - - /* The remaining cleanup functions systematically free all of the - memory we still have pointers to. By definition, if there is - anything left when we're done: it was caused by a memory leak. - Strictly, we don't need to do this, but it makes leak detection - a whole lot easier! */ - - m4_output_exit (); - m4_input_exit (); - - /* Change debug stream back to stderr, to force flushing the debug - stream and detect any errors it might have encountered. The - three standard streams are closed by close_stdin. */ - m4_debug_set_output (context, NULL, NULL); - - exit_status = m4_get_exit_status (context); - m4_delete (context); - - m4_hash_exit (); - quotearg_free (); - -#ifdef USE_STACKOVF - stackovf_exit (); -#endif - - exit (exit_status); -} diff --git a/src/stackovf.c b/src/stackovf.c deleted file mode 100644 index 3d0468ab..00000000 --- a/src/stackovf.c +++ /dev/null @@ -1,454 +0,0 @@ -/* Detect stack overflow (when getrlimit and sigaction or sigvec are available) - Copyright (C) 1993-1994, 2006-2007, 2010, 2013-2014, 2017 Free - Software Foundation, Inc. - Jim Avera <jima@netcom.com>, October 1993. - - 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/>. - */ - -/* Compiled only when USE_STACKOVF is defined, which itself requires - getrlimit with the RLIMIT_STACK option, and support for alternate - signal stacks using either SVR4 or BSD interfaces. - - This should compile on ANY system which supports either sigaltstack() - or sigstack(), with or without <siginfo.h> or another way to determine - the fault address. - - There is no completely portable way to determine if a SIGSEGV signal - indicates a stack overflow. The fault address can be used to infer - this. However, the fault address is passed to the signal handler in - different ways on various systems. One of three methods are used to - determine the fault address: - - 1. The siginfo parameter (with siginfo.h, i.e., SVR4). - - 2. 4th "addr" parameter (assumed if struct sigcontext is defined, - i.e., SunOS 4.x/BSD). - - 3. None (if no method is available). This case just prints a - message before aborting with a core dump. That way the user at - least knows that it *might* be a recursion problem. - - Jim Avera <jima@netcom.com> writes, on Tue, 5 Oct 93 19:27 PDT: - - "I got interested finding out how a program could catch and - diagnose its own stack overflow, and ended up modifying m4 to do - this. Now it prints a nice error message and exits. - - How it works: SIGSEGV is caught using a separate signal stack. The - signal handler declares a stack overflow if the fault address is - near the end of the stack region, or if the maximum VM address - space limit has been reached. Otherwise, it returns to re-execute - the instruction with SIG_DFL set, so that any real bugs cause a - core dump as usual." - - Jim Avera <jima@netcom.com> writes, on Fri, 24 Jun 94 12:14 PDT: - - "The stack-overflow detection code would still be needed to avoid a - SIGSEGV abort if swap space was exhausted at the moment the stack - tried to grow. This is probably unlikely to occur with the - explicit nesting limit option of GNU m4." - - Jim Avera <jima@netcom.com> writes, on Wed, 6 Jul 1994 14:41 PDT: - - "When a stack overflow occurs, a SIGSEGV signal is sent, which by - default aborts the process with a core dump. - - The code in stackovf.c catches SIGSEGV using a separate signal - stack. The signal handler determines whether or not the SIGSEGV - arose from a stack overflow. If it is a stack overflow, an - external function is called (which, in m4, prints a message an - exits). Otherwise the SIGSEGV represents an m4 bug, and the signal - is re-raised with SIG_DFL set, which results in an abort and core - dump in the usual way. It seems important (to me) that internal m4 - bugs not be reported as user recursion errors, or vice-versa." */ - -/* Define this to see runtime debug info. Implied by DEBUG. */ -/*#define DEBUG_STKOVF */ - -#include <config.h> - -#include "m4.h" - -#ifdef USE_STACKOVF - -#include <sys/time.h> -#include <sys/resource.h> - -#if HAVE_SIGINFO_H -# include <siginfo.h> -#endif - -#ifndef SA_RESETHAND -# define SA_RESETHAND 0 -#endif -#ifndef SA_SIGINFO -# define SA_SIGINFO 0 -#endif - -#ifndef SIGSTKSZ -# define SIGSTKSZ 8192 -#endif - -/* If the trap address is within STACKOVF_DETECT bytes of the calculated - stack limit, we diagnose a stack overflow. This must be large enough - to cover errors in our estimatation of the limit address, and to - account for the maximum size of local variables (the amount the - trapping reference might exceed the stack limit). Also, some machines - may report an arbitrary address within the same page frame. - If the value is too large, we might call some other SIGSEGV a stack - overflow, masking a bug. */ - -#ifndef STACKOVF_DETECT -# define STACKOVF_DETECT 16384 -#endif - -typedef void (*handler_t) (void); - -#if defined __ultrix && defined __vax -extern char *sbrk (int); -extern int getrlimit (int, struct rlimit *); -extern int sigstack (struct sigstack *, struct sigstack *); -extern int sigvec (int, struct sigvec *, struct sigvec *); -#endif - -static void *stackbuf; -static const char *stackbot; -static const char *stackend; -static const char *arg0; -static handler_t stackovf_handler; - -/* The following OS-independent procedure is called from the SIGSEGV - signal handler. The signal handler obtains information about the trap - in an OS-dependent manner, and passes a parameter with the meanings as - explained below. - - If the OS explicitly identifies a stack overflow trap, either pass - PARAM_STACKOVF if a stack overflow, or pass PARAM_NOSTACKOVF if not - (id est, it is a random bounds violation). Otherwise, if the fault - address is available, pass the fault address. Otherwise (if no - information is available), pass NULL. - - Not given an explicit indication, we compare the fault address with - the estimated stack limit, and test to see if overall VM space is - exhausted. - - If a stack overflow is identified, then the external *stackovf_handler - function is called, which should print an error message and exit. If - it is NOT a stack overflow, then we silently abort with a core dump by - returning to re-raise the SIGSEGV with SIG_DFL set. If indeterminate, - then we do not call *stackovf_handler, but instead print an ambiguous - message and abort with a core dump. This only occurs on systems which - provide no information, but is better than nothing. */ - -#define PARAM_STACKOVF ((const char *) (1 + STACKOVF_DETECT)) -#define PARAM_NOSTACKOVF ((const char *) (2 + STACKOVF_DETECT)) - -static void -process_sigsegv (int signo, const char *p) -{ - ptrdiff_t diff; - diff = (p - stackend); - -#ifdef DEBUG_STKOVF - { - char buf[200]; - - sprintf (buf, - "process_sigsegv: p=%p stackend=%p diff=%" PRIdPTR "bot=%p\n", - p, stackend, diff, stackbot); - write (2, buf, strlen (buf)); - } -#endif - - if (p != PARAM_NOSTACKOVF) - { - if ((long) sbrk (8192) == (long) -1) - { - const char *cp; - - /* sbrk failed. Assume the RLIMIT_VMEM prevents expansion even - if the stack limit has not been reached. */ - - /* FIXME - calling gettext inside a signal handler is - dangerous, since it can call malloc, which is not signal - safe. We can sort of justify it by the fact that this - handler is designed to exit() the program, but it could - really use a better fix. */ - cp = _("VMEM limit exceeded?\n"); - write (2, cp, strlen (cp)); - p = PARAM_STACKOVF; - } - if (diff >= -STACKOVF_DETECT && diff <= STACKOVF_DETECT) - { - - /* The fault address is "sufficiently close" to the stack lim. */ - - p = PARAM_STACKOVF; - } - if (p == PARAM_STACKOVF) - { - - /* We have determined that this is indeed a stack overflow. */ - - (*stackovf_handler) (); /* should call exit() */ - } - } - if (p == NULL) - { - const char *cp; - - /* FIXME - calling gettext inside a signal handler is dangerous, - since it can call malloc, which is not signal safe. */ - cp = _("\ -Memory bounds violation detected (SIGSEGV). Either a stack overflow\n\ -occurred, or there is a bug in "); - write (2, cp, strlen (cp)); - write (2, arg0, strlen (arg0)); - cp = _(". Check for possible infinite recursion.\n"); - write (2, cp, strlen (cp)); - } - - /* Return to re-execute the instruction which caused the trap with - SIGSEGV set to SIG_DFL. An abort with core dump should occur. */ - - signal (signo, SIG_DFL); -} - -#if HAVE_STRUCT_SIGACTION_SA_SIGACTION - -/* POSIX. */ - -static void -sigsegv_handler (int signo, siginfo_t *ip, void *context) -{ - process_sigsegv - (signo, (ip != NULL - && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL)); -} - -#elif HAVE_SIGINFO_T - -/* SVR4. */ - -static void -sigsegv_handler (int signo, siginfo_t *ip) -{ - process_sigsegv - (signo, (ip != NULL - && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL)); -} - -#elif HAVE_SIGCONTEXT - -/* SunOS 4.x (and BSD?). (not tested) */ - -static void -sigsegv_handler (int signo, int code, struct sigcontext *scp, char *addr) -{ - process_sigsegv (signo, addr); -} - -#else /* not HAVE_SIGCONTEXT */ - -/* OS provides no information. */ - -static void -sigsegv_handler (int signo) -{ - process_sigsegv (signo, NULL); -} - -#endif /* not HAVE_SIGCONTEXT */ - -/* Arrange to trap a stack-overflow and call a specified handler. The - call is on a dedicated signal stack. - - argv and envp are as passed to main. - - If a stack overflow is not detected, then the SIGSEGV is re-raised - with action set to SIG_DFL, causing an abort and coredump in the usual - way. - - Detection of a stack overflow depends on the trap address being near - the stack limit address. The stack limit cannot be directly - determined in a portable way, but we make an estimate based on the - address of the argv and environment vectors, their contents, and the - maximum stack size obtained using getrlimit. */ - -void -setup_stackovf_trap (char *const *argv, char *const *envp, handler_t handler) -{ - struct rlimit rl; - rlim_t stack_len; - int grows_upward; - register char *const *v; - register char *p; -#if HAVE_SIGACTION && defined SA_ONSTACK - struct sigaction act; -#elif HAVE_SIGVEC && defined SV_ONSTACK - struct sigvec vec; -#else - -Error - Do not know how to set up stack-ovf trap handler... - -#endif - - arg0 = argv[0]; - stackovf_handler = handler; - - /* Calculate the approximate expected addr for a stack-ovf trap. */ - - if (getrlimit (RLIMIT_STACK, &rl) < 0) - error (EXIT_FAILURE, errno, _("getrlimit")); - stack_len = (rl.rlim_cur < rl.rlim_max ? rl.rlim_cur : rl.rlim_max); - stackbot = (char *) argv; - grows_upward = ((char *) &stack_len > stackbot); - if (grows_upward) - { - - /* Grows toward increasing addresses. */ - - for (v = argv; (p = (char *) *v) != NULL; v++) - { - if (p < stackbot) - stackbot = p; - } - if ((char *) envp < stackbot) - stackbot = (char *) envp; - for (v = envp; (p = (char *) *v) != NULL; v++) - { - if (p < stackbot) - stackbot = p; - } - stackend = stackbot + stack_len; - } - else - { - - /* The stack grows "downward" (toward decreasing addresses). */ - - for (v = argv; (p = (char *) *v) != NULL; v++) - { - if (p > stackbot) - stackbot = p; - } - if ((char *) envp > stackbot) - stackbot = (char *) envp; - for (v = envp; (p = (char *) *v) != NULL; v++) - { - if (p > stackbot) - stackbot = p; - } - stackend = stackbot - stack_len; - } - - /* Allocate a separate signal-handler stack. */ - -#if HAVE_SIGALTSTACK && (HAVE_SIGINFO_T || ! HAVE_SIGSTACK) - - /* Use sigaltstack only if siginfo_t is available, unless there is no - choice. */ - - { - stack_t ss; -# ifndef HAVE_STACK_T_SS_SP - /* This workaround is for BSD/OS 4.0.1: - http://lists.gnu.org/archive/html/bug-m4/2006-12/msg00004.html */ -# define ss_sp ss_base -# endif /* ! HAVE_STACK_T_SS_SP */ - - stackbuf = xmalloc (SIGSTKSZ); - - ss.ss_size = SIGSTKSZ; - ss.ss_sp = stackbuf; - ss.ss_flags = 0; - if (sigaltstack (&ss, NULL) < 0) - { - /* Oops - sigstack exists but doesn't work. We can't install - the overflow detector, but should gracefully treat it as - though sigstack doesn't exist. For example, this happens - when compiled with Linux 2.1 headers but run against Linux - 2.0 kernel. */ - free (stackbuf); - if (errno == ENOSYS) - return; - error (EXIT_FAILURE, errno, _("sigaltstack")); - } - } - -#elif HAVE_SIGSTACK - - { - struct sigstack ss; - stackbuf = xmalloc (2 * SIGSTKSZ); - - ss.ss_sp = stackbuf + SIGSTKSZ; - ss.ss_onstack = 0; - if (sigstack (&ss, NULL) < 0) - { - /* Oops - sigstack exists but doesn't work. We can't install - the overflow detector, but should gracefully treat it as - though sigstack doesn't exist. For example, this happens - when compiled with Linux 2.1 headers but run against Linux - 2.0 kernel. */ - free (stackbuf); - if (errno == ENOSYS) - return; - error (EXIT_FAILURE, errno, _("sigstack")); - } - } - -#else /* not HAVE_SIGSTACK */ - -Error - Do not know how to set up stack-ovf trap handler... - -#endif /* not HAVE_SIGSTACK */ - - /* Arm the SIGSEGV signal handler. */ - -#if HAVE_SIGACTION && defined SA_ONSTACK - - sigaction (SIGSEGV, NULL, &act); -# if HAVE_STRUCT_SIGACTION_SA_SIGACTION - act.sa_sigaction = sigsegv_handler; -# else /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */ - act.sa_handler = (RETSIGTYPE (*) (int)) sigsegv_handler; -# endif /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */ - sigemptyset (&act.sa_mask); - act.sa_flags = (SA_ONSTACK | SA_RESETHAND | SA_SIGINFO); - if (sigaction (SIGSEGV, &act, NULL) < 0) - error (EXIT_FAILURE, errno, _("sigaction")); - -#else /* ! HAVE_SIGACTION */ - - vec.sv_handler = (RETSIGTYPE (*) (int)) sigsegv_handler; - vec.sv_mask = 0; - vec.sv_flags = (SV_ONSTACK | SV_RESETHAND); - if (sigvec (SIGSEGV, &vec, NULL) < 0) - error (EXIT_FAILURE, errno, _("sigvec")); - -#endif /* ! HAVE_SIGACTION */ - -} - -void -stackovf_exit (void) -{ - DELETE (stackbuf); -} - -#endif /* USE_STACKOVF */ |