Re: Macro + diversion issue

Eric Blake <[email protected]>
Newsgroups gmane.comp.gnu.m4.general,gmane.comp.gnu.m4.patches
Message-ID <[email protected]>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

According to Daniel Richard G. on 5/21/2007 1:43 AM:
> In fact, it's a fairly simple patch. See attached for my stab at it. (Also, 
> there's a more straightforward test input file.) I did the two-arg 
> divert(), since that seems more natural than coming up with a new/separate 
> builtin for this.
> 
> I think this fills a hole in the core m4 feature set---right now, as I 
> understand it, the only way to send text to a diversion is at the point 
> when the text is actually being written out. With this, you can send text 
> to a diversion in the same way that errprint() sends text to stderr: 
> immediately upon evaluation of the builtin. Without regard to the invoking 
> context.

Thanks again for the idea.  And in the process of checking this in, I
discovered a regression from a couple days ago in handling hand-crafted
frozen files.  Here's what I'm checking in to head.  I'm porting the
regression fix, but not the new feature, to the branch.

2007-05-31  Eric Blake  <[email protected]>

	Add extension to divert builtin.
	* modules/m4.c (builtin_divert): Immediately divert second
	argument.
	* m4/output.c (m4_output_text): Allow 0 length.  Fixes regression
	from two days ago.
	(m4_shipout_text): Short-circuit on zero length.
	* tests/freeze.at (loading format 2): Expand test to catch
	regression.  Also check out-of-range version.
	* tests/builtins.at (divert): Expand test to check corner cases
	of extension.
	* NEWS: Document the extension.
	Reported by Daniel Richard G.


- --
Don't work too hard, make some time for fun as well!

Eric Blake             [email protected]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGXszC84KuGfSFAYARApeAAKDV+0R/8Xxr9rRqn454PGWSASqvrQCfdMJm
yJJrW4hk+IlM9MxWjN8PlwY=
=aDjR
-----END PGP SIGNATURE-----

_______________________________________________
M4-discuss mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/m4-discuss
m4.patch268 (text/plain, 6.8 KB)
Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.42
diff -u -p -r1.42 NEWS
--- NEWS	24 Mar 2007 21:29:41 -0000	1.42
+++ NEWS	31 May 2007 13:21:59 -0000
@@ -157,6 +157,11 @@ promoted to 2.0.
     information to the first line to show the definition of the macro being
     expanded.  The 'e' flag has been updated to output non-text expansions.
 
+*** The `divert' builtin now accepts an optional second argument of text
+    that is immediately placed on the new diversion, regardless of whether
+    the current expansion is nested within argument collection of another
+    macro.
+
 *** The `dumpdef' builtin now always outputs to standard error, rather than
     the debug file specified by the `--debugfile' option or `debugfile'
     macro.
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.105
diff -u -p -r1.105 m4.texinfo
--- doc/m4.texinfo	30 May 2007 13:36:58 -0000	1.105
+++ doc/m4.texinfo	31 May 2007 13:21:59 -0000
@@ -5008,11 +5008,17 @@ input file.
 @cindex files, diverting output to
 Output is diverted using @code{divert}:
 
-@deffn {Builtin (m4)} divert (@dvar{number, 0})
+@deffn {Builtin (m4)} divert (@dvar{number, 0}, @ovar{text})
 The current diversion is changed to @var{number}.  If @var{number} is left
 out or empty, it is assumed to be zero.  If @var{number} cannot be
 parsed, the diversion is unchanged.
 
+@cindex @acronym{GNU} extensions
+As a @acronym{GNU} extension, if optional @var{text} is supplied and
+@var{number} was valid, then @var{text} is immediately output to the
+new diversion, regardless of whether the expansion of @code{divert}
+occurred while collecting arguments for another macro.
+
 The expansion of @code{divert} is void.
 @end deffn
 
