m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Another POSIX incompatibility


From: Eric Blake-1
Subject: Re: Another POSIX incompatibility
Date: Wed, 8 Nov 2006 09:58:33 -0800 (PST)

> Here's the patch for the branch.  I'm thinking that for the head, maybe I
> should turn --synclines into an option with an optional argument, so that
> you can turn synclines off between files as well as on by using -s0 (and
> keeping -s as a synonym for -s1).  And maybe it is time to think about
> adding command line options --pushdef, --popdef, and --traceoff (of those,
> only --pushdef might be a candidate for a short option -p), for even more
> inter-file operational capability.  Head also would support -m and -r
> between files (mainly because it was easiest to use the existing deferred
> argument handling mechanism to achieve this patch, by treating intermixed
> files as deferred arguments to option '\1').

Here's the first part of the patch - fixing -D to match the branch.  In
fact, a regression had crept in; on the branch, -D always did a define,
but for a while, on head it was doing a pushdef since rev 1.39 of main.c
in 2003.

On further thought, -s cannot be made an optional argument, because
it would break POSIX-mandated usage like 'm4 -sDfoo' (even though
a POSIX app should use 'm4 -s -D foo' instead).  But I can instead
add a new option, '--syncoutput', with an optional argument, and make
that match the syncoutput builtin, then redefine -s/--synclines to be
a synonym for --syncoutput=1.  I also plan to add --pushdef/-p,
--popdef, add --traceon as a synonym for --trace, and add --traceoff,
all as part of the second part of the patch.


2006-11-08  Eric Blake  <address@hidden>

        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.


Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.76
diff -u -r1.76 m4.texinfo
--- doc/m4.texinfo      7 Nov 2006 19:18:10 -0000       1.76
+++ doc/m4.texinfo      8 Nov 2006 17:49:51 -0000
@@ -537,19 +537,22 @@
 @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
address@hidden 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
address@hidden 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
address@hidden, @kbd{m4 -QPDfoo -d a -d+f} is equivalent to
+be provided as a single argument.  In other words,
address@hidden -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
address@hidden -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 @@
 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 @@
 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
address@hidden@value{VERSION}/@/configure}.
address@hidden@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 @@
 read.  If @address@hidden 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 address@hidden
@@ -725,7 +731,8 @@
 @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 @@
 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 @@
 @itemx address@hidden
 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 @@
 @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
address@hidden (@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:
+
address@hidden file: foo
address@hidden
+$ @kbd{cat foo}
+bar
address@hidden example
+
+The text @samp{bar} can then be redefined over multiple uses of
address@hidden:
+
address@hidden options: -Dbar=hello foo -Dbar=world foo
address@hidden
+$ @kbd{m4 -Dbar=hello foo -Dbar=world foo}
address@hidden
address@hidden
address@hidden 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 @@
 
 @comment file: foo
 @example
+$ @kbd{cat foo}
 bar
 @end example
 
Index: src/main.c
===================================================================
RCS file: /sources/m4/m4/src/main.c,v
retrieving revision 1.100
diff -u -r1.100 main.c
--- src/main.c  27 Oct 2006 04:03:28 -0000      1.100
+++ src/main.c  8 Nov 2006 17:49:51 -0000
@@ -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 @@
   { 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 @@
   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 @@
   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 @@
 
       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 @@
        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 @@
       }
 
   /* 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 @@
     while (defines != NULL)
       {
        macro_definition *next;
-       char *macro_value;
        const char *arg = defines->macro;
 
        switch (defines->code)
@@ -563,13 +599,17 @@
            {
              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 @@
            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 @@
              }
            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 @@
       }
   }
 
-  /* Interactive mode means unbuffered output, and interrupts ignored.  */
-
-  if (m4_get_interactive_opt (context))
-    {
-      signal (SIGINT, SIG_IGN);
-      setbuf (stdout, (char *) NULL);
-    }
+  /* Handle remaining input files.  Each file is pushed on the input,
+     and the input read.  */
 
-  /* 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 @@
   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 ();
Index: tests/generate.awk
===================================================================
RCS file: /sources/m4/m4/tests/generate.awk,v
retrieving revision 1.23
diff -u -r1.23 generate.awk
--- tests/generate.awk  21 Oct 2006 15:23:56 -0000      1.23
+++ tests/generate.awk  8 Nov 2006 17:49:51 -0000
@@ -80,8 +80,7 @@
     {
       if (seq == 0)
        new_group(node);
-      if (!file)
-       seq++;
+      seq++;
       printf ("echo \"$at_srcdir/%s:%d:\"\n", FILENAME, NR)
       next;
     }
Index: tests/macros.at
===================================================================
RCS file: /sources/m4/m4/tests/macros.at,v
retrieving revision 1.10
diff -u -r1.10 macros.at
--- tests/macros.at     10 Oct 2006 12:47:23 -0000      1.10
+++ tests/macros.at     8 Nov 2006 17:49:51 -0000
@@ -99,6 +99,41 @@
 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.  ##
 ## ---------------- ##
Index: tests/options.at
===================================================================
RCS file: /sources/m4/m4/tests/options.at,v
retrieving revision 1.22
diff -u -r1.22 options.at
--- tests/options.at    19 Oct 2006 23:13:45 -0000      1.22
+++ tests/options.at    8 Nov 2006 17:49:51 -0000
@@ -76,6 +76,35 @@
 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([-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@&address@hidden()
 ]])
@@ -120,10 +157,16 @@
 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
 ]])
 


-- 
View this message in context: 
http://www.nabble.com/Re%3A-Another-POSIX-incompatibility-tf2553305.html#a7243522
Sent from the Gnu - M4 - Patches mailing list archive at Nabble.com.





reply via email to

[Prev in Thread] Current Thread [Next in Thread]