diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 762 |
1 files changed, 0 insertions, 762 deletions
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); -} |