@@ -5076,6 +5082,44 @@ divert(`2')hello
 @result{}world
 @end example
 
+The ability to immediately output extra text is a @acronym{GNU}
+extension, but it can prove useful for ensuring that text goes to a
+particular diversion no matter how many pending macro expansions are in
+progress.  For a demonstration of why this is useful, it is important to
+understand in the example below why @samp{one} is output in diversion 2,
+not diversion 1, while @samp{three} and @samp{five} both end up in the
+correctly numbered diversion.  The key point is that when @code{divert}
+is executed unquoted as part of the argument collection of another
+macro, the side effect takes place immediately, but the text @samp{one}
+is not passed to any diversion until after the @samp{divert(`2')} and
+the enclosing @code{echo} have also taken place.  The example with
+@samp{three} shows how following the quoting rule of thumb delays the
+invocation of @code{divert} until it is not nested in any argument
+collection context, while the example with @samp{five} shows the use of
+the optional argument to speed up the output process.
+
+@example
+define(`echo', `$1')
+@result{}
+echo(divert(`1')`one'divert(`2'))`'dnl
+echo(`divert(`3')three`'divert(`4')')`'dnl
+echo(divert(`5', `five')divert(`6'))`'dnl
+divert
+@result{}
+undivert(`1')
+@result{}
+undivert(`2')
+@result{}one
+undivert(`3')
+@result{}three
+undivert(`4')
+@result{}
+undivert(`5')
+@result{}five
+undivert(`6')
+@result{}
+@end example
+
 Note that @code{divert} is an English word, but also an active macro
 without arguments.  When processing plain text, the word might appear in
 normal text and be unintentionally swallowed as a macro invocation.  One
Index: m4/output.c
===================================================================
RCS file: /sources/m4/m4/m4/output.c,v
retrieving revision 1.44
diff -u -p -r1.44 output.c
--- m4/output.c	28 May 2007 21:48:41 -0000	1.44
+++ m4/output.c	31 May 2007 13:21:59 -0000
@@ -423,6 +423,9 @@ m4_output_text (m4 *context, const char 
 {
   size_t count;
 
+  if (!output_diversion || !length)
+    return;
+
   if (!output_file && length > output_unused)
     make_room_for (context, length);
 
@@ -470,7 +473,7 @@ m4_shipout_text (m4 *context, m4_obstack
 
   /* Do nothing if TEXT should be discarded.  */
 
-  if (output_diversion == NULL)
+  if (!output_diversion || !length)
     return;
 
   /* Output TEXT to a file, or in-memory diversion buffer.  */
Index: modules/m4.c
===================================================================
RCS file: /sources/m4/m4/modules/m4.c,v
retrieving revision 1.109
diff -u -p -r1.109 m4.c
--- modules/m4.c	24 May 2007 20:13:44 -0000	1.109
+++ modules/m4.c	31 May 2007 13:21:59 -0000
@@ -70,7 +70,7 @@ extern void m4_make_temp     (m4 *contex
   BUILTIN (decr,	false,	true,	true,	1,	1  )	\
   BUILTIN (define,	true,	true,	false,	1,	2  )	\
   BUILTIN (defn,	false,	true,	false,	1,	-1 )	\
-  BUILTIN (divert,	false,	false,	false,	0,	1  )	\
+  BUILTIN (divert,	false,	false,	false,	0,	2  )	\
   BUILTIN (divnum,	false,	false,	false,	0,	0  )	\
   BUILTIN (dnl,		false,	false,	false,	0,	0  )	\
   BUILTIN (dumpdef,	false,	false,	false,	0,	-1 )	\
@@ -578,11 +578,16 @@ M4BUILTIN_HANDLER (decr)
 M4BUILTIN_HANDLER (divert)
 {
   int i = 0;
+  const char *text;
 
   if (argc >= 2 && !m4_numeric_arg (context, argc, argv, 1, &i))
     return;
 
   m4_make_diversion (context, i);
+
+  text = M4ARG (2);
+  m4_shipout_text (context, NULL, text, strlen (text),
+		   m4_get_current_line (context));
 }
 
 /* Expand to the current diversion number.  */
Index: tests/builtins.at
===================================================================
RCS file: /sources/m4/m4/tests/builtins.at,v
retrieving revision 1.40
diff -u -p -r1.40 builtins.at
--- tests/builtins.at	12 Apr 2007 17:41:35 -0000	1.40
+++ tests/builtins.at	31 May 2007 13:21:59 -0000
@@ -177,6 +177,17 @@ AT_CHECK_M4([divert.m4], 0,
 Text diverted a second time.
 ]])
 
+dnl Test second divert argument, added for m4 2.0
+AT_DATA([in.m4], [[define(`echo',`$1')dnl
+divert(`-1', `discarded without warning')
+divert`'dnl
+echo(` world'divert(divnum, `hello'))
+]])
+
+AT_CHECK_M4([-s in.m4], [0], [[#line 4 "in.m4"
+hello world
+]])
+
 AT_CLEANUP
 
 
Index: tests/freeze.at
===================================================================
RCS file: /sources/m4/m4/tests/freeze.at,v
retrieving revision 1.11
diff -u -p -r1.11 freeze.at
--- tests/freeze.at	28 May 2007 20:10:02 -0000	1.11
+++ tests/freeze.at	31 May 2007 13:21:59 -0000
@@ -163,6 +163,12 @@ M3
 gnu
 F7,7,3
 builtinbuiltingnu
+# Text to negative diversion must not crash.  Catches a regression
+# introduced 2007-05-28 and fixed 2007-05-31.
+D-1,5
+12345
+D0,0
+
 # Testing escape sequences
 T4,5
 blah\t\477\040\X5C
@@ -188,6 +194,13 @@ bar
 	'7 \
 ]])
 
+dnl We don't support anything larger than format 2; make sure of that...
+AT_DATA([bogus.m4f], [[V3
+]])
+AT_CHECK_M4([-R bogus.m4f], [63], [],
+[[m4: frozen file version 3 greater than max supported of 2
+]])
+
 AT_CLEANUP
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.