From 1b6af058955698bbe97dab699a208ece8c0493ca Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 8 Nov 2006 19:06:00 +0000 Subject: [PATCH] Merge deferred handling of -D option from branch. * doc/m4.texinfo (Debugging options, Preprocessor features) (Dynamic loading features, Operation modes, Invoking m4): Document this change. * src/main.c (OPTSTRING): Specify in-order processing. (process_file): New function. (main): Use it to interleave files and deferred options. * tests/macros.at (Command line define): New test. * tests/generate.awk: Allow '@comment file' as first example within a node. * tests/options.at (option grouping): Update to reflect actual POSIX semantics. (file names): New test. --- ChangeLog | 16 +++++++ doc/m4.texinfo | 61 ++++++++++++++++++------ src/main.c | 135 +++++++++++++++++++++++++++++------------------------ tests/generate.awk | 3 +- tests/macros.at | 35 ++++++++++++++ tests/options.at | 45 +++++++++++++++++- 6 files changed, 217 insertions(+), 78 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3b5adee8..91d49915 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2006-11-08 Eric Blake + + Merge deferred handling of -D option from branch. + * doc/m4.texinfo (Debugging options, Preprocessor features) + (Dynamic loading features, Operation modes, Invoking m4): + Document this change. + * src/main.c (OPTSTRING): Specify in-order processing. + (process_file): New function. + (main): Use it to interleave files and deferred options. + * tests/macros.at (Command line define): New test. + * tests/generate.awk: Allow '@comment file' as first example + within a node. + * tests/options.at (option grouping): Update to reflect actual + POSIX semantics. + (file names): New test. + 2006-11-07 Eric Blake * m4/output.c (cleanup_tmpfile, m4_insert_diversion_helper): Check diff --git a/doc/m4.texinfo b/doc/m4.texinfo index 85022c2d..dfae63ee 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -537,19 +537,22 @@ The format of the @code{m4} command is: @cindex @env{POSIXLY_CORRECT} All options begin with @samp{-}, or if long option names are used, with @samp{--}. A long option name need not be written completely, any -unambiguous prefix is sufficient. Unless @env{POSIXLY_CORRECT} is set -in the environment, options may be intermixed with files. The argument -@option{--} is a marker to denote the end of options. +unambiguous prefix is sufficient. @acronym{POSIX} requires @code{m4} to +recognize arguments intermixed with files, even when +@env{POSIXLY_CORRECT} is set in the environment. Most options take +effect at startup regardless of their position, but some are documented +below as taking effect after any files that occurred earlier in the +command line. The argument @option{--} is a marker to denote the end of +options. With short options, options that do not take arguments may be combined into a single command line argument with subsequent options, options with mandatory arguments may be provided either as a single command line argument or as two arguments, and options with optional arguments must -be provided as a single argument. In other words, without -@env{POSIXLY_CORRECT}, @kbd{m4 -QPDfoo -d a -d+f} is equivalent to +be provided as a single argument. In other words, +@kbd{m4 -QPDfoo -d a -d+f} is equivalent to @kbd{m4 -Q -P -D foo -d -d+f -- ./a}, although the latter form is -considered canonical. (With @env{POSIXLY_CORRECT}, it is equivalent to -@kbd{m4 -Q -P -D foo -d -- ./a ./-d+f}). +considered canonical. With long options, options with mandatory arguments may be provided with an equal sign (@samp{=}) in a single argument, or as two arguments, and @@ -643,7 +646,8 @@ expectations, please report that as a bug. Set the regular expression syntax according to @var{RESYNTAX-SPEC}. When this option is not given, @acronym{GNU} M4 uses emacs compatible regular expressions. @xref{Changeresyntax}, for more details on the -format and meaning of @var{RESYNTAX-SPEC}. +format and meaning of @var{RESYNTAX-SPEC}. This option may be given +more than once, and order with respect to file names is significant. @item --safer Cripple the builtins @code{maketemp}, @code{mkstemp} (@pxref{Mkstemp}), @@ -675,7 +679,8 @@ is found or the list is exhausted. @xref{Modules}, for more details. By default, the modules @samp{m4}, @samp{traditional}, and @samp{gnu} are preloaded, although this can be controlled during configuration with the @option{--with-modules} option to -@file{m4-@value{VERSION}/@/configure}. +@file{m4-@value{VERSION}/@/configure}. This option may be given more +than once, and order with respect to file names is significant. @end table @node Preprocessor features @@ -713,8 +718,9 @@ This enters @var{NAME} into the symbol table, before any input files are read. If @samp{=@var{VALUE}} is missing, the value is taken to be the empty string. The @var{VALUE} can be any string, and the macro can be defined to take arguments, just as if it was defined from within the -input. This option may be given more than once; order is significant, -and redefining the same @var{NAME} loses the previous value. +input. This option may be given more than once; order with respect to +file names is significant, and redefining the same @var{NAME} loses the +previous value. @item -I @var{DIRECTORY} @itemx --include=@var{DIRECTORY} @@ -725,7 +731,8 @@ details. This option may be given more than once. @item -s @itemx --synclines Generate synchronization lines, for use by the C preprocessor or other -similar tools. This is useful, for example, when @code{m4} is used as a +similar tools. Order is significant with respect to file names. This +option is useful, for example, when @code{m4} is used as a front end to a compiler. Source file name and line number information is conveyed by directives of the form @samp{#line @var{linenum} "@var{file}"}, which are inserted as needed into the middle of the @@ -745,7 +752,8 @@ runtime control. This deletes any predefined meaning @var{NAME} might have. Obviously, only predefined macros can be deleted in this way. This option may be given more than once; undefining a @var{NAME} that does not have a -definition is silently ignored. +definition is silently ignored. Order is significant with respect to +file names. @end table @node Limits control @@ -890,7 +898,8 @@ withdrawn in a future release. @itemx --trace=@var{NAME} This enables tracing for the macro @var{NAME}, at any point where it is defined. @var{NAME} need not be defined when this option is given. -This option may be given more than once. @xref{Trace}, for more details. +This option may be given more than once, and order is significant with +respect to file names. @xref{Trace}, for more details. @end table @node Command line files @@ -914,6 +923,29 @@ string. @comment interactive use when switching to stdin in a non-default parse @comment state. +The options @option{--define} (@option{-D}), @option{--undefine} +(@option{-U}), @option{--synclines} (@option{-s}), @option{--trace} +(@option{-t}), @option{--regexp-syntax} (@option{-r}), and +@option{--load-module} (@option{-m}) only take effect after processing +input from any file names that occur earlier on the command line. For +example, assume the file @file{foo} contains: + +@comment file: foo +@example +$ @kbd{cat foo} +bar +@end example + +The text @samp{bar} can then be redefined over multiple uses of +@file{foo}: + +@comment options: -Dbar=hello foo -Dbar=world foo +@example +$ @kbd{m4 -Dbar=hello foo -Dbar=world foo} +@result{}hello +@result{}world +@end example + If none of the input files invoked @code{m4exit} (@pxref{M4exit}), the exit status of @code{m4} will be 0 for success, 1 for general failure (such as problems with reading an input file), and 63 for version @@ -4401,6 +4433,7 @@ the current output. This complements the builtin @code{include} @comment file: foo @example +$ @kbd{cat foo} bar @end example diff --git a/src/main.c b/src/main.c index 14ac5fe1..5ad4d73c 100644 --- a/src/main.c +++ b/src/main.c @@ -35,7 +35,7 @@ typedef struct macro_definition { struct macro_definition *next; - int code; /* D, U or t */ + int code; /* deferred optchar */ const char *macro; } macro_definition; @@ -232,7 +232,12 @@ static const struct option long_options[] = { NULL, 0, NULL, 0 }, }; -#define OPTSTRING "B:D:EF:GH:I:L:M:N:PQR:S:T:U:bcd::eil:m:o:r:st:" +/* 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:M:N:PQR:S:T:U:bcd::eil:m:o:r:st:" /* For determining whether to be interactive. */ enum interactive_choice @@ -256,6 +261,33 @@ size_opt (char const *opt, char const *msgid) return size; } +/* Process a command line file NAME, and return true only if it was + stdin. */ +static bool +process_file (m4 *context, const char *name) +{ + bool result = false; + if (strcmp (name, "-") == 0) + { + m4_push_file (context, stdin, "stdin", false); + result = true; + } + else + { + char *full_name; + FILE *fp = m4_path_search (context, name, &full_name); + if (fp == NULL) + { + m4_error (context, 0, errno, _("cannot open file `%s'"), name); + return false; + } + m4_push_file (context, fp, full_name, true); + free (full_name); + } + m4_macro_expand_input (context); + return result; +} + /* Main entry point. Parse arguments, load modules, then parse input. */ int @@ -268,9 +300,9 @@ main (int argc, char *const *argv, char *const *envp) size_t size; /* for parsing numeric option arguments */ macro_definition *defines; - FILE *fp; bool read_stdin = false; /* true iff we have read from stdin */ 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; @@ -339,9 +371,11 @@ main (int argc, char *const *argv, char *const *envp) case 'D': case 'U': - case 't': case 'm': case 'r': + case 's': + case 't': + case '\1': /* Arguments that cannot be handled until later are accumulated. */ defn = xmalloc (sizeof *defn); @@ -478,10 +512,6 @@ main (int argc, char *const *argv, char *const *envp) debugfile = optarg; break; - case 's': - m4_set_sync_output_opt (context, true); - break; - case IMPORT_ENVIRONMENT_OPTION: import_environment = true; break; @@ -502,13 +532,20 @@ main (int argc, char *const *argv, char *const *envp) } /* Interactive if specified, or if no input files and stdin and - stderr are terminals, to match sh behavior. */ + 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 && isatty (STDIN_FILENO) && isatty (STDERR_FILENO)))); + if (m4_get_interactive_opt (context)) + { + signal (SIGINT, SIG_IGN); + setbuf (stdout, NULL); + } + /* Do the basic initializations. */ if (debugfile && !m4_debug_set_output (context, debugfile)) @@ -554,7 +591,6 @@ main (int argc, char *const *argv, char *const *envp) while (defines != NULL) { macro_definition *next; - char *macro_value; const char *arg = defines->macro; switch (defines->code) @@ -563,13 +599,17 @@ main (int argc, char *const *argv, char *const *envp) { m4_symbol_value *value = m4_symbol_value_create (); - macro_value = strchr (arg, '='); + /* defines->arg is read-only, so we need a copy. */ + char *macro_name = xstrdup (arg); + char *macro_value = strchr (macro_name, '='); + if (macro_value != NULL) *macro_value++ = '\0'; m4_set_symbol_value_text (value, xstrdup (macro_value - ? macro_value : "")); + ? macro_value : "")); - m4_symbol_pushdef (M4SYMTAB, arg, value); + m4_symbol_define (M4SYMTAB, macro_name, value); + free (macro_name); } break; @@ -577,10 +617,6 @@ main (int argc, char *const *argv, char *const *envp) m4_symbol_delete (M4SYMTAB, arg); break; - case 't': - m4_set_symbol_name_traced (M4SYMTAB, arg, true); - break; - case 'm': m4_module_load (context, arg, 0); break; @@ -595,6 +631,19 @@ main (int argc, char *const *argv, char *const *envp) } break; + case 's': + m4_set_sync_output_opt (context, true); + break; + + case 't': + m4_set_symbol_name_traced (M4SYMTAB, arg, true); + break; + + case '\1': + seen_file = true; + read_stdin |= process_file (context, arg); + break; + default: assert (!"INTERNAL ERROR: bad code in deferred arguments"); abort (); @@ -606,52 +655,17 @@ main (int argc, char *const *argv, char *const *envp) } } - /* Interactive mode means unbuffered output, and interrupts ignored. */ + /* Handle remaining input files. Each file is pushed on the input, + and the input read. */ - if (m4_get_interactive_opt (context)) - { - signal (SIGINT, SIG_IGN); - setbuf (stdout, (char *) NULL); - } - - /* Handle the various input files. Each file is pushed on the input, - and the input read. Wrapup text is handled separately later. */ - - exit_status = EXIT_SUCCESS; - if (optind == argc) - { - m4_push_file (context, stdin, "stdin", false); - read_stdin = true; - m4_macro_expand_input (context); - } + if (optind == argc && !seen_file) + read_stdin = process_file (context, "-"); else for (; optind < argc; optind++) - { - if (strcmp (argv[optind], "-") == 0) - { - m4_push_file (context, stdin, "stdin", false); - read_stdin = true; - } - else - { - char *name; - fp = m4_path_search (context, argv[optind], &name); - if (fp == NULL) - { - error (0, errno, "%s", argv[optind]); - exit_status = EXIT_FAILURE; - continue; - } - else - { - m4_push_file (context, fp, name, true); - free (name); - } - } - m4_macro_expand_input (context); - } + read_stdin |= process_file (context, argv[optind]); - /* Now handle wrapup text. */ + /* 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); @@ -680,8 +694,7 @@ main (int argc, char *const *argv, char *const *envp) if (read_stdin && fclose (stdin) == EOF) m4_error (context, 0, errno, _("error closing stdin")); - if (exit_status == EXIT_SUCCESS) - exit_status = m4_get_exit_status (context); + exit_status = m4_get_exit_status (context); m4_delete (context); m4_hash_exit (); diff --git a/tests/generate.awk b/tests/generate.awk index f12bd05c..d734f13a 100755 --- a/tests/generate.awk +++ b/tests/generate.awk @@ -80,8 +80,7 @@ BEGIN { { if (seq == 0) new_group(node); - if (!file) - seq++; + seq++; printf ("echo \"$at_srcdir/%s:%d:\"\n", FILENAME, NR) next; } diff --git a/tests/macros.at b/tests/macros.at index 0267d688..cba534bc 100644 --- a/tests/macros.at +++ b/tests/macros.at @@ -99,6 +99,41 @@ AT_CHECK_M4([--reload-state=frozen.m4f input.m4], 0, expout, AT_CLEANUP(freezeme.m4 frozen.m4f) +## ------------------- ## +## Command line define ## +## ------------------- ## + +AT_SETUP([Command line define]) + +dnl Test that -D after last file still affects m4wrap'd text. +AT_DATA([in1], [[m4wrap(`foo +')foo +]]) +AT_DATA([in2], [[foo +]]) +AT_CHECK_M4([-Dfoo=1 in1 -Dfoo=2 in2 -Dfoo=3], [0], +[[1 +2 +3 +]]) + +dnl Test that -D only affects top-most definition. +AT_DATA([in1], [[define(`foo', `1')pushdef(`foo', `2')dnl +]]) +AT_DATA([in2], [[foo +popdef(`foo')foo +popdef(`foo')foo +]]) +AT_CHECK_M4([in1 -Dfoo=3 in2], [0], +[[3 +1 +foo +]]) + +AT_CLEANUP + + + ## ---------------- ## ## pushdef/popdef. ## ## ---------------- ## diff --git a/tests/options.at b/tests/options.at index dec3436b..b26f47f6 100644 --- a/tests/options.at +++ b/tests/options.at @@ -76,6 +76,35 @@ AT_CHECK_M4([--arglength=10], [0], [], AT_CLEANUP +## ---------- ## +## file names ## +## ---------- ## + +AT_SETUP([file names]) + +dnl Check that all files are processed even after missing file +AT_DATA([in], [[hello world +]]) +AT_CHECK_M4([oops in], [1], [[hello world +]], [[m4: cannot open file `oops': No such file or directory +]]) + +dnl Check that '-' means stdin, even if ./- exists. +AT_DATA([-], [[hi +]]) +AT_CHECK_M4([-], [0]) +AT_CHECK_M4([- --], [0]) +AT_CHECK_M4([-- -], [0]) +AT_CHECK_M4([./-], [0], [[hi +]]) +AT_CHECK_M4([./- --], [0], [[hi +]]) +AT_CHECK_M4([-- ./-], [0], [[hi +]]) + +AT_CLEANUP + + ## --------------- ## ## option grouping ## ## --------------- ## @@ -107,6 +136,14 @@ AT_CHECK_M4([-QPDfoo -d a -d+f], [0], [[ 1 AT_CHECK_M4([-Q -P -D foo -d -d+f -- a], [0], [[ 1 ]]) +AT_CHECK_M4([-QPDfoo -d -- a -d+f], [0], [[ 1 +hi +]]) + +AT_CHECK_M4([-Q -P -D foo -d ./a ./-d+f], [0], [[ 1 +hi +]]) + AT_CHECK_M4([--def foo --debug a], [0], [[ 1 m@&t@4_dnl() ]]) @@ -120,10 +157,16 @@ POSIXLY_CORRECT=1 export POSIXLY_CORRECT AT_CHECK_M4([-QPDfoo -d a -d+f], [0], [[ 1 +]]) + +AT_CHECK_M4([-Q -P -D foo -d -d+f -- ./a], [0], [[ 1 +]]) + +AT_CHECK_M4([-QPDfoo -d -- a -d+f], [0], [[ 1 hi ]]) -AT_CHECK_M4([-Q -P -D foo -d -- a ./-d+f], [0], [[ 1 +AT_CHECK_M4([-Q -P -D foo -d ./a ./-d+f], [0], [[ 1 hi ]]) -- 2.11.4.GIT