m4-patches
[Top][All Lists]
Advanced

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

Re: dangling pointer bug and proposed patch


From: Eric Blake
Subject: Re: dangling pointer bug and proposed patch
Date: Tue, 05 Sep 2006 07:25:28 -0600
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.5) Gecko/20060719 Thunderbird/1.5.0.5 Mnenhy/0.7.4.666

-----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  <address@hidden>

        * 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             address@hidden
-----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-----
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
     address@hidden
 
-  + ~/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: address@hidden(...)
-
-    --
-    Akim Demaille
-    address@hidden
-
 
 * FEATURES OR PROBLEMS
 
@@ -74,9 +60,6 @@ for any of these ideas or if you have ot
     --
     Stepan Kasal <address@hidden>
 
-  + 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
address@hidden Names
address@hidden 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
address@hidden Quoted strings
address@hidden 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
address@hidden Comments
address@hidden 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
address@hidden Other tokens
address@hidden 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
address@hidden Input Processing
address@hidden 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
address@hidden Regular Expression Syntax
address@hidden 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.
 
address@hidden status: 1
 @example
 define(
 ^D
address@hidden:stdin:1: ERROR: end of file in argument list
address@hidden:stdin:1: end of file in argument list
 @end example
 
 @node Quoting Arguments
address@hidden Quoting macro arguments
address@hidden 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')
address@hidden
 define(defn(`divnum'), `cannot redefine a builtin token')
address@hidden:stdin:1: Warning: define: invalid macro name ignored
address@hidden: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
address@hidden 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.
+
address@hidden
+define(`f', `1')
address@hidden
+f(define(`f', `2'))
address@hidden
+indir(`f', define(`f', `3'))
address@hidden
+indir(`f', undefine(`f'))
address@hidden:stdin:4: Warning: indir: undefined macro `f'
address@hidden
address@hidden 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
address@hidden Testing macro definitions
address@hidden 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
address@hidden Comparing strings
address@hidden 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})
address@hidden {Builtin (m4)} ifelse (@var{string-1}, @var{string-2}, 
@var{equal}, @w{opt @var{not-equal})}
address@hidden {Builtin (m4)} ifelse (@var{string-1}, @var{string-2}, 
@var{equal}, @dots{})
address@hidden {Builtin (m4)} ifelse (@var{string-1}, @var{string-2}, 
@var{equal}, @
+  @ovar{not-equal})
address@hidden {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
address@hidden, 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
address@hidden 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.
address@hidden 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
address@hidden, 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')
address@hidden
-ifelse(`foo', `foo', `true', `false')
+define(`foo', `bar')
address@hidden
+ifelse(foo, `bar', `true', `false')
 @result{}true
+ifelse(foo, `foo', `true', `false')
address@hidden
address@hidden 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.
+
address@hidden
+define(`foo', `ifelse(`$#', `0', ``$0'', `arguments:$#')')
address@hidden
+foo
address@hidden
+foo()
address@hidden:1
+foo(`a', `b', `c')
address@hidden: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
address@hidden are equal, @code{ifelse} expands into @var{equal}, otherwise
address@hidden 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')
address@hidden:stdin:1: Warning: ifelse: extra arguments ignored: 5 > 4
address@hidden
+ifelse(`foo', `bar', `third', `gnu', `gnats', `sixth')
address@hidden
 ifelse(`foo', `bar', `third', `gnu', `gnats', `sixth', `seventh')
 @result{}seventh
+ifelse(`foo', `bar', `3', `gnu', `gnats', `6', `7', `8')
address@hidden:stdin:4: Warning: ifelse: extra arguments ignored: 8 > 7
address@hidden
 @end example
 
-The macro @code{ifelse} is recognized only with parameters.
address@hidden 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.
 
address@hidden Loops
address@hidden Loops and recursion
address@hidden Shift
address@hidden 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:
 
address@hidden {Builtin (m4)} shift (@dots{})
address@hidden takes any number of arguments, and expands to all but the first
-argument, separated by commas, with each argument quoted.
address@hidden {Builtin (m4)} shift (@var{arg1}, @dots{})
+Takes any number of arguments, and expands to all its arguments except
address@hidden, separated by commas, with each argument quoted.
+
+The macro @code{shift} is recognized only with parameters.
address@hidden 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:
+
address@hidden 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:
+
address@hidden Composite quote (@dots{})
address@hidden Composite dquote (@dots{})
address@hidden 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.
address@hidden deffn
+
+Here is an implementation, along with an example usage.
+
address@hidden FIXME - these macros are worth reusing in other examples;
address@hidden factor them into examples/quote.m4.
address@hidden
+define(`quote', `ifelse(`$#', `0', `', ``$*'')')dnl
+define(`dquote', ``$@@'')dnl
+define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
+                             ```$1'',dquote_elt(shift($@@))')')dnl
+-quote-dquote-dquote_elt-
address@hidden
+-quote(`1')-dquote(`1')-dquote_elt(`1')-
address@hidden'-`1'-
+-quote(`1',`2')-dquote(`1',`2')-dquote_elt(`1',`2')-
address@hidden,2-`1',`2'-`1',`2'-
+dquote(dquote_elt(`1',`2'))
address@hidden'',``2''
+dquote_elt(dquote(`1',`2'))
address@hidden',`2''
address@hidden example
+
+The last two lines show that when given two arguments, @code{dquote}
+results in one string, while @code{dquote_elt} results in two.
+
address@hidden Forloop
address@hidden Iteration by counting
 
address@hidden forloops
address@hidden 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.
address@hidden FIXME - this section still needs some work done
+
address@hidden 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
address@hidden  Any definition of @var{iterator} prior to this
+invocation is restored.
address@hidden deffn
 
+It can, for example, be used for simple counting:
+
address@hidden 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.
 
address@hidden FIXME - it would be nice to introduce foreach here, and even
address@hidden give an example like this for finding defined macros that meet
address@hidden a certain pattern (see examples/foreach.m4):
address@hidden define(`quote', ``$@'')
address@hidden foreach(`macro', (quote(symbols)),
address@hidden         `regexp(macro, `.*_.*', ``\&',')')
address@hidden Foreach
address@hidden Iteration by list contents
+
address@hidden for each loops
address@hidden loops, list iteration
address@hidden iterating over lists
+Here is an example of a loop macro that implements list iteration.
address@hidden FIXME - this section still needs some work done
+
address@hidden 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}.
address@hidden 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
address@hidden  Any definition of @var{iterator} prior to this
+invocation is restored.
address@hidden deffn
+
+As an example, this displays each word in a list inside of a sentence.
+
address@hidden FIXME - include(`foreach.m4')
address@hidden ignore
address@hidden
+foreach(`x', `(foo, bar, foobar)', `Word was: x
+')
address@hidden was: foo
address@hidden was: bar
address@hidden was: foobar
address@hidden 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
address@hidden
+
address@hidden FIXME - include(foreach.m4),include(quote.m4)
address@hidden
+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', ``$@@'')
address@hidden
+foreach(`macro', (dquote(symbols)), `regexp(macro, `.*if.*', ``\&',')')
address@hidden,ifelse,shift,
address@hidden 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
address@hidden Changing comment delimiters
address@hidden Changing the comment delimiters
 
 @cindex changing comment delimiters
 @cindex comment delimiters, changing
@@ -3101,7 +3280,7 @@ macro calls.
 
 
 @node M4wrap
address@hidden Saving input
address@hidden 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
address@hidden Runtime Dynamic Modules
address@hidden 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
address@hidden Formatted output
address@hidden 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
address@hidden Evaluating integer or rational expressions
address@hidden 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
address@hidden Running shell commands
address@hidden 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
address@hidden Exit codes
address@hidden Exit status
 
 @cindex exit code from shell commands
 @cindex shell commands, exit code from
@@ -4323,7 +4502,7 @@ sysval
 @end example
 
 @node Maketemp
address@hidden Making names for temporary files
address@hidden 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
address@hidden Turning sync lines on and off within @code{m4}
address@hidden 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
address@hidden Extensions in GNU @code{m4}
address@hidden Extensions in @acronym{GNU} M4
 
 @cindex GNU extensions
 @cindex @acronym{POSIX}
@@ -4835,7 +5015,7 @@ define the macro @code{__windows__}, whi
 
 
 @node  Experiments
address@hidden Experimental features in GNU M4
address@hidden 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:
+
address@hidden FIXME - include(foreach.m4),include(quote.m4)
address@hidden options: -d-V
address@hidden
+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', ``$@'')
address@hidden
+foreach(`macro', (dquote(symbols)), `regexp(macro, `shift', `\&')')
address@hidden
+traceon(`shift')
address@hidden
+foreach(`a', `(1,2,3)', `a
+')
address@hidden
address@hidden: -2- shift
address@hidden: -2- shift
address@hidden
address@hidden: -3- shift
address@hidden: -2- shift
address@hidden: -3- shift
address@hidden: -2- shift
address@hidden
address@hidden: -4- shift
address@hidden: -3- shift
address@hidden: -2- shift
address@hidden
address@hidden 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
address@hidden instead of @code{dquote} to get the necessary quoting.
+
address@hidden FIXME - include(foreach.m4),include(quote.m4)
address@hidden options: -d-V
address@hidden
+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', `\&')')
address@hidden
+traceon(`shift')
address@hidden
+foreach(`a', ``1',`2',`3'', `a
+')
address@hidden
address@hidden: -3- shift
address@hidden
address@hidden: -3- shift
address@hidden
address@hidden
address@hidden 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
address@hidden Copying This Manual
address@hidden 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
address@hidden Indices
address@hidden 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
address@hidden Concept index
address@hidden Index for many concepts
 
 @printindex cp
 
 @node Macro index
address@hidden Macro index
address@hidden 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);
 }
 
 

reply via email to

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