Re: Another POSIX incompatibility

Eric Blake-1 <[email protected]>
Newsgroups gmane.comp.gnu.m4.patches
Message-ID <[email protected]>
> 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  <[email protected]>

	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
-@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 @@
 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
-@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 @@
 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 @@
 @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 --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 @@
 @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 @@
 
 @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@&t@4_dnl()
 ]])
@@ -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.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.