Re: dangling pointer bug and proposed patch

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

According to Eric Blake on 6/3/2006 2:51 PM:
> Also, notice that m4 1.4.4 does the following inconsistent behavior:
> define(`f',`1')f(popdef(`f')pushdef(`f',`2'))
> 1
> define(`g',`1')g(define(`g',`2'))
> 2
> 
> So, the following patch is my proposed solution to the original problem of
> core dumps when undefining a symbol while it is still in use by a pending
> expansion.
> It will take some work to forward port a similar patch to CVS head.

And it is now finally ported.

2006-09-05  Eric Blake  <[email protected]>

	* m4/macro.c (expansion_level, macro_call_id): Change to size_t.
	All users updated.
	(expand_token): Avoid assertion just added to docs.
	(expand_macro): Track pending expansions, for when a symbol's
	definition changes during argument collection.
	(m4_macro_call, process_macro): Operate on symbol value, not
	symbol, since symbol may have changed during argument collection.
	* m4/m4private.h (m4_symbol_value): Add pending_expansions member.
	(VALUE_PENDING, SYMBOL_PENDING, VALUE_DELETED_BIT): New defines.
	(m4_get_symbol_value): Add fast macro version.
	* m4/m4module.h (M4_BUILTIN_FLAGS_MASK): New enumerator.
	(m4_macro_call): Adjust prototype.
	* m4/module.c (install_builtin_table): Check that flags are valid
	when creating builtin.
	* m4/symtab.c (m4__symtab_remove_module_references): Use
	m4_symbol_value_delete, rather than inlining it.
	(m4_symbol_value_copy): Copy placeholder text.
	(symbol_popval): Use m4_symbol_value_delete.
	(m4_symbol_value_delete): Implementation was missing when NDEBUG.
	Handle pending expansions.
	* modules/gnu.c (indir): Update to new prototype.
	* doc/m4.texinfo: Fix menus to be consistent with section names.
	(Defn): Add test that macro tokens flatten to empty string;
	triggered an assert before this patch.
	(Ifelse): Merge another node.
	(Loops): Split into...
	(Forloop, Foreach): New nodes; work is still underway on them.
	(Answers): Add more info on foreach macro; work is still underway.
	(Indir): Add test that indir collects arguments before looking up
	macro.
	* TODO: Update based on this patch.

- --
Life is short - so eat dessert first!

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

iD8DBQFE/XrH84KuGfSFAYARAvmzAKCgJHpGIfYqI8asCRVpXltnrotlnwCZAdqq
BODsh3hGxUmq3okWPbnfsp4=
=BeOR
-----END PGP SIGNATURE-----

_______________________________________________
M4-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/m4-patches
m4.patch149 (text/plain, 51.2 KB)
Index: TODO
===================================================================
RCS file: /sources/m4/m4/TODO,v
retrieving revision 1.19
diff -u -p -r1.19 TODO
--- TODO	22 Aug 2006 16:16:47 -0000	1.19
+++ TODO	5 Sep 2006 13:23:50 -0000
@@ -10,9 +10,6 @@ for any of these ideas or if you have ot
 
   + The test case `other-tests/stackovf.test' does not work.
 
-  + Some characters, such as comma, are still hardcoded, and do not
-    follow changes in the syntax table.
-
   + stack overflow is basically broken
 
     The routines to detect stack overflow throuh segv  are basically
@@ -28,17 +25,6 @@ for any of these ideas or if you have ot
     Marc Espie
     [email protected]
 
-  + ~/src/ace % m4 --trace undefine -dV                       nostromo Err 1$
-    m4 debug: NONE: 0: input read from stdin
-    undefine(`undefine')
-    m4trace:stdin:1: -1- id 1: undefine ...
-    m4trace:stdin:1: -1- id 1: undefine(`undefine') -> ???
-    m4trace:stdin:1: -1- id 1: p@8(...)
-
-    --
-    Akim Demaille
-    [email protected]
-
 
 * FEATURES OR PROBLEMS
 
@@ -74,9 +60,6 @@ for any of these ideas or if you have ot
     --
     Stepan Kasal <[email protected]>
 
-  + The $ used in user defined macros cannot be changed through
-    changesyntax.  It should be handled as a modifier.
-
   + If configured --with-gmp for multiple precision arithmetic there are
     some warnings, but it passes the tests.
 
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.39
diff -u -p -r1.39 m4.texinfo
--- doc/m4.texinfo	31 Aug 2006 03:21:35 -0000	1.39
+++ doc/m4.texinfo	5 Sep 2006 13:23:51 -0000
@@ -124,7 +124,7 @@ file in the distribution).  @xref{Experi
 * File Inclusion::              File inclusion
 * Diversions::                  Diverting and undiverting output
 
-* Modules::                     Extending m4 with dynamic runtime modules
+* Modules::                     Extending M4 with dynamic runtime modules
 
 * Text handling::               Macros for text handling
 * Arithmetic::                  Macros for doing arithmetic
@@ -132,8 +132,7 @@ file in the distribution).  @xref{Experi
 * Miscellaneous::               Miscellaneous builtin macros
 * Frozen files::                Fast loading of frozen state
 
-* Compatibility::               Compatibility with other versions of m4
-* Experiments::                 Experimental features in GNU M4
+* Compatibility::               Compatibility with other versions of @code{m4}
 * Answers::                     Correct version of some examples
 * Copying This Manual::         How to make copies of this manual
 * Indices::                     Indices of concepts and macros
@@ -152,11 +151,11 @@ Introduction and preliminaries
 Lexical and syntactic conventions
 
 * Names::                       Macro names
-* Quoted strings::              Quoting input to m4
-* Comments::                    Comments in m4 input
+* Quoted strings::              Quoting input to @code{m4}
+* Comments::                    Comments in @code{m4} input
 * Other tokens::                Other kinds of input tokens
-* Input processing::            How m4 copies input to output
-* Regular expression syntax::   How m4 interprets regular expressions
+* Input processing::            How @code{m4} copies input to output
+* Regular expression syntax::   How @code{m4} interprets regular expressions
 
 How to invoke macros
 
@@ -170,7 +169,7 @@ How to define new macros
 
 * Define::                      Defining a new macro
 * Arguments::                   Arguments to macros
-* Pseudo Arguments::            Pseudo arguments to macros
+* Pseudo Arguments::            Special arguments to macros
 * Undefine::                    Deleting a macro
 * Defn::                        Renaming macros
 * Pushdef::                     Temporarily redefining macros
@@ -178,14 +177,15 @@ How to define new macros
 
 * Indir::                       Indirect call of macros
 * Builtin::                     Indirect call of builtins
-
 * Symbols::                     Getting the defined macro names
 
 Conditionals, loops, and recursion
 
 * Ifdef::                       Testing if a macro is defined
 * Ifelse::                      If-else construct, or multibranch
-* Loops::                       Loops and recursion in m4
+* Shift::                       Recursion in @code{m4}
+* Forloop::                     Iteration by counting
+* Foreach::                     Iteration by list contents
 
 How to debug macros and input
 
@@ -201,7 +201,7 @@ Input control
 * Changecom::                   Changing the comment delimiters
 * Changeresyntax::              Changing the regular expression syntax
 * Changesyntax::                Changing the lexical structure of the input
-* M4wrap::                      Saving input until end of input
+* M4wrap::                      Saving text until end of input
 
 File inclusion
 
@@ -215,7 +215,7 @@ Diverting and undiverting output
 * Divnum::                      Diversion numbers
 * Cleardiv::                    Discarding diverted text
 
-Extending m4 with dynamic runtime modules
+Extending M4 with dynamic runtime modules
 
 * Listing Modules::             Listing loaded modules
 * Load::                        Loading additional modules
@@ -238,19 +238,19 @@ Macros for doing arithmetic
 * Eval::                        Evaluating integer expressions
 * Mpeval::                      Multiple precision arithmetic
 
-Running shell commands
+Macros for running shell commands
 
 * Platform macros::             Determining the platform
 * Syscmd::                      Executing simple commands
 * Esyscmd::                     Reading the output of commands
 * Sysval::                      Exit status
-* Maketemp::                    Making names for temporary files
+* Maketemp::                    Making temporary files
 
 Miscellaneous builtin macros
 
 * Errprint::                    Printing error messages
 * Location::                    Printing current location
-* M4exit::                      Exiting from m4
+* M4exit::                      Exiting from @code{m4}
 * Syncoutput::                  Turning on and off sync lines
 
 Fast loading of frozen state
@@ -263,15 +263,16 @@ Compatibility with other versions of @co
 
 * Extensions::                  Extensions in @acronym{GNU} M4
 * Incompatibilities::           Other incompatibilities
+* Experiments::                 Experimental features in @acronym{GNU} M4
 
-Copying This Manual
+How to make copies of this manual
 
 * GNU Free Documentation License::  License for copying this manual
 
-Indices
+Indices of concepts and macros
 
 * Concept index::               Index for many concepts
-* Macro index::                 Index for all m4 macros
+* Macro index::                 Index for all @code{m4} macros
 
 @end detailmenu
 @end menu
@@ -811,15 +812,15 @@ exception of the @sc{nul} character (the
 
 @menu
 * Names::                       Macro names
-* Quoted strings::              Quoting input to m4
-* Comments::                    Comments in m4 input
+* Quoted strings::              Quoting input to @code{m4}
+* Comments::                    Comments in @code{m4} input
 * Other tokens::                Other kinds of input tokens
-* Input processing::            How m4 copies input to output
-* Regular expression syntax::   How m4 interprets regular expressions
+* Input processing::            How @code{m4} copies input to output
+* Regular expression syntax::   How @code{m4} interprets regular expressions
 @end menu
 
 @node Names
-@section Names
+@section Macro names
 
 @cindex names
 A name is any sequence of letters, digits, and the character @kbd{_}
@@ -835,7 +836,7 @@ changed at any time, using the builtin m
 @xref{Changesyntax}, for more information.
 
 @node Quoted strings
-@section Quoted strings
+@section Quoting input to @code{m4}
 
 @cindex quoted string
 A quoted string is a sequence of characters surrounded by quote
@@ -864,7 +865,7 @@ The quote characters can be changed at a
 (@pxref{Changesyntax}).
 
 @node Comments
-@section Comments
+@section Comments in @code{m4} input
 
 @cindex comments
 Comments in @code{m4} are normally delimited by the characters @samp{#}
@@ -900,7 +901,7 @@ the builtin macros @code{changecom} (@px
 @code{changesyntax} (@pxref{Changesyntax}).
 
 @node Other tokens
-@section Other tokens
+@section Other kinds of input tokens
 
 Any character, that is neither a part of a name, nor of a quoted string,
 nor a comment, is a token by itself.  When not in the context of macro
@@ -912,7 +913,7 @@ roles, explained later.  Which character
 can be adjusted with @code{changesyntax} (@pxref{Changesyntax}).
 
 @node Input processing
-@section Input Processing
+@section How @code{m4} copies input to output
 
 As @code{m4} reads the input token by token, it will copy each token
 directly to the output immediately.
@@ -968,7 +969,7 @@ This process continues until there are n
 all the input has been consumed.
 
 @node Regular expression syntax
-@section Regular Expression Syntax
+@section How @code{m4} interprets regular expressions
 
 There are several contexts where @code{m4} parses an argument as a
 regular expression.  This section describes the various flavors of
@@ -1253,14 +1254,15 @@ f
 
 It is an error if the end of file occurs while collecting arguments.
 
+@comment status: 1
 @example
 define(
 ^D
-@error{}m4:stdin:1: ERROR: end of file in argument list
+@error{}m4:stdin:1: end of file in argument list
 @end example
 
 @node Quoting Arguments
-@section Quoting macro arguments
+@section On Quoting Arguments to macros
 
 @cindex quoted macro arguments
 @cindex macros, quoted arguments to
@@ -1335,7 +1337,7 @@ value, and bring back the original value
 @menu
 * Define::                      Defining a new macro
 * Arguments::                   Arguments to macros
-* Pseudo Arguments::            Pseudo arguments to macros
+* Pseudo Arguments::            Special arguments to macros
 * Undefine::                    Deleting a macro
 * Defn::                        Renaming macros
 * Pushdef::                     Temporarily redefining macros
@@ -1343,7 +1345,6 @@ value, and bring back the original value
 
 * Indir::                       Indirect call of macros
 * Builtin::                     Indirect call of builtins
-
 * Symbols::                     Getting the defined macro names
 @end menu
 
@@ -1780,11 +1781,14 @@ echo(foo)
 @end example
 
 Using @code{defn} to generate special tokens for builtin macros outside
-of expected contexts can sometimes trigger warnings.
+of expected contexts can sometimes trigger warnings.  But most of the
+time, such tokens are silently converted to the empty string.
 
 @example
+defn(`defn')
+@result{}
 define(defn(`divnum'), `cannot redefine a builtin token')
-@error{}m4:stdin:1: Warning: define: invalid macro name ignored
+@error{}m4:stdin:2: Warning: define: invalid macro name ignored
 @result{}
 divnum
 @result{}0
@@ -2003,6 +2007,25 @@ The point is, here, that larger macro pa
 defined, that will not be called by accident.  They can @emph{only} be
 called through the builtin @code{indir}.
 
+One other point to observe is that argument collection occurs before
+@code{indir} invokes @var{name}, so if argument collection changes the
+value of @var{name}, that will be reflected in the final expansion.
+This is different than the behavior when invoking macros directly,
+where the definition that was in effect before argument collection is
+used.
+
+@example
+define(`f', `1')
+@result{}
+f(define(`f', `2'))
+@result{}1
+indir(`f', define(`f', `3'))
+@result{}3
+indir(`f', undefine(`f'))
+@error{}m4:stdin:4: Warning: indir: undefined macro `f'
+@result{}
+@end example
+
 @node Builtin
 @section Indirect call of builtins
 
@@ -2124,11 +2147,13 @@ something a number of times, or while so
 @menu
 * Ifdef::                       Testing if a macro is defined
 * Ifelse::                      If-else construct, or multibranch
-* Loops::                       Loops and recursion in m4
+* Shift::                       Recursion in @code{m4}
+* Forloop::                     Iteration by counting
+* Foreach::                     Iteration by list contents
 @end menu
 
 @node Ifdef
-@section Testing macro definitions
+@section Testing if a macro is defined
 
 @cindex conditionals
 There are two different builtin conditionals in @code{m4}.  The first is
@@ -2156,7 +2181,7 @@ ifdef(`no_such_macro', `yes', `no', `ext
 @end example
 
 @node Ifelse
-@section Comparing strings
+@section If-else construct, or multibranch
 
 @cindex comparing strings
 The other conditional, @code{ifelse}, is much more powerful.  It can be
@@ -2164,13 +2189,30 @@ used as a way to introduce a long commen
 as a multibranch, depending on the number of arguments supplied:
 
 @deffn {Builtin (m4)} ifelse (@var{comment})
-@deffnx {Builtin (m4)} ifelse (@var{string-1}, @var{string-2}, @var{equal}, @w{opt @var{not-equal})}
-@deffnx {Builtin (m4)} ifelse (@var{string-1}, @var{string-2}, @var{equal}, @dots{})
+@deffnx {Builtin (m4)} ifelse (@var{string-1}, @var{string-2}, @var{equal}, @
+  @ovar{not-equal})
+@deffnx {Builtin (m4)} ifelse (@var{string-1}, @var{string-2}, @var{equal-1}, @
+  @var{string-3}, @var{string-4}, @var{equal-2}, @dots{})
 Used with only one argument, the @code{ifelse} simply discards it and
-produces no output.  This is a common @code{m4} idiom for introducing a
+produces no output.
+
+If called with three or four arguments, @code{ifelse} expands into
+@var{equal}, if @var{string-1} and @var{string-2} are equal (character
+for character), otherwise it expands to @var{not-equal}.  A final fifth
+argument is ignored, after triggering a warning.
+
+If called with six or more arguments, and @var{string-1} and
+@var{string-2} are equal, @code{ifelse} expands into @var{equal-1},
+otherwise the first three arguments are discarded and the processing
+starts again.
+
+The macro @code{ifelse} is recognized only with parameters.
+@end deffn
+
+Using only one argument is a common @code{m4} idiom for introducing a
 block comment, as an alternative to repeatedly using @code{dnl}.  This
-special usage is recognized by GNU @code{m4}, so that in this case, the
-warning about missing arguments is never triggered.
+special usage is recognized by @acronym{GNU} @code{m4}, so that in this
+case, the warning about missing arguments is never triggered.
 
 @example
 ifelse(`some comments')
@@ -2180,43 +2222,64 @@ ifelse(`foo', `bar')
 @result{}
 @end example
 
-If called with three or four arguments, @code{ifelse} expands into
-@var{equal}, if @var{string-1} and @var{string-2} are equal (character
-for character), otherwise it expands to @var{not-equal}.
+Using three or four arguments provides decision points.
 
 @example
 ifelse(`foo', `bar', `true')
 @result{}
 ifelse(`foo', `foo', `true')
 @result{}true
-ifelse(`foo', `bar', `true', `false')
-@result{}false
-ifelse(`foo', `foo', `true', `false')
+define(`foo', `bar')
+@result{}
+ifelse(foo, `bar', `true', `false')
 @result{}true
+ifelse(foo, `foo', `true', `false')
+@result{}false
+@end example
+
+Notice how the first argument was used unquoted; it is common to compare
+the expansion of a macro with a string.  With this macro, you can now
+reproduce the behavior of many of the builtins, where the macro is
+recognized only with arguments.
+
+@example
+define(`foo', `ifelse(`$#', `0', ``$0'', `arguments:$#')')
+@result{}
+foo
+@result{}foo
+foo()
+@result{}arguments:1
+foo(`a', `b', `c')
+@result{}arguments:3
 @end example
 
 @cindex multibranches
 However, @code{ifelse} can take more than four arguments.  If given more
 than four arguments, @code{ifelse} works like a @code{case} or @code{switch}
 statement in traditional programming languages.  If @var{string-1} and
-@var{string-2} are equal, @code{ifelse} expands into @var{equal}, otherwise
+@var{string-2} are equal, @code{ifelse} expands into @var{equal-1}, otherwise
 the procedure is repeated with the first three arguments discarded.  This
 calls for an example:
 
 @example
+ifelse(`foo', `bar', `third', `gnu', `gnats')
+@error{}m4:stdin:1: Warning: ifelse: extra arguments ignored: 5 > 4
+@result{}gnu
+ifelse(`foo', `bar', `third', `gnu', `gnats', `sixth')
+@result{}
 ifelse(`foo', `bar', `third', `gnu', `gnats', `sixth', `seventh')
 @result{}seventh
+ifelse(`foo', `bar', `3', `gnu', `gnats', `6', `7', `8')
+@error{}m4:stdin:4: Warning: ifelse: extra arguments ignored: 8 > 7
+@result{}7
 @end example
 
-The macro @code{ifelse} is recognized only with parameters.
-@end deffn
-
 Naturally, the normal case will be slightly more advanced than these
 examples.  A common use of @code{ifelse} is in macros implementing loops
 of various kinds.
 
-@node Loops
-@section Loops and recursion
+@node Shift
+@section Recursion in @code{m4}
 
 @cindex recursive macros
 @cindex macros, recursive
@@ -2231,9 +2294,12 @@ previously.
 There is a builtin macro, @code{shift}, which can, among other things,
 be used for iterating through the actual arguments to a macro:
 
-@deffn {Builtin (m4)} shift (@dots{})
-@code{shift} takes any number of arguments, and expands to all but the first
-argument, separated by commas, with each argument quoted.
+@deffn {Builtin (m4)} shift (@var{arg1}, @dots{})
+Takes any number of arguments, and expands to all its arguments except
+@var{arg1}, separated by commas, with each argument quoted.
+
+The macro @code{shift} is recognized only with parameters.
+@end deffn
 
 @example
 shift
@@ -2243,10 +2309,14 @@ shift(`bar')
 shift(`foo', `bar', `baz')
 @result{}bar,baz
 @end example
+
+An example of the use of @code{shift} is this macro:
+
+@deffn Composite reverse (@dots{})
+Takes any number of arguments, and reverse their order.
 @end deffn
 
-An example of the use of @code{shift} is this macro, which reverses the
-order of its arguments:
+It is implemented as:
 
 @example
 define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'',
@@ -2261,14 +2331,66 @@ reverse(`foo', `bar', `gnats', `and gnus
 @end example
 
 While not a very interesting macro, it does show how simple loops can be
-made with @code{shift}, @code{ifelse} and recursion.
+made with @code{shift}, @code{ifelse} and recursion.  It also shows
+that @code{shift} is usually used with @samp{$@@}.  Sometimes, a
+recursive algorithm requires adding quotes to each element:
+
+@deffn Composite quote (@dots{})
+@deffnx Composite dquote (@dots{})
+@deffnx Composite dquote_elt (@dots{})
+Takes any number of arguments, and quoting.  With @code{quote}, only one
+level of quoting is added, effectively removing whitespace after commas
+and turning the arguments into a string.  With @code{dquote}, two
+levels of quoting are added, one around each element, and one around
+the list.  And with @code{dquote_elt}, two levels of quoting are added
+around each element.
+@end deffn
+
+Here is an implementation, along with an example usage.
+
+@comment FIXME - these macros are worth reusing in other examples;
+@comment factor them into examples/quote.m4.
+@example
+define(`quote', `ifelse(`$#', `0', `', ``$*'')')dnl
+define(`dquote', ``$@@'')dnl
+define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
+                             ```$1'',dquote_elt(shift($@@))')')dnl
+-quote-dquote-dquote_elt-
+@result{}----
+-quote(`1')-dquote(`1')-dquote_elt(`1')-
+@result{}-1-`1'-`1'-
+-quote(`1',`2')-dquote(`1',`2')-dquote_elt(`1',`2')-
+@result{}-1,2-`1',`2'-`1',`2'-
+dquote(dquote_elt(`1',`2'))
+@result{}``1'',``2''
+dquote_elt(dquote(`1',`2'))
+@result{}``1',`2''
+@end example
+
+The last two lines show that when given two arguments, @code{dquote}
+results in one string, while @code{dquote_elt} results in two.
+
+@node Forloop
+@section Iteration by counting
 
-@cindex forloops
+@cindex for loops
 @cindex loops, counting
 @cindex counting loops
-Here is an example of a loop macro that implements a simple forloop.  It
-can, for example, be used for simple counting:
+Here is an example of a loop macro that implements a simple for loop.
+@comment FIXME - this section still needs some work done
+
+@deffn Composite forloop (@var{iterator}, @var{start}, @var{end}, @var{text})
+Takes the name in @var{iterator}, which must be a valid macro name, and
+successively assign it each integer value from @var{start} to @var{end},
+inclusive.  For each assignment to @var{iterator}, append @var{text} to
+the expansion of the @code{forloop}.  @var{text} may refer to
+@var{iterator}.  Any definition of @var{iterator} prior to this
+invocation is restored.
+@end deffn
 
+It can, for example, be used for simple counting:
+
+@comment FIXME - include(`forloop.m4')
 @comment ignore
 @example
 forloop(`i', 1, 8, `i ')
@@ -2325,12 +2447,69 @@ enough for general use. They lack even b
 like start value less than final value, and the first argument not being
 a name.  Correcting these errors are left as an exercise to the reader.
 
-@comment FIXME - it would be nice to introduce foreach here, and even
-@comment give an example like this for finding defined macros that meet
-@comment a certain pattern (see examples/foreach.m4):
-@comment define(`quote', ``$@'')
-@comment foreach(`macro', (quote(symbols)),
-@comment         `regexp(macro, `.*_.*', ``\&',')')
+@node Foreach
+@section Iteration by list contents
+
+@cindex for each loops
+@cindex loops, list iteration
+@cindex iterating over lists
+Here is an example of a loop macro that implements list iteration.
+@comment FIXME - this section still needs some work done
+
+@deffn Composite foreach (@var{iterator}, @var{paren-list}, @var{text})
+Takes the name in @var{iterator}, which must be a valid macro name, and
+successively assign it each value from @var{paren-list}.
+@var{paren-list} is a comma-separated list of elements surrounded by
+parentheses.  For each assignment to @var{iterator}, append @var{text}
+to the expansion of @code{foreach}.  @var{text} may refer to
+@var{iterator}.  Any definition of @var{iterator} prior to this
+invocation is restored.
+@end deffn
+
+As an example, this displays each word in a list inside of a sentence.
+
+@comment FIXME - include(`foreach.m4')
+@comment ignore
+@example
+foreach(`x', `(foo, bar, foobar)', `Word was: x
+')
+@result{}Word was: foo
+@result{}Word was: bar
+@result{}Word was: foobar
+@end example
+
+The implementation of the @code{foreach} macro is a bit more involved;
+it is a wrapper around two helper macros.  First, @code{_arg1} is needed
+to grab the first element of a list.  Second, @code{_foreach} implements
+the recursion, successively walking through the original list.
+
+Here is an actual implementation of @code{forloop}, followed by a
+demonstration of using it to filter out a list of symbols that contain
+@samp{if}.
+
+@comment FIXME - include(foreach.m4),include(quote.m4)
+@example
+define(`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')')dnl
+define(`_arg1', ``$1'')dnl
+define(`_foreach',
+       `ifelse($2, `()', ,
+               `define(`$1',
+                       `_arg1$2')$3`'_foreach(`$1', `(shift$2)',
+                                              `$3')')')dnl
+define(`dquote', ``$@@'')
+@result{}
+foreach(`macro', (dquote(symbols)), `regexp(macro, `.*if.*', ``\&',')')
+@result{}ifdef,ifelse,shift,
+@end example
+
+The example had to use a helper @code{quote} to ensure that the output
+from @code{symbols} was double quoted; without it, the macro would have
+gone into an infinite loop thanks to macros being reinvoked during the
+rescanning.  Choosing @samp{()} as the list delimiters made this
+example rather awkward in terms of proper quoting.  (A different
+implementation can be acheived by changing @var{list} from a
+parenthesized list to a quoted list; try reimplementing @code{foreach}
+with these semantics yourself, then check @pxref{Answers}).
 
 @node Debugging
 @chapter How to debug macros and input
@@ -2534,7 +2713,7 @@ to @code{m4}.
 * Changecom::                   Changing the comment delimiters
 * Changeresyntax::              Changing the regular expression syntax
 * Changesyntax::                Changing the lexical structure of the input
-* M4wrap::                      Saving input until end of input
+* M4wrap::                      Saving text until end of input
 @end menu
 
 @node Dnl
@@ -2630,7 +2809,7 @@ as they will be confused with names in t
 the quoting mechanism.
 
 @node Changecom
-@section Changing comment delimiters
+@section Changing the comment delimiters
 
 @cindex changing comment delimiters
 @cindex comment delimiters, changing
@@ -3101,7 +3280,7 @@ macro calls.
 
 
 @node M4wrap
-@section Saving input
+@section Saving text until end of input
 
 @cindex saving input
 @cindex input, saving
@@ -3473,7 +3652,7 @@ diversions, given by the arguments.  (Th
 should try to see if you can find it and correct it.   @xref{Answers}.)
 
 @node Modules
-@chapter Runtime Dynamic Modules
+@chapter Extending M4 with dynamic runtime modules
 
 @cindex modules
 @sc{gnu} m4-1.4.x had a monolithic architecture.  All of its
@@ -3979,7 +4158,7 @@ patreg(`aba abb 121', `\(.\)\(.\)\1', `\
 
 
 @node Format
-@section Formatted output
+@section Formatting strings (printf-like)
 
 @cindex formatted output
 @cindex output, formatted
@@ -4006,7 +4185,7 @@ len(format(`%-*X', `5000', `1'))
 @result{}5000
 @end example
 
-Using the @code{forloop} macro defined in @xref{Loops}, this
+Using the @code{forloop} macro defined in @xref{Forloop}, this
 example shows how @code{format} can be used to produce tabular output.
 
 @comment ignore
@@ -4074,7 +4253,7 @@ given arguments.
 @end deffn
 
 @node Eval
-@section Evaluating integer or rational expressions
+@section Evaluating integer expressions
 
 @cindex integer expression evaluation
 @cindex evaluation, of integer expressions
@@ -4213,7 +4392,7 @@ The builtin macro @code{mpeval} is recog
 @end deffn
 
 @node Shell commands
-@chapter Running shell commands
+@chapter Macros for running shell commands
 
 @cindex executing shell commands
 @cindex running shell commands
@@ -4227,8 +4406,8 @@ commands from within @code{m4}.
 * Platform macros::             Determining the platform
 * Syscmd::                      Executing simple commands
 * Esyscmd::                     Reading the output of commands
-* Sysval::                      Exit codes
-* Maketemp::                    Making names for temporary files
+* Sysval::                      Exit status
+* Maketemp::                    Making temporary files
 @end menu
 
 @node Platform macros
@@ -4299,7 +4478,7 @@ arguments.
 @end deffn
 
 @node Sysval
-@section Exit codes
+@section Exit status
 
 @cindex exit code from shell commands
 @cindex shell commands, exit code from
@@ -4323,7 +4502,7 @@ sysval
 @end example
 
 @node Maketemp
-@section Making names for temporary files
+@section Making temporary files
 
 @cindex temporary file names
 @cindex files, names of temporary
@@ -4357,7 +4536,7 @@ any of the previous chapters.
 @menu
 * Errprint::                    Printing error messages
 * Location::                    Printing current location
-* M4exit::                      Exiting from m4
+* M4exit::                      Exiting from @code{m4}
 * Syncoutput::                  Turning on and off sync lines
 @end menu
 
@@ -4433,7 +4612,7 @@ not followed, e.g., diverted text is not
 (@pxref{M4wrap}) is not reread.
 
 @node Syncoutput
-@section Turning sync lines on and off within @code{m4}
+@section Turning on and off sync lines
 
 @cindex Toggling sync lines within @code{m4}
 @deffn {Builtin (gnu)} syncoutput (@var{truth})
@@ -4661,12 +4840,13 @@ There are also differences in BSD flavor
 is made to summarize these here.
 
 @menu
-* Extensions::                  Extensions in GNU m4
+* Extensions::                  Extensions in @acronym{GNU} M4
 * Incompatibilities::           Other incompatibilities
+* Experiments::                 Experimental features in @acronym{GNU} M4
 @end menu
 
 @node Extensions
-@section Extensions in GNU @code{m4}
+@section Extensions in @acronym{GNU} M4
 
 @cindex GNU extensions
 @cindex @acronym{POSIX}
@@ -4835,7 +5015,7 @@ define the macro @code{__windows__}, whi
 
 
 @node  Experiments
-@chapter Experimental features in GNU M4
+@section Experimental features in @acronym{GNU} M4
 
 Certain features of GNU @code{m4} are experimental.
 
@@ -4877,6 +5057,82 @@ unproven and might go away.  Do not coun
 Some of the examples in this manuals are buggy.  Correctly working
 macros are presented here.
 
+The @code{foreach} macro (@pxref{Foreach}) as presented required the
+user to use parentheses to delineate the list.  This approach is
+quadratic, because the entire list is propagated through each recursion,
+with additional invocations of the shift macro added on each iteration:
+
+@comment FIXME - include(foreach.m4),include(quote.m4)
+@comment options: -d-V
+@example
+define(`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')')dnl
+define(`_arg1', ``$1'')dnl
+define(`_foreach',
+       `ifelse($2, `()', ,
+               `define(`$1',
+                       `_arg1$2')$3`'_foreach(`$1', `(shift$2)',
+                                              `$3')')')dnl
+define(`dquote', ``$@'')
+@result{}
+foreach(`macro', (dquote(symbols)), `regexp(macro, `shift', `\&')')
+@result{}shift
+traceon(`shift')
+@result{}
+foreach(`a', `(1,2,3)', `a
+')
+@result{}1
+@error{}m4trace: -2- shift
+@error{}m4trace: -2- shift
+@result{}2
+@error{}m4trace: -3- shift
+@error{}m4trace: -2- shift
+@error{}m4trace: -3- shift
+@error{}m4trace: -2- shift
+@result{}3
+@error{}m4trace: -4- shift
+@error{}m4trace: -3- shift
+@error{}m4trace: -2- shift
+@result{}
+@end example
+
+An alternative implementation takes a quoted list, with semantics that
+the list is expanded once before iteration, and has the benefit that
+each iteration operates on a shorter list, giving linear performance on
+long lists.  Another way of viewing these semantics is that the
+outermost quotes delineates the list, then each element of the list must
+be quoted as though the list delimiters were not present.  Notice the
+difference when iterating over @code{symbols}; we must use
+@code{dquote_elt} instead of @code{dquote} to get the necessary quoting.
+
+@comment FIXME - include(foreach.m4),include(quote.m4)
+@comment options: -d-V
+@example
+define(`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')')dnl
+define(`_arg1', ``$1'')dnl
+define(`quote', `ifelse(`$#', `0', `', ``$*'')')dnl
+define(`dquote', ``$@@'')dnl
+define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
+                             ```$1'',dquote_elt(shift($@@))')')dnl
+define(`_rest', `ifelse(`$#', `1', , `dquote(shift($@@))')')dnl
+define(`_foreach',
+       `ifelse(quote($2), , ,
+               `define(`$1',
+                       `_arg1($2)')$3`'_foreach(`$1', _rest($2),
+                                                `$3')')')dnl
+foreach(`macro', `dquote_elt(symbols)', `regexp(macro, `shift', `\&')')
+@result{}shift
+traceon(`shift')
+@result{}
+foreach(`a', ``1',`2',`3'', `a
+')
+@result{}1
+@error{}m4trace: -3- shift
+@result{}2
+@error{}m4trace: -3- shift
+@result{}3
+@result{}
+@end example
+
 The @code{cleardivert} macro (@pxref{Cleardiv}) cannot, as it stands, be
 called without arguments to clear all pending diversions.  A macro that
 achieves that as well is:
@@ -4895,7 +5151,7 @@ otherwise they will be passed to undiver
 @c ========================================================== Appendices
 
 @node Copying This Manual
-@appendix Copying This Manual
+@appendix How to make copies of this manual
 @cindex License
 
 @menu
@@ -4905,20 +5161,20 @@ otherwise they will be passed to undiver
 @include fdl.texi
 
 @node Indices
-@appendix Indices
+@appendix Indices of concepts and macros
 
 @menu
 * Concept index::               Index for many concepts
-* Macro index::                 Index for all m4 macros
+* Macro index::                 Index for all @code{m4} macros
 @end menu
 
 @node Concept index
-@appendixsec Concept index
+@appendixsec Index for many concepts
 
 @printindex cp
 
 @node Macro index
-@appendixsec Macro index
+@appendixsec Index for all @code{m4} macros
 
 References are exclusively to the places where a builtin is introduced
 the first time.
Index: m4/m4module.h
===================================================================
RCS file: /sources/m4/m4/m4/m4module.h,v
retrieving revision 1.81
diff -u -p -r1.81 m4module.h
--- m4/m4module.h	28 Aug 2006 12:48:54 -0000	1.81
+++ m4/m4module.h	5 Sep 2006 13:23:51 -0000
@@ -51,9 +51,12 @@ enum {
   /* Set if macro should only be recognized with arguments; may only
      be set if min_args is nonzero.  */
   M4_BUILTIN_BLIND		= (1 << 1),
-  /* set if macro has side effects even when there are too few
+  /* Set if macro has side effects even when there are too few
      arguments; may only be set if min_args is nonzero.  */
-  M4_BUILTIN_SIDE_EFFECT	= (1 << 2)
+  M4_BUILTIN_SIDE_EFFECT	= (1 << 2),
+
+  /* Mask of valid flag bits.  Any other bits must be set to 0.  */
+  M4_BUILTIN_FLAGS_MASK		= (1 << 3) - 1
 };
 
 struct m4_builtin
@@ -253,8 +256,9 @@ extern const m4_builtin *m4_builtin_find
 /* --- MACRO MANAGEMENT --- */
 
 extern void	   m4_macro_expand_input (m4 *);
-extern void	   m4_macro_call	 (m4 *, m4_symbol *, m4_obstack *,
-					  int, m4_symbol_value **);
+extern void	   m4_macro_call	 (m4 *, m4_symbol_value *,
+					  m4_obstack *, int,
+					  m4_symbol_value **);
 
 
 
Index: m4/m4private.h
===================================================================
RCS file: /sources/m4/m4/m4/m4private.h,v
retrieving revision 1.57
diff -u -p -r1.57 m4private.h
--- m4/m4private.h	29 Aug 2006 20:38:30 -0000	1.57
+++ m4/m4private.h	5 Sep 2006 13:23:51 -0000
@@ -167,7 +167,9 @@ struct m4_symbol_value {
   int			flags;
 
   m4_hash *		arg_signature;
-  int			min_args, max_args;
+  unsigned int		min_args;
+  unsigned int		max_args;
+  size_t		pending_expansions;
 
   m4__symbol_type	type;
   union {
@@ -182,6 +184,7 @@ struct m4_symbol_value {
 #define VALUE_ARG_SIGNATURE(T)	((T)->arg_signature)
 #define VALUE_MIN_ARGS(T)	((T)->min_args)
 #define VALUE_MAX_ARGS(T)	((T)->max_args)
+#define VALUE_PENDING(T)	((T)->pending_expansions)
 
 #define SYMBOL_NEXT(S)		(VALUE_NEXT          ((S)->value))
 #define SYMBOL_HANDLE(S)	(VALUE_HANDLE        ((S)->value))
@@ -189,15 +192,18 @@ struct m4_symbol_value {
 #define SYMBOL_ARG_SIGNATURE(S)	(VALUE_ARG_SIGNATURE ((S)->value))
 #define SYMBOL_MIN_ARGS(S)	(VALUE_MIN_ARGS      ((S)->value))
 #define SYMBOL_MAX_ARGS(S)	(VALUE_MAX_ARGS      ((S)->value))
+#define SYMBOL_PENDING(S)	(VALUE_PENDING       ((S)->value))
 
 /* Fast macro versions of symbol table accessor functions,
    that also have an identically named function exported in m4module.h.  */
 #ifdef NDEBUG
 #  define m4_get_symbol_traced(S)	((S)->traced)
 #  define m4_set_symbol_traced(S, V)	((S)->traced = (V))
+#  define m4_get_symbol_value(S)	((S)->value)
+#  define m4_set_symbol_value(S, V)	((S)->value = (V))
 
 #  define m4_symbol_value_create()	xzalloc (sizeof (m4_symbol_value))
-#  define m4_symbol_value_delete(V)	(DELETE (V))
+/* m4_symbol_value_delete is too complex for a simple macro.  */
 
 #  define m4_is_symbol_value_text(V)	((V)->type == M4_SYMBOL_TEXT)
 #  define m4_is_symbol_value_func(V)	((V)->type == M4_SYMBOL_FUNC)
@@ -219,15 +225,15 @@ struct m4_symbol_value {
 
 
 
-/* m4_symbol_value.flags bit masks.  Be sure these are consistent with
-   M4_BUILTIN_* bit masks, so we can copy m4_builtin.flags to
-   m4_symbol_arg.flags.  However, if we ever add blind support to user
-   macros, then these names are better for use in the symbol
-   table:  */
+/* m4_symbol_value.flags bit masks.  Be sure these are a consistent
+   superset of the M4_BUILTIN_* bit masks, so we can copy
+   m4_builtin.flags to m4_symbol_arg.flags.  We can use additional
+   bits for private use.  */
 
 #define VALUE_MACRO_ARGS_BIT		(1 << 0)
 #define VALUE_BLIND_ARGS_BIT		(1 << 1)
 #define VALUE_SIDE_EFFECT_ARGS_BIT	(1 << 2)
+#define VALUE_DELETED_BIT		(1 << 3)
 
 
 struct m4_symbol_arg {
Index: m4/macro.c
===================================================================
RCS file: /sources/m4/m4/m4/macro.c,v
retrieving revision 1.51
diff -u -p -r1.51 macro.c
--- m4/macro.c	31 Aug 2006 03:21:35 -0000	1.51
+++ m4/macro.c	5 Sep 2006 13:23:51 -0000
@@ -35,14 +35,14 @@ static void    expand_token      (m4 *co
 static bool    expand_argument   (m4 *context, m4_obstack *obs,
 				  m4_symbol_value *argp);
 
-static void    process_macro	 (m4 *context, m4_symbol *symbol,
+static void    process_macro	 (m4 *context, m4_symbol_value *value,
 				  m4_obstack *expansion, int argc,
 				  m4_symbol_value **argv);
 
-static void    trace_prepre	 (m4 *context, const char *, int);
-static void    trace_pre	 (m4 *context, const char *, int, int,
+static void    trace_prepre	 (m4 *context, const char *, size_t);
+static void    trace_pre	 (m4 *context, const char *, size_t, int,
 				  m4_symbol_value **);
-static void    trace_post	 (m4 *context, const char *, int, int,
+static void    trace_post	 (m4 *context, const char *, size_t, int,
 				  m4_symbol_value **, const char *);
 
 /* It would be nice if we could use M4_GNUC_PRINTF(2, 3) on
@@ -50,15 +50,15 @@ static void    trace_post	 (m4 *context,
    it would lead to compiler warnings.  */
 static void    trace_format	 (m4 *context, const char *fmt, ...);
 
-static void    trace_header	 (m4 *, int);
+static void    trace_header	 (m4 *, size_t);
 static void    trace_flush	 (m4 *);
 
 
 /* Current recursion level in expand_macro ().  */
-static int expansion_level = 0;
+static size_t expansion_level = 0;
 
 /* The number of the current call of expand_macro ().  */
-static int macro_call_id = 0;
+static size_t macro_call_id = 0;
 
 /* This function reads all input, and expands each token, one at a time.  */
 void
@@ -81,7 +81,10 @@ expand_token (m4 *context, m4_obstack *o
 	      m4__token_type type, m4_symbol_value *token)
 {
   m4_symbol *symbol;
-  char *text = xstrdup (m4_get_symbol_value_text (token));
+  /* Copy name, since expand_macro can consume additional tokens,
+     invalidating the current token.  */
+  char *text = (m4_is_symbol_value_text (token)
+		? xstrdup (m4_get_symbol_value_text (token)) : NULL);
 
   switch (type)
     {				/* TOKSW */
@@ -172,7 +175,9 @@ expand_argument (m4 *context, m4_obstack
 		{
 		  m4_set_symbol_value_text (argp, text);
 		}
-	      return (bool) (m4_has_syntax (M4SYNTAX, *m4_get_symbol_value_text (&token), M4_SYNTAX_COMMA));
+	      return m4_has_syntax (M4SYNTAX,
+				    *m4_get_symbol_value_text (&token),
+				    M4_SYNTAX_COMMA);
 	    }
 
 	  if (m4_has_syntax (M4SYNTAX, *text, M4_SYNTAX_OPEN))
@@ -225,8 +230,13 @@ expand_macro (m4 *context, const char *n
   m4_obstack *expansion;
   const char *expanded;
   bool traced;
-  int my_call_id;
+  size_t my_call_id;
+  m4_symbol_value *value;
 
+  /* Grab the current value of this macro, because it may change while
+     collecting arguments.  */
+  value = m4_get_symbol_value (symbol);
+  VALUE_PENDING (value)++;
   expansion_level++;
   if (expansion_level > m4_get_nesting_limit_opt (context))
     m4_error (context, EXIT_FAILURE, 0, _("\
@@ -236,8 +246,8 @@ recursion limit of %d exceeded, use -L<N
   macro_call_id++;
   my_call_id = macro_call_id;
 
-  traced = (bool) (m4_is_debug_bit (context, M4_DEBUG_TRACE_ALL)
-		      || m4_get_symbol_traced (symbol));
+  traced = (m4_is_debug_bit (context, M4_DEBUG_TRACE_ALL)
+	    || m4_get_symbol_traced (symbol));
 
   obstack_init (&argptr);
   obstack_init (&arguments);
@@ -254,13 +264,16 @@ recursion limit of %d exceeded, use -L<N
     trace_pre (context, name, my_call_id, argc, argv);
 
   expansion = m4_push_string_init (context);
-  m4_macro_call (context, symbol, expansion, argc, argv);
+  m4_macro_call (context, value, expansion, argc, argv);
   expanded = m4_push_string_finish ();
 
   if (traced)
     trace_post (context, name, my_call_id, argc, argv, expanded);
 
   --expansion_level;
+  --VALUE_PENDING (value);
+  if (BIT_TEST (VALUE_FLAGS (value), VALUE_DELETED_BIT))
+    m4_symbol_value_delete (value);
 
   obstack_free (&arguments, NULL);
   obstack_free (&argptr, NULL);
@@ -314,27 +327,27 @@ collect_arguments (m4 *context, const ch
    the call, stored in the ARGV table.  The expansion is left on
    the obstack EXPANSION.  Macro tracing is also handled here.  */
 void
-m4_macro_call (m4 *context, m4_symbol *symbol, m4_obstack *expansion,
+m4_macro_call (m4 *context, m4_symbol_value *value, m4_obstack *expansion,
 	       int argc, m4_symbol_value **argv)
 {
   if (m4_bad_argc (context, argc, argv,
-		   SYMBOL_MIN_ARGS (symbol), SYMBOL_MAX_ARGS (symbol),
-		   BIT_TEST (SYMBOL_FLAGS (symbol),
+		   VALUE_MIN_ARGS (value), VALUE_MAX_ARGS (value),
+		   BIT_TEST (VALUE_FLAGS (value),
 			     VALUE_SIDE_EFFECT_ARGS_BIT)))
     return;
-  if (m4_is_symbol_text (symbol))
+  if (m4_is_symbol_value_text (value))
     {
-      process_macro (context, symbol, expansion, argc, argv);
+      process_macro (context, value, expansion, argc, argv);
     }
-  else if (m4_is_symbol_func (symbol))
+  else if (m4_is_symbol_value_func (value))
     {
-      (*m4_get_symbol_func (symbol)) (context, expansion, argc, argv);
+      (*m4_get_symbol_value_func (value)) (context, expansion, argc, argv);
     }
-  else if (m4_is_symbol_placeholder (symbol))
+  else if (m4_is_symbol_value_placeholder (value))
     {
       m4_warn (context, 0,
 	       _("%s: builtin `%s' requested by frozen file not found"),
-	       M4ARG (0), m4_get_symbol_placeholder (symbol));
+	       M4ARG (0), m4_get_symbol_value_placeholder (value));
     }
   else
     {
@@ -349,13 +362,13 @@ m4_macro_call (m4 *context, m4_symbol *s
    definition, giving the expansion text.  ARGC and ARGV are the arguments,
    as usual.  */
 static void
-process_macro (m4 *context, m4_symbol *symbol, m4_obstack *obs,
+process_macro (m4 *context, m4_symbol_value *value, m4_obstack *obs,
 	       int argc, m4_symbol_value **argv)
 {
   const unsigned char *text;
   int i;
 
-  for (text = m4_get_symbol_text (symbol); *text != '\0';)
+  for (text = m4_get_symbol_value_text (value); *text != '\0';)
     {
       char ch;
 
@@ -370,6 +383,10 @@ process_macro (m4 *context, m4_symbol *s
 	{
 	case '0': case '1': case '2': case '3': case '4':
 	case '5': case '6': case '7': case '8': case '9':
+	  /* FIXME - multidigit arguments should convert over to ${10}
+	     syntax instead of $10; see
+	     http://lists.gnu.org/archive/html/m4-discuss/2006-08/msg00028.html
+	     for more discussion.  */
 	  if (m4_get_posixly_correct_opt (context) || !isdigit(text[1]))
 	    {
 	      i = *text++ - '0';
@@ -377,7 +394,7 @@ process_macro (m4 *context, m4_symbol *s
 	  else
 	    {
 	      char *endp;
-	      i = (int)strtol (text, &endp, 10);
+	      i = (int) strtol (text, &endp, 10);
 	      text = endp;
 	    }
 	  if (i < argc)
@@ -397,7 +414,7 @@ process_macro (m4 *context, m4_symbol *s
 
 	default:
 	  if (m4_get_posixly_correct_opt (context)
-	      || !SYMBOL_ARG_SIGNATURE (symbol))
+	      || !VALUE_ARG_SIGNATURE (value))
 	    {
 	      obstack_1grow (obs, ch);
 	    }
@@ -421,7 +438,7 @@ process_macro (m4 *context, m4_symbol *s
 		{
 		  struct m4_symbol_arg **arg
 		    = (struct m4_symbol_arg **)
-		      m4_hash_lookup (SYMBOL_ARG_SIGNATURE (symbol), key);
+		      m4_hash_lookup (VALUE_ARG_SIGNATURE (value), key);
 
 		  if (arg)
 		    {
@@ -462,8 +479,9 @@ process_macro (m4 *context, m4_symbol *s
    various builtins.  */
 
 /* Tracing output is formatted here, by a simplified printf-to-obstack
-  function trace_format ().  Understands only %S, %s, %d, %l (optional
-  left quote) and %r (optional right quote).  */
+  function trace_format ().  Understands only %S (length-limited
+  string), %s, %d, %z (size_t value), %l (optional left quote) and %r
+  (optional right quote).  */
 static void
 trace_format (m4 *context, const char *fmt, ...)
 {
@@ -471,6 +489,7 @@ trace_format (m4 *context, const char *f
   char ch;
 
   int d;
+  size_t z;
   char nbuf[32];
   const char *s;
   int slen;
@@ -515,6 +534,16 @@ trace_format (m4 *context, const char *f
 	  s = nbuf;
 	  break;
 
+	case 'z':
+	  if (sizeof (size_t) < sizeof (int))
+	    z = va_arg (args, int);
+	  else
+	    z = va_arg (args, size_t);
+	  /* FIXME - it would be nice to assume POSIX-mandated %zu.  */
+	  sprintf (nbuf, "%lu", (unsigned long) z);
+	  s = nbuf;
+	  break;
+
 	default:
 	  s = "";
 	  break;
@@ -535,7 +564,7 @@ trace_format (m4 *context, const char *f
 
 /* Format the standard header attached to all tracing output lines.  */
 static void
-trace_header (m4 *context, int id)
+trace_header (m4 *context, size_t id)
 {
   trace_format (context, "m4trace:");
   if (m4_get_current_line (context))
@@ -545,9 +574,9 @@ trace_header (m4 *context, int id)
       if (m4_is_debug_bit (context, M4_DEBUG_TRACE_LINE))
 	trace_format (context, "%d:", m4_get_current_line (context));
     }
-  trace_format (context, " -%d- ", expansion_level);
+  trace_format (context, " -%z- ", expansion_level);
   if (m4_is_debug_bit (context, M4_DEBUG_TRACE_CALLID))
-    trace_format (context, "id %d: ", id);
+    trace_format (context, "id %z: ", id);
 }
 
 /* Print current tracing line, and clear the obstack.  */
@@ -566,7 +595,7 @@ trace_flush (m4 *context)
 /* Do pre-argument-collction tracing for macro NAME.  Used from
    expand_macro ().  */
 static void
-trace_prepre (m4 *context, const char *name, int id)
+trace_prepre (m4 *context, const char *name, size_t id)
 {
   trace_header (context, id);
   trace_format (context, "%s ...", name);
@@ -576,7 +605,7 @@ trace_prepre (m4 *context, const char *n
 /* Format the parts of a trace line, that can be made before the macro is
    actually expanded.  Used from expand_macro ().  */
 static void
-trace_pre (m4 *context, const char *name, int id,
+trace_pre (m4 *context, const char *name, size_t id,
 	   int argc, m4_symbol_value **argv)
 {
   int i;
@@ -633,7 +662,7 @@ trace_pre (m4 *context, const char *name
 /* Format the final part of a trace line and print it all.  Used from
    expand_macro ().  */
 static void
-trace_post (m4 *context, const char *name, int id,
+trace_post (m4 *context, const char *name, size_t id,
 	    int argc, m4_symbol_value **argv, const char *expanded)
 {
   if (m4_is_debug_bit (context, M4_DEBUG_TRACE_CALL))
Index: m4/module.c
===================================================================
RCS file: /sources/m4/m4/m4/module.c,v
retrieving revision 1.42
diff -u -p -r1.42 module.c
--- m4/module.c	29 Aug 2006 20:38:30 -0000	1.42
+++ m4/module.c	5 Sep 2006 13:23:51 -0000
@@ -150,6 +150,7 @@ install_builtin_table (m4 *context, lt_d
 	  assert (bp->min_args > 0
 		  || (bp->flags & (M4_BUILTIN_BLIND
 				   | M4_BUILTIN_SIDE_EFFECT)) == 0);
+	  assert ((bp->flags & ~M4_BUILTIN_FLAGS_MASK) == 0);
 
 	  m4_set_symbol_value_func (value, bp->func);
 	  VALUE_HANDLE   (value)	= handle;
Index: m4/symtab.c
===================================================================
RCS file: /sources/m4/m4/m4/symtab.c,v
retrieving revision 1.58
diff -u -p -r1.58 symtab.c
--- m4/symtab.c	1 Sep 2006 23:11:05 -0000	1.58
+++ m4/symtab.c	5 Sep 2006 13:23:51 -0000
@@ -51,7 +51,8 @@ struct m4_symbol_table {
 
 static m4_symbol *symtab_fetch		(m4_symbol_table*, const char *);
 static void	  symbol_popval		(m4_symbol *symbol);
-static void *	  symbol_destroy_CB	(m4_symbol_table *symtab, const char *name,
+static void *	  symbol_destroy_CB	(m4_symbol_table *symtab,
+					 const char *name,
 					 m4_symbol *symbol, void *ignored);
 static void *	  arg_destroy_CB	(m4_hash *hash, const void *name,
 					 void *arg, void *ignored);
@@ -140,7 +141,8 @@ symtab_fetch (m4_symbol_table *symtab, c
 /* Remove every symbol that references the given module handle from
    the symbol table.  */
 void
-m4__symtab_remove_module_references (m4_symbol_table *symtab, lt_dlhandle handle)
+m4__symtab_remove_module_references (m4_symbol_table *symtab,
+				     lt_dlhandle handle)
 {
   m4_hash_iterator *place = 0;
 
@@ -165,9 +167,7 @@ m4__symtab_remove_module_references (m4_
 		  VALUE_NEXT (data) = VALUE_NEXT (next);
 
 		  assert (next->type != M4_SYMBOL_PLACEHOLDER);
-		  if (next->type == M4_SYMBOL_TEXT)
-		    free (m4_get_symbol_value_text (next));
-		  free (next);
+		  m4_symbol_value_delete (next);
 		}
 	      else
 		data = next;
@@ -186,8 +186,8 @@ m4__symtab_remove_module_references (m4_
    on every symbol so that m4_symbol_popdef() doesn't try to preserve
    the table entry.  */
 static void *
-symbol_destroy_CB (m4_symbol_table *symtab, const char *name, m4_symbol *symbol,
-		   void *ignored)
+symbol_destroy_CB (m4_symbol_table *symtab, const char *name,
+		   m4_symbol *symbol, void *ignored)
 {
   char *key = xstrdup ((char *) name);
 
@@ -224,7 +224,8 @@ m4_symbol_lookup (m4_symbol_table *symta
    associated with NAME, push the new VALUE on top of the value stack
    for this symbol.  Otherwise create a new association.  */
 m4_symbol *
-m4_symbol_pushdef (m4_symbol_table *symtab, const char *name, m4_symbol_value *value)
+m4_symbol_pushdef (m4_symbol_table *symtab, const char *name,
+		   m4_symbol_value *value)
 {
   m4_symbol *symbol;
 
@@ -289,6 +290,7 @@ m4_symbol_popdef (m4_symbol_table *symta
       }
 }
 
+/* Remove the top-most value from SYMBOL's stack.  */
 static void
 symbol_popval (m4_symbol *symbol)
 {
@@ -301,17 +303,29 @@ symbol_popval (m4_symbol *symbol)
   if (stale)
     {
       symbol->value = VALUE_NEXT (stale);
+      m4_symbol_value_delete (stale);
+    }
+}
 
-      if (VALUE_ARG_SIGNATURE (stale))
+/* Remove VALUE from the symbol table, and mark it as deleted.  If no
+   expansions are pending, reclaim its resources.  */
+void
+m4_symbol_value_delete (m4_symbol_value *value)
+{
+  if (VALUE_PENDING (value) > 0)
+    BIT_SET (VALUE_FLAGS (value), VALUE_DELETED_BIT);
+  else
+    {
+      if (VALUE_ARG_SIGNATURE (value))
 	{
-	  m4_hash_apply (VALUE_ARG_SIGNATURE (stale), arg_destroy_CB, NULL);
-	  m4_hash_delete (VALUE_ARG_SIGNATURE (stale));
+	  m4_hash_apply (VALUE_ARG_SIGNATURE (value), arg_destroy_CB, NULL);
+	  m4_hash_delete (VALUE_ARG_SIGNATURE (value));
 	}
-      if (m4_is_symbol_value_text (stale))
-	free (m4_get_symbol_value_text (stale));
-      else if (m4_is_symbol_value_placeholder (stale))
-	free (m4_get_symbol_value_placeholder (stale));
-      free (stale);
+      if (m4_is_symbol_value_text (value))
+	free (m4_get_symbol_value_text (value));
+      else if (m4_is_symbol_value_placeholder (value))
+	free (m4_get_symbol_value_placeholder (value));
+      free (value);
     }
 }
 
@@ -384,16 +398,20 @@ m4_symbol_value_copy (m4_symbol_value *d
       m4_hash_delete (VALUE_ARG_SIGNATURE (dest));
     }
 
-  /* Copy the valuecontents over, being careful to preserve
+  /* Copy the value contents over, being careful to preserve
      the next pointer.  */
   next = VALUE_NEXT (dest);
-  bcopy (src, dest, sizeof (m4_symbol_value));
+  memcpy (dest, src, sizeof (m4_symbol_value));
   VALUE_NEXT (dest) = next;
 
   /* Caller is supposed to free text token strings, so we have to
      copy the string not just its address in that case.  */
   if (m4_is_symbol_value_text (src))
     m4_set_symbol_value_text (dest, xstrdup (m4_get_symbol_value_text (src)));
+  else if (m4_is_symbol_value_placeholder (src))
+    m4_set_symbol_value_placeholder (dest,
+				     xstrdup (m4_get_symbol_value_placeholder
+					      (src)));
 
   if (VALUE_ARG_SIGNATURE (src))
     VALUE_ARG_SIGNATURE (dest) = m4_hash_dup (VALUE_ARG_SIGNATURE (src),
Index: modules/gnu.c
===================================================================
RCS file: /sources/m4/m4/modules/gnu.c,v
retrieving revision 1.53
diff -u -p -r1.53 gnu.c
--- modules/gnu.c	31 Aug 2006 03:21:35 -0000	1.53
+++ modules/gnu.c	5 Sep 2006 13:23:51 -0000
@@ -507,7 +507,8 @@ M4BUILTIN_HANDLER (indir)
   if (symbol == NULL)
     m4_warn (context, 0, _("%s: undefined macro `%s'"), M4ARG (0), name);
   else
-    m4_macro_call (context, symbol, obs, argc - 1, argv + 1);
+    m4_macro_call (context, m4_get_symbol_value (symbol), obs,
+		   argc - 1, argv + 1);
 }
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.