m4-patches
[Top][All Lists]
Advanced

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

argv_ref patch 23: allow tracing of indirect macros


From: Eric Blake
Subject: argv_ref patch 23: allow tracing of indirect macros
Date: Fri, 09 May 2008 06:16:29 -0600
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.14) Gecko/20080421 Thunderbird/2.0.0.14 Mnenhy/0.7.5.666

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Next round of porting.  A while ago, I noticed an entry in the autoconf
TODO file that mentioned an m4 bug
(http://lists.gnu.org/archive/html/m4-discuss/2007-09/msg00000.html).
With a little bit of shuffling when macro tracing prints output, it was
rather easy to make indir now trace its macros.  This change (along with
stage 22) alters how trace output appears, but I have tested that it
didn't break autoconf, which is one of the projects that wants to parse
trace output.  The changes include:
| -dc now prints two lines, not three, per macro (the second and third
line were always adjacent anyway prior to this patch)
| -de now prints something even when a macro results in a builtin token or
empty string
| debugmode(V)debugmode no longer truncates the tracing output of the
second invocation, where the mode was reduced mid-expansion
| the documentation is improved and test cases added, so that it becomes
harder to change behavior down the road without detecting the change.

On the other hand, I think there are still some things that should be
considered before 1.6:
| -db could be added, which enables \ escaping of output so that tracing
never includes raw newlines or terminal control characters
| on the master branch, -dc shows the macro definition that will be used
prior to collecting the arguments, this is probably worth porting to 1.6
| is it worth making traceon(defn(`define')) legal, in which case
builtin(`define',...) would trace the builtin define?

There is a slight memory penalty (the argv struct is a bit larger), but
less munging of global variables, so the overall speed really didn't
change.  A future patch will also make m4_error/m4_warn use the call
context that tracing now uses, for even less global variable munging.

2008-05-09  Eric Blake  <address@hidden>

~        Stage 23: allow tracing of indirect macro calls.
~        Track all trace information as part of the argv struct, rather
~        than temporarily resetting global state.  Teach indir to trace
~        macros that it invokes.
~        Memory impact: slight penalty, due to larger argv struct.
~        Speed impact: none noticed.
        * src/m4.h (struct call_info): New struct.
        (trace_prepre, trace_pre, trace_post, push_string_finish)
        (input_print, call_macro): Alter signatures.
        (arg_info): New prototype.
        (input_block): Delete...
        * src/input.c (input_block): ...and make typedef local again.
        (push_string_init): Initialize length.
        (push_string_finish, input_print): Change signature, so that
        printing can be done before finalization.
        * src/macro.c (struct macro_arguments): Add info member.
        (collect_arguments, make_argv_ref): Manage new field.
        (expand_macro): Change call context management.  Move tracing...
        (call_macro): ...here, and remove redundant parameter.
        (arg_info): New function.
        * src/debug.c (trace_format): Delete unused modifiers.
        (trace_header, trace_flush, trace_prepre, trace_pre, trace_post):
        Change signatures for stacked trace messages, and for using call
        context.
        * src/builtin.c (m4_builtin, m4_indir): Update callers.
        * src/m4.c (usage): Update debugmode flag summary.
        * doc/m4.texinfo (Defn): Update examples to match new behavior.
        (Trace): Mention new capability of indir.
        (Debug Levels): Enhance text for 'c' and 'x', update examples.
        (Incompatibilities): Mention risk of parsing trace output.
        * examples/null.m4: Enhance test.
        * examples/null.err: Update expected output.
        * NEWS: Mention these changes.
        Reported by Akim Demaille in the autoconf TODO file in 2000.

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

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkgkQJ0ACgkQ84KuGfSFAYCv4wCaAxON5wUs6GcLmj+Bh0p47RC+
6oEAoJ9zuTV435RnzSPqiiqHlLhLT65Q
=41bk
-----END PGP SIGNATURE-----
From b9738adbaaf9c8f02c97c6179b9e595a0d28121c Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 8 May 2008 20:39:16 -0600
Subject: [PATCH] Stage 23: allow tracing of indirect macro calls.

* m4/m4module.h (m4_input_block): Remove.
(m4_call_info): New opaque type.
(m4_trace_prepare, m4_arg_info): New prototypes.
(m4_macro_call, m4_push_string_finish, m4_input_print): Change
prototypes.
* m4/m4private.h (struct m4_macro_args): Add info field.
(struct m4_call_info): New structure.
(m4_arg_info): New accessor.
* m4/input.c (m4_input_block): Make typedef local.
(m4_push_string_init): Initialize length.
(m4_push_string_finish, m4_input_print): Change signature, so that
printing can be done before finalization.
(struct input_funcs): Add parameter to print_func.
(file_print, string_print, composite_print): Adjust accordingly.
* m4/macro.c (trace_header, trace_flush, trace_pre, trace_post):
Change signatures for stacked trace messages, and for using call
context.
(trace_prepre): Export and rename...
(m4_trace_prepare): ...to this, for use by indir.  Alter signature
to use call context.
(collect_arguments): Alter signature, to manage new field.
(expand_macro): Change call context management.  Move tracing...
(m4_macro_call): ...here.  Remove redundant parameter.
(m4_arg_argc): New function.
(m4_make_argv_ref): Replace unused skip parameter with new trace
parameter; manage new field.
* modules/gnu.c (builtin, indir): Adjust callers.
* src/main.c (usage): Update debugmode flag summary.
* tests/null.m4: Enhance test.
* tests/null.err: Update expected output.
* tests/macros.at (Tracing Hanoi Towers): Likewise.
* doc/m4.texinfo (Trace): Mention more about trace formatting.
(Debugmode): Enhance description of 'c' and 'x'.  Enhance test to
cover line numbering details in traces.
(Debuglen): Enhance test to cover indir tracing.
* NEWS: Mention these changes.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog       |   45 +++++++++
 NEWS            |   19 +++-
 doc/m4.texinfo  |   97 +++++++++++++++----
 m4/input.c      |   83 ++++++++--------
 m4/m4module.h   |   20 ++--
 m4/m4private.h  |   19 ++++
 m4/macro.c      |  288 +++++++++++++++++++++++++++++--------------------------
 modules/gnu.c   |   10 +-
 src/main.c      |    6 +-
 tests/macros.at |    2 +-
 tests/null.err  |    2 +
 tests/null.m4   |    7 +-
 12 files changed, 379 insertions(+), 219 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2f39b0e..c20ac6e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,48 @@
+2008-05-08  Eric Blake  <address@hidden>
+
+       Stage 23: allow tracing of indirect macro calls.
+       Track all trace information as part of the argv struct, rather
+       than temporarily resetting global state.  Teach indir to trace
+       macros that it invokes.
+       Memory impact: slight penalty, due to larger argv struct.
+       Speed impact: none noticed.
+       * m4/m4module.h (m4_input_block): Remove.
+       (m4_call_info): New opaque type.
+       (m4_trace_prepare, m4_arg_info): New prototypes.
+       (m4_macro_call, m4_push_string_finish, m4_input_print): Change
+       prototypes.
+       * m4/m4private.h (struct m4_macro_args): Add info field.
+       (struct m4_call_info): New structure.
+       (m4_arg_info): New accessor.
+       * m4/input.c (m4_input_block): Make typedef local.
+       (m4_push_string_init): Initialize length.
+       (m4_push_string_finish, m4_input_print): Change signature, so that
+       printing can be done before finalization.
+       (struct input_funcs): Add parameter to print_func.
+       (file_print, string_print, composite_print): Adjust accordingly.
+       * m4/macro.c (trace_header, trace_flush, trace_pre, trace_post):
+       Change signatures for stacked trace messages, and for using call
+       context.
+       (trace_prepre): Export and rename...
+       (m4_trace_prepare): ...to this, for use by indir.  Alter signature
+       to use call context.
+       (collect_arguments): Alter signature, to manage new field.
+       (expand_macro): Change call context management.  Move tracing...
+       (m4_macro_call): ...here.  Remove redundant parameter.
+       (m4_arg_argc): New function.
+       (m4_make_argv_ref): Replace unused skip parameter with new trace
+       parameter; manage new field.
+       * modules/gnu.c (builtin, indir): Adjust callers.
+       * src/main.c (usage): Update debugmode flag summary.
+       * tests/null.m4: Enhance test.
+       * tests/null.err: Update expected output.
+       * tests/macros.at (Tracing Hanoi Towers): Likewise.
+       * doc/m4.texinfo (Trace): Mention more about trace formatting.
+       (Debugmode): Enhance description of 'c' and 'x'.  Enhance test to
+       cover line numbering details in traces.
+       (Debuglen): Enhance test to cover indir tracing.
+       * NEWS: Mention these changes.
+
 2008-05-07  Eric Blake  <address@hidden>
 
        Test for traceon regression just fixed in branch-1.6.
diff --git a/NEWS b/NEWS
index fd3bb09..ea0385b 100644
--- a/NEWS
+++ b/NEWS
@@ -158,10 +158,8 @@ promoted to 2.0.
     builtin traces actions related to module loading and unloading, and
     affects `dumpdef' and trace output to show where builtins come from.
     New `s' flag shows the entire stack of `pushdef' definitions during
-    `dumpdef'.  The `c' flag has been updated to output two lines instead
-    of three (since the last two had always been paired), and to add
-    information to the first line to show the definition of the macro being
-    expanded.  The 'e' flag has been updated to output non-text expansions.
+    `dumpdef'.  The `c' flag has been updated to add information to the
+    first line to show the definition of the macro being expanded.
 
 *** The `divert' builtin now accepts an optional second argument of text
     that is immediately placed on the new diversion, regardless of whether
@@ -248,6 +246,19 @@ promoted to 2.0.
    To simulate 1.4.x behavior, use:
      pushdef(`defn', `ifdef(`$1', `builtin(`defn', `$1')')')
 
+** Enhance the `indir' builtin to trace indirect macros, where the trace
+   is requested via `traceon' or the command-line option `-t'.  Previously,
+   it was impossible to trace macro names such as `foo-bar' which could
+   only be invoked indirectly, without relying on global tracing (such as
+   with `debugmode(`t')') or the experimental `changeword'.
+
+** Aspects of tracing output that were previously undocumented have been
+   slightly altered, and the effect of the builtin `debugmode' on trace
+   output is more fully documented.  As POSIX does not specify trace output
+   format, parsing such output is inherently fragile in the first place.
+   The intent is that future M4 versions will not change documented trace
+   output without adding additional `debugmode' flags.
+
 ** Enhance the `ifdef', `ifelse', and `shift' builtins, as well as all
    user macros, to transparently handle builtin tokens generated by `defn'.
 
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 7bdfa66..8c4c237 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -3958,7 +3958,7 @@ foo # untraced
 @result{}bar # untraced
 @end example
 
-However, @acronym{GNU} M4 1.4.7 and earlier had slightly different
+However, @acronym{GNU} M4 prior to 2.0 had slightly different
 semantics, where @code{traceon} without arguments only affected symbols
 that were defined at that moment, and @code{traceoff} without arguments
 stopped all tracing, even when tracing was requested by macro name.  The
@@ -4006,7 +4006,56 @@ a b
 @end example
 
 @xref{Debugmode}, for information on controlling the details of the
-display.
+display.  The format of the trace output is not specified by
address@hidden, and varies between implementations of @code{m4}.
+
+Starting with M4 1.6, tracing also works via @code{indir}
+(@pxref{Indir}).  However, since tracing is an attribute tracked by
+macro names, and @code{builtin} bypasses macro names (@pxref{Builtin}),
+it is not possible for @code{builtin} to trace which subsidiary builtin
+it invokes.  If you are worried about tracking all invocations of a
+given builtin, you should also trace @code{builtin}, or enable global
+tracing (the @samp{t} debug level, @pxref{Debugmode}).
+
address@hidden
+$ @kbd{m4 -d}
+define(`my_defn', defn(`defn'))undefine(`defn')
address@hidden
+define(`foo', `bar')traceon(`foo', `defn', `my_defn')
address@hidden
+foo
address@hidden: -1- foo -> `bar'
address@hidden
+indir(`foo')
address@hidden: -1- foo -> `bar'
address@hidden
+my_defn(`foo')
address@hidden: -1- my_defn(`foo') -> ``bar''
address@hidden
+indir(`my_defn', `foo')
address@hidden: -1- my_defn(`foo') -> ``bar''
address@hidden
+builtin(`defn', `foo')
address@hidden
+debugmode(`+cxt')
address@hidden
+builtin(`defn', builtin(`shift', `', `foo'))
address@hidden: -1- id 12: builtin ... = <builtin>
address@hidden: -2- id 13: builtin ... = <builtin>
address@hidden: -2- id 13: builtin(`shift', `', `foo') -> ``foo''
address@hidden: -1- id 12: builtin(`defn', `foo') -> ``bar''
address@hidden
+indir(`my_defn', indir(`shift', `', `foo'))
address@hidden: -1- id 14: indir ... = <indir>
address@hidden: -2- id 15: indir ... = <indir>
address@hidden: -2- id 15: shift ... = <shift>
address@hidden: -2- id 15: shift(`', `foo') -> ``foo''
address@hidden: -2- id 15: indir(`shift', `', `foo') -> ``foo''
address@hidden: -1- id 14: my_defn ... = <defn>
address@hidden: -1- id 14: my_defn(`foo') -> ``bar''
address@hidden: -1- id 14: indir(`my_defn', `foo') -> ``bar''
address@hidden
address@hidden example
 
 @node Debugmode
 @section Controlling debugging options
@@ -4033,10 +4082,13 @@ invoking the macro.  Arguments are subject to length 
truncation
 specified by @code{debuglen} (@pxref{Debuglen}).
 
 @item c
-In trace output, show an additional line for each macro call, prior to
-the arguments being collected, that shows the definition of the macro
-that will be used for the expansion.  The definition is subject to
-length truncation specified by @code{debuglen} (@pxref{Debuglen}).
+In trace output, show an additional line for each macro call, when the
+macro is seen, but before the arguments are collected, and show the
+definition of the macro that will be used for the expansion.  By
+default, only one line is printed, after all arguments are collected and
+the expansion determined.  The definition is subject to length
+truncation specified by @code{debuglen} (@pxref{Debuglen}).  This is
+often used with the @samp{x} flag.
 
 @item e
 In trace output, show the expansion of each macro call.  The expansion
@@ -4082,7 +4134,11 @@ arguments.
 
 @item x
 In trace output, add a unique `macro call id' to each line of the trace
-output.  This is useful in connection with the @samp{c} flag above.
+output.  This is useful in connection with the @samp{c} flag above, to
+match where a macro is first recognized with where it is finally
+expanded, in spite of intermediate expansions that occur while
+collecting arguments.  It can also be used in isolation to determine how
+many macros have been expanded.
 
 @item V
 A shorthand for all of the above flags.
@@ -4128,15 +4184,18 @@ debugmode()
 foo
 @error{}m4trace: -1- foo -> `FOO'
 @result{}FOO
-debugmode(`V')
address@hidden
-foo(`BAR')
address@hidden:stdin:6: -1- id 6: foo ... = `FOO$1'
address@hidden:stdin:6: -1- id 6: foo(`BAR') -> `FOOBAR'
+debugmode(`V')debugmode(`-q')
address@hidden:stdin:5: -1- id 6: debugmode ... = <debugmode>@address@hidden
address@hidden:stdin:5: -1- id 6: debugmode(`-q') -> `'
address@hidden
+foo(
+`BAR')
address@hidden:stdin:6: -1- id 7: foo ... = FOO$1
address@hidden:stdin:6: -1- id 7: foo(BAR) -> FOOBAR
 @result{}FOOBAR
 debugmode
address@hidden:stdin:7: -1- id 7: debugmode ... = <debugmode>@address@hidden
address@hidden:stdin:7: -1- id 7: debugmode ->@w{ }
address@hidden:stdin:8: -1- id 8: debugmode ... = <debugmode>@address@hidden
address@hidden:stdin:8: -1- id 8: debugmode ->@w{ }
 @result{}
 foo
 @error{}m4trace: -1- foo
@@ -4144,10 +4203,10 @@ foo
 debugmode(`+clmx')
 @result{}
 foo(divnum)
address@hidden:10: -1- id 10: foo ... = FOO$1
address@hidden:10: -2- id 11: divnum ... = <divnum>@address@hidden
address@hidden:10: -2- id 11: divnum
address@hidden:10: -1- id 10: foo
address@hidden:11: -1- id 11: foo ... = FOO$1
address@hidden:11: -2- id 12: divnum ... = <divnum>@address@hidden
address@hidden:11: -2- id 12: divnum
address@hidden:11: -1- id 11: foo
 @result{}FOO0
 debugmode(`-m')
 @result{}
@@ -4224,7 +4283,7 @@ define(`echo', `$@@')
 echo(`1', `long string')
 @error{}m4trace: -1- echo(`1', `long s...') -> ``1',`l...'
 @result{}1,long string
-echo(defn(`changequote'))
+indir(`echo', defn(`changequote'))
 @error{}m4trace: -2- defn(`change...') -> `<changequote>'
 @error{}m4trace: -1- echo(<changequote>) -> ``<changequote>''
 @result{}
diff --git a/m4/input.c b/m4/input.c
index 1f89916..a021e70 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -92,23 +92,28 @@
    maintains its own notion of the current file and line, so swapping
    between input blocks must update the context accordingly.  */
 
+typedef struct m4_input_block m4_input_block;
+
 static int     file_peek               (m4_input_block *, m4 *, bool);
 static int     file_read               (m4_input_block *, m4 *, bool, bool,
                                         bool);
 static void    file_unget              (m4_input_block *, int);
 static bool    file_clean              (m4_input_block *, m4 *, bool);
-static void    file_print              (m4_input_block *, m4 *, m4_obstack *);
+static void    file_print              (m4_input_block *, m4 *, m4_obstack *,
+                                        int);
 static int     string_peek             (m4_input_block *, m4 *, bool);
 static int     string_read             (m4_input_block *, m4 *, bool, bool,
                                         bool);
 static void    string_unget            (m4_input_block *, int);
-static void    string_print            (m4_input_block *, m4 *, m4_obstack *);
+static void    string_print            (m4_input_block *, m4 *, m4_obstack *,
+                                        int);
 static int     composite_peek          (m4_input_block *, m4 *, bool);
 static int     composite_read          (m4_input_block *, m4 *, bool, bool,
                                         bool);
 static void    composite_unget         (m4_input_block *, int);
 static bool    composite_clean         (m4_input_block *, m4 *, bool);
-static void    composite_print         (m4_input_block *, m4 *, m4_obstack *);
+static void    composite_print         (m4_input_block *, m4 *, m4_obstack *,
+                                        int);
 static int     eof_peek                (m4_input_block *, m4 *, bool);
 static int     eof_read                (m4_input_block *, m4 *, bool, bool,
                                         bool);
@@ -159,7 +164,7 @@ struct input_funcs
 
   /* Add a representation of the input block to the obstack, for use
      in trace expansion output.  */
-  void (*print_func)   (m4_input_block *, m4 *, m4_obstack *);
+  void (*print_func)   (m4_input_block *, m4 *, m4_obstack *, int);
 };
 
 /* A block of input to be scanned.  */
@@ -337,9 +342,11 @@ file_clean (m4_input_block *me, m4 *context, bool cleanup)
 }
 
 static void
-file_print (m4_input_block *me, m4 *context, m4_obstack *obs)
+file_print (m4_input_block *me, m4 *context M4_GNUC_UNUSED, m4_obstack *obs,
+           int debug_level M4_GNUC_UNUSED)
 {
   const char *text = me->file;
+  assert (obstack_object_size (current_input) == 0);
   obstack_grow (obs, "<file: ", strlen ("<file: "));
   obstack_grow (obs, text, strlen (text));
   obstack_1grow (obs, '>');
@@ -417,12 +424,15 @@ string_unget (m4_input_block *me, int ch)
 }
 
 static void
-string_print (m4_input_block *me, m4 *context, m4_obstack *obs)
+string_print (m4_input_block *me, m4 *context, m4_obstack *obs,
+             int debug_level)
 {
-  bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
+  bool quote = (debug_level & M4_DEBUG_TRACE_QUOTE) != 0;
   size_t arg_length = m4_get_max_debug_arg_length_opt (context);
 
-  m4_shipout_string_trunc (obs, me->u.u_s.str, me->u.u_s.len,
+  assert (!me->u.u_s.len);
+  m4_shipout_string_trunc (obs, (char *) obstack_base (current_input),
+                          obstack_object_size (current_input),
                           quote ? m4_get_syntax_quotes (M4SYNTAX) : NULL,
                           &arg_length);
 }
@@ -442,6 +452,7 @@ m4_push_string_init (m4 *context)
   next->funcs = &string_funcs;
   next->file = m4_get_current_file (context);
   next->line = m4_get_current_line (context);
+  next->u.u_s.len = 0;
 
   return current_input;
 }
@@ -610,24 +621,19 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, 
size_t level, bool inuse)
 }
 
 /* Last half of m4_push_string ().  If next is now NULL, a call to
-   m4_push_file () or m4_push_builtin () has pushed a different input
-   block to the top of the stack.  If the new object is void, we do
-   not push it.  The function m4_push_string_finish () returns the
-   opaque finished object, whether that is still a string or has been
-   replaced by a file or builtin; this object can then be used in
-   m4_input_print () during tracing.  This pointer is only for
-   temporary use, since reading the next token might release the
-   memory used for the object.  */
-m4_input_block *
+   m4_push_file () has pushed a different input block to the top of
+   the stack.  Otherwise, all unfinished text on the obstack returned
+   from push_string_init is collected into the input stack.  If the
+   new object is empty, we do not push it.  */
+void
 m4_push_string_finish (void)
 {
-  m4_input_block *ret = NULL;
   size_t len = obstack_object_size (current_input);
 
   if (next == NULL)
     {
       assert (!len);
-      return isp;
+      return;
     }
 
   if (len || next->funcs == &composite_funcs)
@@ -641,13 +647,12 @@ m4_push_string_finish (void)
        m4__make_text_link (current_input, &next->u.u_c.chain,
                            &next->u.u_c.end);
       next->prev = isp;
-      ret = isp = next;
+      isp = next;
       input_change = true;
     }
   else
     obstack_free (current_input, next);
   next = NULL;
-  return ret;
 }
 
 
@@ -846,14 +851,16 @@ composite_clean (m4_input_block *me, m4 *context, bool 
cleanup)
 }
 
 static void
-composite_print (m4_input_block *me, m4 *context, m4_obstack *obs)
+composite_print (m4_input_block *me, m4 *context, m4_obstack *obs,
+                int debug_level)
 {
-  bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
+  bool quote = (debug_level & M4_DEBUG_TRACE_QUOTE) != 0;
   size_t maxlen = m4_get_max_debug_arg_length_opt (context);
   m4__symbol_chain *chain = me->u.u_c.chain;
   const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX);
-  bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE);
+  bool module = (debug_level & M4_DEBUG_TRACE_MODULE) != 0;
   bool done = false;
+  size_t len = obstack_object_size (current_input);
 
   if (quote)
     m4_shipout_string (context, obs, quotes->str1, quotes->len1, false);
@@ -885,6 +892,9 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
        }
       chain = chain->next;
     }
+  if (len)
+    m4_shipout_string_trunc (obs, (char *) obstack_base (current_input), len,
+                            NULL, &maxlen);
   if (quote)
     m4_shipout_string (context, obs, quotes->str2, quotes->len2, false);
 }
@@ -988,25 +998,16 @@ eof_unget (m4_input_block *me M4_GNUC_UNUSED, int ch)
 
 
 /* When tracing, print a summary of the contents of the input block
-   created by push_string_init/push_string_finish to OBS.  */
+   created by push_string_init/push_string_finish to OBS.  Use
+   DEBUG_LEVEL to determine whether to add quotes or module
+   designations.  */
 void
-m4_input_print (m4 *context, m4_obstack *obs, m4_input_block *input)
+m4_input_print (m4 *context, m4_obstack *obs, int debug_level)
 {
-  assert (context && obs);
-  if (input == NULL)
-    {
-      if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
-       {
-         const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX);
-         obstack_grow (obs, quotes->str1, quotes->len1);
-         obstack_grow (obs, quotes->str2, quotes->len2);
-       }
-    }
-  else
-    {
-      assert (input->funcs->print_func);
-      input->funcs->print_func (input, context, obs);
-    }
+  m4_input_block *block = next ? next : isp;
+  assert (context && obs && (debug_level & M4_DEBUG_TRACE_EXPANSION));
+  assert (block->funcs->print_func);
+  block->funcs->print_func (block, context, obs, debug_level);
 }
 
 /* Return an obstack ready for direct expansion of wrapup text, and
diff --git a/m4/m4module.h b/m4/m4module.h
index 21a8339..96b76ea 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -32,15 +32,14 @@ BEGIN_C_DECLS
 
 typedef struct m4              m4;
 typedef struct m4_builtin      m4_builtin;
+typedef struct m4_call_info    m4_call_info;
 typedef struct m4_macro                m4_macro;
-typedef struct m4_symbol       m4_symbol;
-typedef struct m4_symbol_value m4_symbol_value;
-typedef struct m4_input_block  m4_input_block;
-typedef struct m4_module       m4_module;
 typedef struct m4_macro_args   m4_macro_args;
-typedef struct m4_string_pair  m4_string_pair;
-
+typedef struct m4_module       m4_module;
 typedef struct obstack         m4_obstack;
+typedef struct m4_string_pair  m4_string_pair;
+typedef struct m4_symbol       m4_symbol;
+typedef struct m4_symbol_value m4_symbol_value;
 
 typedef void   m4_builtin_func  (m4 *, m4_obstack *, size_t, m4_macro_args *);
 
@@ -347,8 +346,9 @@ extern m4_symbol_value      *m4_builtin_find_by_func 
(m4_module *,
 
 extern void    m4_macro_expand_input   (m4 *);
 extern void    m4_macro_call           (m4 *, m4_symbol_value *, m4_obstack *,
-                                        size_t, m4_macro_args *);
+                                        m4_macro_args *);
 extern size_t  m4_arg_argc             (m4_macro_args *);
+extern const m4_call_info *m4_arg_info (m4_macro_args *);
 extern m4_symbol_value *m4_arg_symbol  (m4_macro_args *, size_t);
 extern bool    m4_is_arg_text          (m4_macro_args *, size_t);
 extern bool    m4_is_arg_func          (m4_macro_args *, size_t);
@@ -414,6 +414,8 @@ extern void m4_debug_message_prefix (m4 *);
 extern void    m4_debug_message        (m4 *, int, const char *, ...)
   M4_GNUC_PRINTF (3, 4);
 
+extern void    m4_trace_prepare        (m4 *, const m4_call_info *,
+                                        m4_symbol_value *);
 
 
 /* --- REGEXP SYNTAX --- */
@@ -496,9 +498,9 @@ extern      void    m4_skip_line    (m4 *context, const 
char *);
 extern void    m4_push_file    (m4 *, FILE *, const char *, bool);
 extern void    m4_push_builtin (m4 *, m4_obstack *, m4_symbol_value *);
 extern m4_obstack      *m4_push_string_init    (m4 *);
-extern m4_input_block  *m4_push_string_finish  (void);
+extern void    m4_push_string_finish   (void);
 extern bool    m4_pop_wrapup   (m4 *);
-extern void    m4_input_print  (m4 *, m4_obstack *, m4_input_block *);
+extern void    m4_input_print  (m4 *, m4_obstack *, int);
 
 
 
diff --git a/m4/m4private.h b/m4/m4private.h
index 7e1f7a8..526a0ef 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -313,6 +313,9 @@ struct m4_macro_args
      during parsing or any token is potentially unsafe and requires a
      rescan.  */
   unsigned int quote_age;
+  /* The context of the macro call during expansion, and NULL in a
+     back-reference.  */
+  m4_call_info *info;
   size_t level; /* Which obstack owns this argv.  */
   size_t arraylen; /* True length of allocated elements in array.  */
   /* Used as a variable-length array, storing information about each
@@ -332,6 +335,21 @@ struct m4__macro_arg_stacks
   void *argv_base;     /* Location for clearing the argv obstack.  */
 };
 
+/* Opaque structure for managing call context information.  Contains
+   the context used in tracing and error messages that was valid at
+   the start of the macro expansion, even if argument collection
+   changes global context in the meantime.  */
+struct m4_call_info
+{
+  const char *file;    /* The file containing the macro invocation.  */
+  int line;            /* The line the macro was called on.  */
+  int call_id;         /* The unique sequence call id of the macro.  */
+  int trace : 1;       /* True to trace this macro.  */
+  int debug_level : 31;        /* The debug level for tracing the macro call.  
*/
+  const char *name;    /* The macro name.  */
+  size_t name_len;     /* The length of name.  */
+};
+
 extern size_t  m4__adjust_refcount     (m4 *, size_t, bool);
 extern bool    m4__arg_adjust_refcount (m4 *, m4_macro_args *, bool);
 extern void    m4__push_arg_quote      (m4 *, m4_obstack *, m4_macro_args *,
@@ -553,6 +571,7 @@ extern      bool            m4__next_token_is_open (m4 *);
    that also have an identically named function exported in m4module.h.  */
 #ifdef NDEBUG
 # define m4_arg_argc(A)                (A)->argc
+# define m4_arg_info(A)                (A)->info
 # define m4_arg_scratch(C)                             \
   ((C)->arg_stacks[(C)->expansion_level - 1].argv)
 #endif /* NDEBUG */
diff --git a/m4/macro.c b/m4/macro.c
index f5bec18..1e88299 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -122,9 +122,8 @@
    memory left on the obstack while waiting for refcounts to drop.
 */
 
-static m4_macro_args *collect_arguments (m4 *, const char *, size_t,
-                                        m4_symbol *, m4_obstack *,
-                                        m4_obstack *);
+static m4_macro_args *collect_arguments (m4 *, m4_call_info *, m4_symbol *,
+                                        m4_obstack *, m4_obstack *);
 static void    expand_macro      (m4 *, const char *, size_t, m4_symbol *);
 static bool    expand_token      (m4 *, m4_obstack *, m4__token_type,
                                  m4_symbol_value *, int, bool);
@@ -133,16 +132,13 @@ static bool    expand_argument   (m4 *, m4_obstack *, 
m4_symbol_value *,
 static void    process_macro    (m4 *, m4_symbol_value *, m4_obstack *, int,
                                  m4_macro_args *);
 
-static void    trace_prepre     (m4 *, const char *, size_t,
-                                 m4_symbol_value *);
-static void    trace_pre        (m4 *, size_t, m4_macro_args *);
-static void    trace_post       (m4 *, size_t, m4_macro_args *,
-                                 m4_input_block *, bool);
+static unsigned int trace_pre   (m4 *, m4_macro_args *);
+static void    trace_post       (m4 *, unsigned int, const m4_call_info *);
 
 static void    trace_format     (m4 *, const char *, ...)
   M4_GNUC_PRINTF (2, 3);
-static void    trace_header     (m4 *, size_t);
-static void    trace_flush      (m4 *);
+static unsigned int trace_header (m4 *, const m4_call_info *);
+static void    trace_flush      (m4 *, unsigned int);
 
 
 /* The number of the current call of expand_macro ().  */
@@ -443,22 +439,12 @@ expand_macro (m4 *context, const char *name, size_t len, 
m4_symbol *symbol)
   void *argv_base;             /* Base of stack->argv on entry.  */
   m4_macro_args *argv;         /* Arguments to the called macro.  */
   m4_obstack *expansion;       /* Collects the macro's expansion.  */
-  m4_input_block *expanded;    /* The resulting expansion, for tracing.  */
-  bool traced;                 /* True if this macro is traced.  */
-  bool trace_expansion = false;        /* True if trace and debugmode(`e').  */
-  size_t my_call_id;           /* Sequence id for this macro.  */
   m4_symbol_value *value;      /* Original value of this macro.  */
   size_t level;                        /* Expansion level of this macro.  */
   m4__macro_arg_stacks *stack; /* Storage for this macro.  */
+  m4_call_info info;           /* Context of this macro call.  */
 
-  /* Report errors at the location where the open parenthesis (if any)
-     was found, but after expansion, restore global state back to the
-     location of the close parenthesis.  This is safe since we
-     guarantee that macro expansion does not alter the state of
-     current_file/current_line (dnl, include, and sinclude are special
-     cased in the input engine to ensure this fact).  */
-  const char *loc_open_file = m4_get_current_file (context);
-  int loc_open_line = m4_get_current_line (context);
+  /* TODO - use m4_call_info to avoid temporary munging of global state.  */
   const char *loc_close_file;
   int loc_close_line;
 
@@ -496,10 +482,14 @@ expand_macro (m4 *context, const char *name, size_t len, 
m4_symbol *symbol)
      collecting arguments.  Likewise, grab any state needed during
      tracing.  */
   value = m4_get_symbol_value (symbol);
-  traced = (m4_is_debug_bit (context, M4_DEBUG_TRACE_ALL)
-           || m4_get_symbol_traced (symbol));
-  if (traced)
-    trace_expansion = m4_is_debug_bit (context, M4_DEBUG_TRACE_EXPANSION);
+  info.file = m4_get_current_file (context);
+  info.line = m4_get_current_line (context);
+  info.call_id = ++macro_call_id;
+  info.trace = (m4_is_debug_bit (context, M4_DEBUG_TRACE_ALL)
+               || m4_get_symbol_traced (symbol));
+  info.debug_level = m4_get_debug_level_opt (context);
+  info.name = name;
+  info.name_len = len;
 
   /* Prepare for macro expansion.  */
   VALUE_PENDING (value)++;
@@ -507,13 +497,9 @@ expand_macro (m4 *context, const char *name, size_t len, 
m4_symbol *symbol)
     m4_error (context, EXIT_FAILURE, 0, NULL, _("\
 recursion limit of %zu exceeded, use -L<N> to change it"),
              m4_get_nesting_limit_opt (context));
-  my_call_id = ++macro_call_id;
 
-  if (traced && m4_is_debug_bit (context, M4_DEBUG_TRACE_CALL))
-    trace_prepre (context, name, my_call_id, value);
-
-  argv = collect_arguments (context, name, len, symbol, stack->args,
-                           stack->argv);
+  m4_trace_prepare (context, &info, value);
+  argv = collect_arguments (context, &info, symbol, stack->args, stack->argv);
   /* Since collect_arguments can invalidate stack by reallocating
      context->arg_stacks during a recursive expand_macro call, we must
      reset it here.  */
@@ -523,22 +509,16 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
   /* The actual macro call.  */
   loc_close_file = m4_get_current_file (context);
   loc_close_line = m4_get_current_line (context);
-  m4_set_current_file (context, loc_open_file);
-  m4_set_current_line (context, loc_open_line);
-
-  if (traced)
-    trace_pre (context, my_call_id, argv);
-
+  m4_set_current_file (context, info.file);
+  m4_set_current_line (context, info.line);
   expansion = m4_push_string_init (context);
-  m4_macro_call (context, value, expansion, argv->argc, argv);
-  expanded = m4_push_string_finish ();
-
-  if (traced)
-    trace_post (context, my_call_id, argv, expanded, trace_expansion);
+  m4_macro_call (context, value, expansion, argv);
+  m4_push_string_finish ();
 
   /* Cleanup.  */
   m4_set_current_file (context, loc_close_file);
   m4_set_current_line (context, loc_close_line);
+  argv->info = NULL;
 
   --context->expansion_level;
   --VALUE_PENDING (value);
@@ -558,8 +538,8 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
          obstack_free (stack->args, args_scratch);
          if (debug_macro_level & PRINT_ARGCOUNT_CHANGES)
            xfprintf (stderr, "m4debug: -%d- `%s' in use, level=%d, "
-                     "refcount=%zu, argcount=%zu\n", my_call_id, argv->argv0,
-                     level, stack->refcount, stack->argcount);
+                     "refcount=%zu, argcount=%zu\n", info.call_id,
+                     argv->argv0, level, stack->refcount, stack->argcount);
        }
       else
        {
@@ -570,15 +550,13 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
     }
 }
 
-/* Collect all the arguments to a call of the macro SYMBOL (called
-   NAME, with length LEN).  The arguments are stored on the obstack
-   ARGUMENTS and a table of pointers to the arguments on the obstack
-   argv_stack.  Return the object describing all of the macro
-   arguments.  */
+/* Collect all the arguments to a call of the macro SYMBOL, with call
+   context INFO.  The arguments are stored on the obstack ARGUMENTS
+   and a table of pointers to the arguments on ARGV_STACK.  Return the
+   object describing all of the macro arguments.  */
 static m4_macro_args *
-collect_arguments (m4 *context, const char *name, size_t len,
-                  m4_symbol *symbol, m4_obstack *arguments,
-                  m4_obstack *argv_stack)
+collect_arguments (m4 *context, m4_call_info *info, m4_symbol *symbol,
+                  m4_obstack *arguments, m4_obstack *argv_stack)
 {
   m4_symbol_value token;
   m4_symbol_value *tokenp;
@@ -594,23 +572,24 @@ collect_arguments (m4 *context, const char *name, size_t 
len,
   args.has_func = false;
   /* Must copy here, since we are consuming tokens, and since symbol
      table can be changed during argument collection.  */
-  args.argv0 = (char *) obstack_copy0 (arguments, name, len);
-  args.argv0_len = len;
+  args.argv0 = (char *) obstack_copy0 (arguments, info->name, info->name_len);
+  args.argv0_len = info->name_len;
   args.quote_age = m4__quote_age (M4SYNTAX);
+  args.info = info;
   args.level = context->expansion_level - 1;
   args.arraylen = 0;
   obstack_grow (argv_stack, &args, offsetof (m4_macro_args, array));
-  name = args.argv0;
+  info->name = args.argv0;
 
   if (m4__next_token_is_open (context))
     {
       /* Gobble parenthesis, then collect arguments.  */
-      m4__next_token (context, &token, NULL, NULL, false, name);
+      m4__next_token (context, &token, NULL, NULL, false, info->name);
       do
        {
          tokenp = (m4_symbol_value *) obstack_alloc (arguments,
                                                      sizeof *tokenp);
-         more_args = expand_argument (context, arguments, tokenp, name);
+         more_args = expand_argument (context, arguments, tokenp, info->name);
 
          if ((m4_is_symbol_value_text (tokenp)
               && !m4_get_symbol_value_len (tokenp))
@@ -669,33 +648,41 @@ collect_arguments (m4 *context, const char *name, size_t 
len,
 
 
 /* The actual call of a macro is handled by m4_macro_call ().
-   m4_macro_call () is passed a SYMBOL, whose type is used to
+   m4_macro_call () is passed a symbol VALUE, whose type is used to
    call either a builtin function, or the user macro expansion
-   function process_macro ().  There are ARGC arguments to
-   the call, stored in the ARGV table.  The expansion is left on
-   the obstack EXPANSION.  Macro tracing is also handled here.  */
+   function process_macro ().  The arguments are provided by 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_value *value, m4_obstack *expansion,
-              size_t argc, m4_macro_args *argv)
+              m4_macro_args *argv)
 {
-  if (m4_bad_argc (context, argc, argv->argv0,
-                  VALUE_MIN_ARGS (value), VALUE_MAX_ARGS (value),
-                  BIT_TEST (VALUE_FLAGS (value),
-                            VALUE_SIDE_EFFECT_ARGS_BIT)))
-    return;
-  if (m4_is_symbol_value_text (value))
-    process_macro (context, value, expansion, argc, argv);
-  else if (m4_is_symbol_value_func (value))
-    m4_get_symbol_value_func (value) (context, expansion, argc, argv);
-  else if (m4_is_symbol_value_placeholder (value))
-    m4_warn (context, 0, M4ARG (0),
-            _("builtin `%s' requested by frozen file not found"),
-            m4_get_symbol_value_placeholder (value));
-  else
+  unsigned int trace_start = 0;
+
+  if (argv->info->trace)
+    trace_start = trace_pre (context, argv);
+  if (!m4_bad_argc (context, argv->argc, argv->argv0,
+                   VALUE_MIN_ARGS (value), VALUE_MAX_ARGS (value),
+                   BIT_TEST (VALUE_FLAGS (value),
+                             VALUE_SIDE_EFFECT_ARGS_BIT)))
     {
-      assert (!"m4_macro_call");
-      abort ();
+      if (m4_is_symbol_value_text (value))
+       process_macro (context, value, expansion, argv->argc, argv);
+      else if (m4_is_symbol_value_func (value))
+       m4_get_symbol_value_func (value) (context, expansion, argv->argc,
+                                         argv);
+      else if (m4_is_symbol_value_placeholder (value))
+       m4_warn (context, 0, M4ARG (0),
+                _("builtin `%s' requested by frozen file not found"),
+                m4_get_symbol_value_placeholder (value));
+      else
+       {
+         assert (!"m4_macro_call");
+         abort ();
+       }
     }
+  if (argv->info->trace)
+    trace_post (context, trace_start, argv->info);
 }
 
 /* This function handles all expansion of user defined and predefined
@@ -881,91 +868,106 @@ trace_format (m4 *context, const char *fmt, ...)
   va_end (args);
 }
 
-/* Format the standard header attached to all tracing output lines.  */
-static void
-trace_header (m4 *context, size_t id)
+/* Format the standard header attached to all tracing output lines,
+   using the context in INFO as appropriate.  Return the offset into
+   the trace obstack where this particular trace begins.  */
+static unsigned int
+trace_header (m4 *context, const m4_call_info *info)
 {
+  unsigned int result = obstack_object_size (&context->trace_messages);
   trace_format (context, "m4trace:");
-  if (m4_get_current_line (context))
-    {
-      if (m4_is_debug_bit (context, M4_DEBUG_TRACE_FILE))
-       trace_format (context, "%s:", m4_get_current_file (context));
-      if (m4_is_debug_bit (context, M4_DEBUG_TRACE_LINE))
-       trace_format (context, "%d:", m4_get_current_line (context));
-    }
+  if (info->debug_level & M4_DEBUG_TRACE_FILE)
+    trace_format (context, "%s:", info->file);
+  if (info->debug_level & M4_DEBUG_TRACE_LINE)
+    trace_format (context, "%d:", info->line);
   trace_format (context, " -%zu- ", context->expansion_level);
-  if (m4_is_debug_bit (context, M4_DEBUG_TRACE_CALLID))
-    trace_format (context, "id %zu: ", id);
+  if (info->debug_level & M4_DEBUG_TRACE_CALLID)
+    trace_format (context, "id %zu: ", info->call_id);
+  return result;
 }
 
-/* Print current tracing line, and clear the obstack.  */
+/* Print current tracing line starting at offset START, as returned
+   from an earlier trace_header(), then clear the obstack.  */
 static void
-trace_flush (m4 *context)
+trace_flush (m4 *context, unsigned int start)
 {
-  char *line;
-
-  obstack_1grow (&context->trace_messages, '\n');
-  obstack_1grow (&context->trace_messages, '\0');
-  line = obstack_finish (&context->trace_messages);
-  if (m4_get_debug_file (context))
-    fputs (line, m4_get_debug_file (context));
-  obstack_free (&context->trace_messages, line);
+  char *str;
+  FILE *file = m4_get_debug_file (context);
+
+  if (file)
+    {
+      obstack_1grow (&context->trace_messages, '\0');
+      str = (char *) obstack_base (&context->trace_messages);
+      fprintf (file, "%s\n", &str[start]);
+    }
+  start -= obstack_object_size (&context->trace_messages);
+  obstack_blank (&context->trace_messages, start);
 }
 
-/* Do pre-argument-collection tracing for macro NAME.  Used from
-   expand_macro ().  */
-static void
-trace_prepre (m4 *context, const char *name, size_t id, m4_symbol_value *value)
+/* Do pre-argument-collection tracing for the macro described in INFO.
+   Should be called prior to m4_macro_call().  */
+void
+m4_trace_prepare (m4 *context, const m4_call_info *info,
+                 m4_symbol_value *value)
 {
   const m4_string_pair *quotes = NULL;
   size_t arg_length = m4_get_max_debug_arg_length_opt (context);
-  bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE);
+  bool module = (info->debug_level & M4_DEBUG_TRACE_MODULE) != 0;
 
-  if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
+  if (info->debug_level & M4_DEBUG_TRACE_QUOTE)
     quotes = m4_get_syntax_quotes (M4SYNTAX);
-  trace_header (context, id);
-  trace_format (context, "%s ... = ", name);
-  m4__symbol_value_print (context, value, &context->trace_messages, quotes,
-                         false, NULL, &arg_length, module);
-  trace_flush (context);
+  if (info->trace && (info->debug_level & M4_DEBUG_TRACE_CALL))
+    {
+      unsigned int start = trace_header (context, info);
+      trace_format (context, "%s ... = ", info->name);
+      m4__symbol_value_print (context, value, &context->trace_messages, quotes,
+                             false, NULL, &arg_length, module);
+      trace_flush (context, start);
+    }
 }
 
-/* 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, size_t id, m4_macro_args *argv)
+/* Format the parts of a trace line that are known via ARGV before the
+   macro is actually expanded.  Used from m4_macro_call().  Return the
+   start of the current trace, in case other traces are printed before
+   this trace completes trace_post.  */
+static unsigned int
+trace_pre (m4 *context, m4_macro_args *argv)
 {
-  trace_header (context, id);
+  int trace_level = argv->info->debug_level;
+  unsigned int start = trace_header (context, argv->info);
+
+  assert (argv->info->trace);
   trace_format (context, "%s", M4ARG (0));
 
-  if (1 < m4_arg_argc (argv) && m4_is_debug_bit (context, M4_DEBUG_TRACE_ARGS))
+  if (1 < m4_arg_argc (argv) && (trace_level & M4_DEBUG_TRACE_ARGS))
     {
       const m4_string_pair *quotes = NULL;
       size_t arg_length = m4_get_max_debug_arg_length_opt (context);
-      bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE);
+      bool module = (trace_level & M4_DEBUG_TRACE_MODULE) != 0;
 
-      if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
+      if (trace_level & M4_DEBUG_TRACE_QUOTE)
        quotes = m4_get_syntax_quotes (M4SYNTAX);
       trace_format (context, "(");
       m4__arg_print (context, &context->trace_messages, argv, 1, quotes, false,
                     NULL, ", ", &arg_length, true, module);
       trace_format (context, ")");
     }
+  return start;
 }
 
-/* Format the final part of a trace line and print it all.  Used from
-   expand_macro ().  */
+/* If requested by the trace state in INFO, format the final part of a
+   trace line.  Then print all collected information from START,
+   returned from a prior trace_pre().  Used from m4_macro_call ().  */
 static void
-trace_post (m4 *context, size_t id, m4_macro_args *argv,
-           m4_input_block *expanded, bool trace_expansion)
+trace_post (m4 *context, unsigned int start, const m4_call_info *info)
 {
-  if (trace_expansion)
+  assert (info->trace);
+  if (info->debug_level & M4_DEBUG_TRACE_EXPANSION)
     {
       trace_format (context, " -> ");
-      m4_input_print (context, &context->trace_messages, expanded);
+      m4_input_print (context, &context->trace_messages, info->debug_level);
     }
-
-  trace_flush (context);
+  trace_flush (context, start);
 }
 
 
@@ -1586,23 +1588,24 @@ m4__arg_print (m4 *context, m4_obstack *obs, 
m4_macro_args *argv, size_t arg,
 /* Create a new argument object using the same obstack as ARGV; thus,
    the new object will automatically be freed when the original is
    freed.  Explicitly set the macro name (argv[0]) from ARGV0 with
-   length ARGV0_LEN.  If SKIP, set argv[1] of the new object to
-   argv[2] of the old, otherwise the objects share all arguments.  If
+   length ARGV0_LEN, and discard argv[1] of the wrapped ARGV.  If
    FLATTEN, any builtins in ARGV are flattened to an empty string when
-   referenced through the new object.  */
+   referenced through the new object.  If TRACE, then trace the macro
+   regardless of global trace state.  */
 m4_macro_args *
 m4_make_argv_ref (m4 *context, m4_macro_args *argv, const char *argv0,
-                 size_t argv0_len, bool skip, bool flatten)
+                 size_t argv0_len, bool flatten, bool trace)
 {
   m4_macro_args *new_argv;
   m4_symbol_value *value;
   m4_symbol_value *new_value;
-  size_t arg = skip ? 2 : 1;
   m4_obstack *obs = m4_arg_scratch (context);
+  m4_call_info *info;
 
+  info = (m4_call_info *) obstack_copy (obs, argv->info, sizeof *info);
   new_value = (m4_symbol_value *) obstack_alloc (obs, sizeof *value);
   value = make_argv_ref (context, new_value, obs, context->expansion_level - 1,
-                        argv, arg, flatten, NULL);
+                        argv, 2, flatten, NULL);
   if (!value)
     {
       obstack_free (obs, new_value);
@@ -1626,11 +1629,15 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, 
const char *argv0,
       new_argv->flatten = flatten;
       new_argv->has_func = argv->has_func;
     }
-  new_argv->argc = argv->argc - (arg - 1);
+  new_argv->argc = argv->argc - 1;
   new_argv->inuse = false;
   new_argv->argv0 = argv0;
   new_argv->argv0_len = argv0_len;
   new_argv->quote_age = argv->quote_age;
+  new_argv->info = info;
+  info->trace = (argv->info->debug_level & M4_DEBUG_TRACE_ALL) || trace;
+  info->name = argv0;
+  info->name_len = argv0_len;
   new_argv->level = argv->level;
   return new_argv;
 }
@@ -1780,6 +1787,17 @@ m4_arg_argc (m4_macro_args *argv)
   return argv->argc;
 }
 
+/* Given ARGV, return the call context in effect when argument
+   collection began.  Only safe to call while the macro is being
+   expanded.  */
+#undef m4_arg_info
+const m4_call_info *
+m4_arg_info (m4_macro_args *argv)
+{
+  assert (argv->info);
+  return argv->info;
+}
+
 /* Return an obstack useful for scratch calculations, and which will
    not interfere with macro expansion.  The obstack will be reset when
    expand_macro completes.  */
diff --git a/modules/gnu.c b/modules/gnu.c
index f0d3a44..c28a100 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -447,7 +447,7 @@ M4BUILTIN_HANDLER (builtin)
              m4_macro_args *new_argv;
              bool flatten = (bp->flags & M4_BUILTIN_FLATTEN_ARGS) != 0;
              new_argv = m4_make_argv_ref (context, argv, name, M4ARGLEN (1),
-                                          true, flatten);
+                                          flatten, false);
              bp->func (context, obs, argc - 1, new_argv);
            }
          free (value);
@@ -680,10 +680,12 @@ M4BUILTIN_HANDLER (indir)
       else
        {
          m4_macro_args *new_argv;
+         m4_symbol_value *value = m4_get_symbol_value (symbol);
          new_argv = m4_make_argv_ref (context, argv, name, M4ARGLEN (1),
-                                      true, m4_symbol_flatten_args (symbol));
-         m4_macro_call (context, m4_get_symbol_value (symbol), obs,
-                        argc - 1, new_argv);
+                                      m4_symbol_flatten_args (symbol),
+                                      m4_get_symbol_traced (symbol));
+         m4_trace_prepare (context, m4_arg_info (new_argv), value);
+         m4_macro_call (context, value, obs, new_argv);
        }
     }
 }
diff --git a/src/main.c b/src/main.c
index 7c3b9cc..e500046 100644
--- a/src/main.c
+++ b/src/main.c
@@ -156,7 +156,7 @@ Debugging:\n\
       fputs (_("\
 FLAGS is any of:\n\
   a   show actual arguments in trace\n\
-  c   show definition line in trace\n\
+  c   show collection line in trace\n\
   e   show expansion in trace\n\
   f   include current input file name in trace and debug\n\
   i   show changes in input files in debug\n\
@@ -165,9 +165,9 @@ FLAGS is any of:\n\
       fputs (_("\
   m   show module information in trace, debug, and dumpdef\n\
   p   show results of path searches in debug\n\
-  q   quote values as necessary in dumpdef and trace, useful with a or e\n\
+  q   quote values in dumpdef and trace, useful with a or e\n\
   s   show full stack of pushdef values in dumpdef\n\
-  t   trace all macro calls, regardless of named traceon state\n\
+  t   trace all macro calls, regardless of per-macro traceon state\n\
   x   include unique macro call id in trace, useful with c\n\
   V   shorthand for all of the above flags\n\
 "), stdout);
diff --git a/tests/macros.at b/tests/macros.at
index c4db9e6..fe42518 100644
--- a/tests/macros.at
+++ b/tests/macros.at
@@ -356,7 +356,7 @@ m4trace: -2- eval(`1<=1') -> `1'
 m4trace: -1- ifelse(`1', `1', `move(auxilliary, destination)', 
`_hanoi(decr(1), auxilliary, source, destination)move(auxilliary, 
destination)_hanoi(decr(1), source, destination, auxilliary)') -> 
`move(auxilliary, destination)'
 m4trace: -1- move(`auxilliary', `destination') -> `Move one disk from 
`auxilliary' to `destination'.
 '
-m4trace: -1- debugmode -> @&t@
+m4trace: -1- debugmode -> `'
 m4trace: -1- _hanoi(2, source, destination, auxilliary) -> 
ifelse(eval(`2'<=1), 1, `move(source, destination)',
 `_hanoi(decr(2), source, auxilliary, destination)move(source, 
destination)_hanoi(decr(2), auxilliary, destination, source)')
 m4trace: -1- _hanoi(1, source, auxilliary, destination) -> 
ifelse(eval(`1'<=1), 1, `move(source, auxilliary)',
diff --git a/tests/null.err b/tests/null.err
index 61518e8..d825818 100644
--- a/tests/null.err
+++ b/tests/null.err
@@ -1 +1,3 @@
 errprint: -- --
+traceon: fixme
+m4trace: -1- -(`-
diff --git a/tests/null.m4 b/tests/null.m4
index 90e339f..851d665 100644
--- a/tests/null.m4
+++ b/tests/null.m4
@@ -127,9 +127,10 @@ dnl Passed to syscmd: not tested yet. NUL truncates 
string, needs to warn
 dnl Sysval takes no arguments, and never produces NUL.
 dnl Passed to traceoff:
 traceoff(`--', `')dnl
-dnl Macro name and arguments of traceon: not tested yet. Trace output needs 
quoting
-`traceon:' indir(`--', `--')
-dnl Defined text of traceon: not tested yet. Needs tracing indirect macros
+dnl Macro name and arguments of traceon: not perfect yet
+`traceon:' errprint(`traceon: fixme
+')traceon(`--')indir(`--', `--')
+dnl Defined text of traceon: not tested yet, needs quoting
 dnl First argument of translit: not tested yet
 dnl Single character in other arguments of translit: not tested yet
 dnl Character ranges of translit: not tested yet
-- 
1.5.5.1

From bc4cf0aa6f3aa46da1cb46c25ded92362f6dba22 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 13 Dec 2007 10:37:51 -0700
Subject: [PATCH] Stage 23: allow tracing of indirect macro calls.

* src/m4.h (struct call_info): New struct.
(trace_prepre, trace_pre, trace_post, push_string_finish)
(input_print, call_macro): Alter signatures.
(arg_info): New prototype.
(input_block): Delete...
* src/input.c (input_block): ...and make typedef local again.
(push_string_init): Initialize length.
(push_string_finish, input_print): Change signature, so that
printing can be done before finalization.
* src/macro.c (struct macro_arguments): Add info member.
(collect_arguments, make_argv_ref): Manage new field.
(expand_macro): Change call context management.  Move tracing...
(call_macro): ...here, and remove redundant parameter.
(arg_info): New function.
* src/debug.c (trace_format): Delete unused modifiers.
(trace_header, trace_flush, trace_prepre, trace_pre, trace_post):
Change signatures for stacked trace messages, and for using call
context.
* src/builtin.c (m4_builtin, m4_indir): Update callers.
* src/m4.c (usage): Update debugmode flag summary.
* doc/m4.texinfo (Defn): Update examples to match new behavior.
(Trace): Mention new capability of indir.
(Debug Levels): Enhance text for 'c' and 'x', update examples.
(Incompatibilities): Mention risk of parsing trace output.
* examples/null.m4: Enhance test.
* examples/null.err: Update expected output.
* NEWS: Mention these changes.
Reported by Akim Demaille in the autoconf TODO file in 2000.

(cherry picked from commit ec804cba9ceeef7ca863d163eeae3fca669ccd16)

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog         |   37 ++++++++++++
 NEWS              |   13 ++++
 doc/m4.texinfo    |  110 +++++++++++++++++++++++++++-------
 examples/null.err |    2 +
 examples/null.m4  |    7 +-
 src/builtin.c     |    8 ++-
 src/debug.c       |  168 ++++++++++++++++++++++++-----------------------------
 src/input.c       |   42 +++++++------
 src/m4.c          |   95 ++++++++++++++++--------------
 src/m4.h          |   30 +++++++---
 src/macro.c       |  120 ++++++++++++++++++++++----------------
 11 files changed, 391 insertions(+), 241 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d5b23b6..0a40526 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,40 @@
+2008-05-09  Eric Blake  <address@hidden>
+
+       Stage 23: allow tracing of indirect macro calls.
+       Track all trace information as part of the argv struct, rather
+       than temporarily resetting global state.  Teach indir to trace
+       macros that it invokes.
+       Memory impact: slight penalty, due to larger argv struct.
+       Speed impact: none noticed.
+       * src/m4.h (struct call_info): New struct.
+       (trace_prepre, trace_pre, trace_post, push_string_finish)
+       (input_print, call_macro): Alter signatures.
+       (arg_info): New prototype.
+       (input_block): Delete...
+       * src/input.c (input_block): ...and make typedef local again.
+       (push_string_init): Initialize length.
+       (push_string_finish, input_print): Change signature, so that
+       printing can be done before finalization.
+       * src/macro.c (struct macro_arguments): Add info member.
+       (collect_arguments, make_argv_ref): Manage new field.
+       (expand_macro): Change call context management.  Move tracing...
+       (call_macro): ...here, and remove redundant parameter.
+       (arg_info): New function.
+       * src/debug.c (trace_format): Delete unused modifiers.
+       (trace_header, trace_flush, trace_prepre, trace_pre, trace_post):
+       Change signatures for stacked trace messages, and for using call
+       context.
+       * src/builtin.c (m4_builtin, m4_indir): Update callers.
+       * src/m4.c (usage): Update debugmode flag summary.
+       * doc/m4.texinfo (Defn): Update examples to match new behavior.
+       (Trace): Mention new capability of indir.
+       (Debug Levels): Enhance text for 'c' and 'x', update examples.
+       (Incompatibilities): Mention risk of parsing trace output.
+       * examples/null.m4: Enhance test.
+       * examples/null.err: Update expected output.
+       * NEWS: Mention these changes.
+       Reported by Akim Demaille in the autoconf TODO file in 2000.
+
 2008-05-07  Eric Blake  <address@hidden>
 
        Fix traceon regression introduced 2006-06-06.
diff --git a/NEWS b/NEWS
index 9bde89c..bdb7665 100644
--- a/NEWS
+++ b/NEWS
@@ -41,6 +41,19 @@ Foundation, Inc.
    To simulate 1.4.x behavior, use:
      pushdef(`defn', `ifdef(`$1', `builtin(`defn', `$1')')')
 
+** Enhance the `indir' builtin to trace indirect macros, where the trace
+   is requested via `traceon' or the command-line option `-t'.  Previously,
+   it was impossible to trace macro names such as `foo-bar' which could
+   only be invoked indirectly, without relying on global tracing (such as
+   with `debugmode(`t')') or the experimental `changeword'.
+
+** Aspects of tracing output that were previously undocumented have been
+   slightly altered, and the effect of the builtin `debugmode' on trace
+   output is more fully documented.  As POSIX does not specify trace output
+   format, parsing such output is inherently fragile in the first place.
+   The intent is that future M4 versions will not change documented trace
+   output without adding additional `debugmode' flags.
+
 ** Enhance the `ifdef', `ifelse', and `shift' builtins, as well as all
    user macros, to transparently handle builtin tokens generated by `defn'.
 
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 30db58a..3131e9a 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -2300,7 +2300,7 @@ defn(`a', `divnum', `a')
 define(`mydivnum', defn(`divnum', `divnum'))mydivnum
 @error{}m4trace: -2- defn(`divnum', `divnum') -> `<divnum><divnum>'
 @error{}m4:stdin:5: Warning: define: cannot concatenate builtins
address@hidden: -1- define(`mydivnum', `<divnum><divnum>')
address@hidden: -1- define(`mydivnum', `<divnum><divnum>') -> `'
 @result{}
 traceoff(`defn', `define')dumpdef(`mydivnum')
 @error{}mydivnum:@tabchar{}`'
@@ -3711,10 +3711,10 @@ $ @kbd{m4 -d}
 traceon(`traceon')
 @result{}
 traceon(`traceoff')
address@hidden: -1- traceon(`traceoff')
address@hidden: -1- traceon(`traceoff') -> `'
 @result{}
 traceoff(`traceoff')
address@hidden: -1- traceoff(`traceoff')
address@hidden: -1- traceoff(`traceoff') -> `'
 @result{}
 traceoff(`traceon')
 @result{}
@@ -3733,7 +3733,56 @@ m4_eval(m4_divnum)
 @end example
 
 @xref{Debug Levels}, for information on controlling the details of the
-display.
+display.  The format of the trace output is not specified by
address@hidden, and varies between implementations of @code{m4}.
+
+Starting with M4 1.6, tracing also works via @code{indir}
+(@pxref{Indir}).  However, since tracing is an attribute tracked by
+macro names, and @code{builtin} bypasses macro names (@pxref{Builtin}),
+it is not possible for @code{builtin} to trace which subsidiary builtin
+it invokes.  If you are worried about tracking all invocations of a
+given builtin, you should also trace @code{builtin}, or enable global
+tracing (the @samp{t} debug level, @pxref{Debug Levels}).
+
address@hidden
+$ @kbd{m4 -d}
+define(`my_defn', defn(`defn'))undefine(`defn')
address@hidden
+define(`foo', `bar')traceon(`foo', `defn', `my_defn')
address@hidden
+foo
address@hidden: -1- foo -> `bar'
address@hidden
+indir(`foo')
address@hidden: -1- foo -> `bar'
address@hidden
+my_defn(`foo')
address@hidden: -1- my_defn(`foo') -> ``bar''
address@hidden
+indir(`my_defn', `foo')
address@hidden: -1- my_defn(`foo') -> ``bar''
address@hidden
+builtin(`defn', `foo')
address@hidden
+debugmode(`+cxt')
address@hidden
+builtin(`defn', builtin(`shift', `', `foo'))
address@hidden: -1- id 12: builtin ...
address@hidden: -2- id 13: builtin ...
address@hidden: -2- id 13: builtin(`shift', `', `foo') -> ``foo''
address@hidden: -1- id 12: builtin(`defn', `foo') -> ``bar''
address@hidden
+indir(`my_defn', indir(`shift', `', `foo'))
address@hidden: -1- id 14: indir ...
address@hidden: -2- id 15: indir ...
address@hidden: -2- id 15: shift ...
address@hidden: -2- id 15: shift(`', `foo') -> ``foo''
address@hidden: -2- id 15: indir(`shift', `', `foo') -> ``foo''
address@hidden: -1- id 14: my_defn ...
address@hidden: -1- id 14: my_defn(`foo') -> ``bar''
address@hidden: -1- id 14: indir(`my_defn', `foo') -> ``bar''
address@hidden
address@hidden example
 
 @node Debug Levels
 @section Controlling debugging output
@@ -3761,11 +3810,14 @@ flag is used, otherwise only the macros covered by 
calls of
 @code{traceon}.  Arguments are subject to length truncation specified by
 the command line option @option{--arglength} (or @option{-l}).
 
address@hidden FIXME - is it worth adding @item b, which turns on backslash
address@hidden escaping of all trace output, to guarantee single-line traces?
+
 @item c
-In trace output, show several trace lines for each macro call.  A line
-is shown when the macro is seen, but before the arguments are collected;
-a second line when the arguments have been collected and a third line
-after the call has completed.
+In trace output, show an additional trace line for each macro call, when
+the macro is seen, but before the arguments are collected.  By default,
+only one line is printed, after all arguments are collected and the
+expansion determined.  This is often used with the @samp{x} flag.
 
 @item e
 In trace output, show the expansion of each macro call, if it is not
@@ -3802,7 +3854,11 @@ In trace output, trace all macro calls made in this 
invocation of
 
 @item x
 In trace output, add a unique `macro call id' to each line of the trace
-output.  This is useful in connection with the @samp{c} flag above.
+output.  This is useful in connection with the @samp{c} flag above, to
+match where a macro is first recognized with where it is finally
+expanded, in spite of intermediate expansions that occur while
+collecting arguments.  It can also be used in isolation to determine how
+many macros have been expanded.
 
 @item V
 A shorthand for all of the above flags.
@@ -3840,17 +3896,18 @@ debugmode()
 foo
 @error{}m4trace: -1- foo -> `FOO'
 @result{}FOO
-debugmode(`V')
address@hidden
-foo(`ignored')
address@hidden:stdin:6: -1- id 6: foo ...
address@hidden:stdin:6: -1- id 6: foo(`ignored') -> ???
address@hidden:stdin:6: -1- id 6: foo(...) -> `FOO'
+debugmode(`V')debugmode(`-q')
address@hidden:stdin:5: -1- id 6: debugmode ...
address@hidden:stdin:5: -1- id 6: debugmode(`-q') -> `'
address@hidden
+foo(
+`ignored')
address@hidden:stdin:6: -1- id 7: foo ...
address@hidden:stdin:6: -1- id 7: foo(ignored) -> FOO
 @result{}FOO
 debugmode
address@hidden:stdin:7: -1- id 7: debugmode ...
address@hidden:stdin:7: -1- id 7: debugmode -> ???
address@hidden
address@hidden:stdin:8: -1- id 8: debugmode ...
address@hidden:stdin:8: -1- id 8: debugmode ->@w{ }
 @result{}
 foo
 @error{}m4trace: -1- foo
@@ -3858,7 +3915,7 @@ foo
 debugmode(`+l')
 @result{}
 foo
address@hidden:10: -1- foo
address@hidden:11: -1- foo
 @result{}FOO
 @end example
 
@@ -3867,17 +3924,17 @@ when specified on the command line.  Note that each 
argument and the
 final result are individually truncated.  Also, the special tokens for
 builtin functions are not truncated.
 
address@hidden options: -l6
address@hidden options: -l6 -tdefn -techo
 @example
-$ @kbd{m4 -d -l 6}
-define(`echo', `$@@')debugmode(`+t')
+$ @kbd{m4 -d -l 6 -t defn -t echo}
+define(`echo', `$@@')
 @result{}
 echo(`1', `long string')
 @error{}m4trace: -1- echo(`1', `long s...') -> ``1',`l...'
 @result{}1,long string
 indir(`echo', defn(`changequote'))
 @error{}m4trace: -2- defn(`change...') -> `<changequote>'
address@hidden: -1- indir(`echo', <changequote>) -> ``<changequote>''
address@hidden: -1- echo(<changequote>) -> ``<changequote>''
 @result{}
 @end example
 
@@ -7277,6 +7334,13 @@ also traced by name; and tracing by name, such as with 
@option{-tfoo} at
 the command line or @code{traceon(`foo')} in the input, is an attribute
 that is preserved even if the macro is currently undefined.
 
+Additionally, while @acronym{POSIX} requires trace output, it makes no
+demands on the formatting of that output.  Parsing trace output is not
+guaranteed to be reliable, even between different releases of
address@hidden M4; however, the intent is that any future changes in
+trace output will only occur under the direction of additional
address@hidden flags (@pxref{Debug Levels}).
+
 @item
 @acronym{POSIX} requires @code{eval} (@pxref{Eval}) to treat all
 operators with the same precedence as address@hidden  However, earlier 
versions of
diff --git a/examples/null.err b/examples/null.err
index 61518e8..d825818 100644
--- a/examples/null.err
+++ b/examples/null.err
@@ -1 +1,3 @@
 errprint: -- --
+traceon: fixme
+m4trace: -1- -(`-
diff --git a/examples/null.m4 b/examples/null.m4
index 7cf123f..10d15ae 100644
--- a/examples/null.m4
+++ b/examples/null.m4
@@ -116,9 +116,10 @@ dnl Passed to syscmd: not tested yet. NUL truncates 
string, needs to warn
 dnl Sysval takes no arguments, and never produces NUL.
 dnl Passed to traceoff:
 traceoff(`--', `')dnl
-dnl Macro name and arguments of traceon: not tested yet. Trace output needs 
quoting
-dnl Defined text of traceon: not tested yet. Needs tracing indirect macros
-`traceon:' indir(`--', `--')
+dnl Macro name and arguments of traceon: not perfect yet, needs quoting
+`traceon:' errprint(`traceon: fixme
+')traceon(`--')indir(`--', `--')
+dnl Defined text of traceon: not tested yet, needs quoting
 dnl First argument of translit: not tested yet
 dnl Single character in other arguments of translit: not tested yet
 dnl Character ranges of translit: not tested yet
diff --git a/src/builtin.c b/src/builtin.c
index 0549745..830d4a2 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -943,7 +943,7 @@ m4_builtin (struct obstack *obs, int argc, macro_arguments 
*argv)
   else
     {
       macro_arguments *new_argv = make_argv_ref (argv, name, ARG_LEN (1),
-                                                true, !bp->groks_macro_args);
+                                                !bp->groks_macro_args, false);
       bp->func (obs, argc - 1, new_argv);
     }
 }
@@ -977,8 +977,10 @@ m4_indir (struct obstack *obs, int argc, macro_arguments 
*argv)
   else
     {
       macro_arguments *new_argv = make_argv_ref (argv, name, ARG_LEN (1),
-                                                true, !SYMBOL_MACRO_ARGS (s));
-      call_macro (s, argc - 1, new_argv, obs);
+                                                !SYMBOL_MACRO_ARGS (s),
+                                                SYMBOL_TRACED (s));
+      trace_prepre (arg_info (new_argv));
+      call_macro (s, new_argv, obs);
     }
 }
 
diff --git a/src/debug.c b/src/debug.c
index fde6c49..0298fc8 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -33,7 +33,7 @@ static struct obstack trace;
 static void debug_set_file (const char *, FILE *);
 
 /*----------------------------------.
-| Initialise the debugging module.  |
+| Initialize the debugging module.  |
 `----------------------------------*/
 
 void
@@ -113,11 +113,12 @@ debug_decode (const char *opts)
   return level;
 }
 
-/*------------------------------------------------------------------------.
-| Change the debug output stream to FP.  If the underlying file is the   |
-| same as stdout, use stdout instead so that debug messages appear in the |
-| correct relative position.                                             |
-`------------------------------------------------------------------------*/
+/*-----------------------------------------------------------------.
+| Change the debug output stream to FP.  If the underlying file is |
+| the same as stdout, use stdout instead so that debug messages    |
+| appear in the correct relative position.  Report any failure on  |
+| behalf of CALLER.                                                |
+`-----------------------------------------------------------------*/
 
 static void
 debug_set_file (const char *caller, FILE *fp)
@@ -210,7 +211,7 @@ debug_set_output (const char *caller, const char *name)
 }
 
 /*-----------------------------------------------------------------------.
-| Print the header of a one-line debug message, starting by "m4 debug".        
 |
+| Print the header of a one-line debug message, starting by "m4debug:".        
 |
 `-----------------------------------------------------------------------*/
 
 void
@@ -235,10 +236,8 @@ debug_message_prefix (void)
 
 /*-------------------------------------------------------------------.
 | Tracing output to the obstack is formatted here, by a simplified   |
-| printf-like function trace_format ().  Understands only %B (1 arg: |
-| input block), %S (1 arg: length-limited text), %s (1 arg: text),   |
-| %d (1 arg: integer), %l (0 args: optional left quote) and %r (0    |
-| args: optional right quote).                                       |
+| printf-like function trace_format ().  Understands only %s (1 arg: |
+| text), %d (1 arg: integer).                                        |
 `-------------------------------------------------------------------*/
 
 static void
@@ -263,27 +262,10 @@ trace_format (const char *fmt, ...)
       maxlen = SIZE_MAX;
       switch (*fmt++)
        {
-       case 'B':
-         s = "";
-         input_print (&trace, va_arg (args, input_block *));
-         break;
-
-       case 'S':
-         maxlen = max_debug_argument_length;
-         /* fall through */
-
        case 's':
          s = va_arg (args, const char *);
          break;
 
-       case 'l':
-         s = (debug_level & DEBUG_TRACE_QUOTE) ? curr_quote.str1 : "";
-         break;
-
-       case 'r':
-         s = (debug_level & DEBUG_TRACE_QUOTE) ? curr_quote.str2 : "";
-         break;
-
        case 'd':
          d = va_arg (args, int);
          s = ntoa (d, 10);
@@ -302,102 +284,106 @@ trace_format (const char *fmt, ...)
 }
 
 /*------------------------------------------------------------------.
-| Format the standard header attached to all tracing output lines.  |
-| ID is the current macro id.                                       |
+| Format the standard header attached to all tracing output lines,  |
+| using the context in INFO as appropriate.  Return the offset into |
+| the trace obstack where this particular trace begins.             |
 `------------------------------------------------------------------*/
 
-static void
-trace_header (int id)
+static unsigned int
+trace_header (const call_info *info)
 {
+  int trace_level = info->debug_level;
+  unsigned int result = obstack_object_size (&trace);
   trace_format ("m4trace:");
-  if (current_line)
-    {
-      if (debug_level & DEBUG_TRACE_FILE)
-       trace_format ("%s:", current_file);
-      if (debug_level & DEBUG_TRACE_LINE)
-       trace_format ("%d:", current_line);
-    }
+  if (trace_level & DEBUG_TRACE_FILE)
+    trace_format ("%s:", info->file);
+  if (trace_level & DEBUG_TRACE_LINE)
+    trace_format ("%d:", info->line);
   trace_format (" -%d- ", expansion_level);
-  if (debug_level & DEBUG_TRACE_CALLID)
-    trace_format ("id %d: ", id);
+  if (trace_level & DEBUG_TRACE_CALLID)
+    trace_format ("id %d: ", info->call_id);
+  return result;
 }
 
-/*----------------------------------------------------.
-| Print current tracing line, and clear the obstack.  |
-`----------------------------------------------------*/
+/*-----------------------------------------------------------------.
+| Print current tracing line starting at offset START, as returned |
+| from an earlier trace_header(), then clear the obstack.          |
+`-----------------------------------------------------------------*/
 
 static void
-trace_flush (void)
+trace_flush (unsigned int start)
 {
   char *line;
 
   obstack_1grow (&trace, '\0');
-  line = (char *) obstack_finish (&trace);
-  DEBUG_PRINT1 ("%s\n", line);
-  obstack_free (&trace, line);
+  line = (char *) obstack_base (&trace);
+  DEBUG_PRINT1 ("%s\n", &line[start]);
+  start -= obstack_object_size (&trace);
+  obstack_blank (&trace, start);
 }
 
-/*----------------------------------------------------------------.
-| Do pre-argument-collection tracing for macro NAME, with a given |
-| ID.  Used from expand_macro ().                                 |
-`----------------------------------------------------------------*/
+/*-------------------------------------------------------------------.
+| Do pre-argument-collection tracing for the macro call described in |
+| INFO.  Used from expand_macro ().                                  |
+`-------------------------------------------------------------------*/
 
 void
-trace_prepre (const char *name, int id)
+trace_prepre (const call_info *info)
 {
-  trace_header (id);
-  trace_format ("%s ...", name);
-  trace_flush ();
+  if (info->trace && (info->debug_level & DEBUG_TRACE_CALL))
+    {
+      unsigned int start = trace_header (info);
+      trace_format ("%s ...", info->name);
+      trace_flush (start);
+    }
 }
 
-/*-----------------------------------------------------------------.
-| Format the parts of a trace line that are known before the macro |
-| is actually expanded.  Called for the macro NAME with ID, and    |
-| arguments ARGV.  Used from expand_macro ().                      |
-`-----------------------------------------------------------------*/
+/*------------------------------------------------------------------.
+| Format the parts of a trace line that are known via ARGV before   |
+| the macro is actually expanded.  Used from call_macro ().  Return |
+| the start of the current trace, in case other traces are printed  |
+| before this trace completes trace_post.                           |
+`------------------------------------------------------------------*/
 
-void
-trace_pre (const char *name, int id, macro_arguments *argv)
+unsigned int
+trace_pre (macro_arguments *argv)
 {
-  trace_header (id);
-  trace_format ("%s", name);
+  const call_info *info = arg_info (argv);
+  int trace_level = info->debug_level;
+  unsigned int start = trace_header (info);
 
-  if (arg_argc (argv) > 1 && (debug_level & DEBUG_TRACE_ARGS))
+  assert (info->trace);
+  trace_format ("%s", info->name);
+  if (1 < arg_argc (argv) && (trace_level & DEBUG_TRACE_ARGS))
     {
       size_t len = max_debug_argument_length;
-      trace_format ("(");
+      obstack_1grow (&trace, '(');
       arg_print (&trace, argv, 1,
-                (debug_level & DEBUG_TRACE_QUOTE) ? &curr_quote : NULL,
+                (trace_level & DEBUG_TRACE_QUOTE) ? &curr_quote : NULL,
                 false, NULL, ", ", &len, true);
-      trace_format (")");
-    }
-
-  if (debug_level & DEBUG_TRACE_CALL)
-    {
-      trace_format (" -> ???");
-      trace_flush ();
+      obstack_1grow (&trace, ')');
     }
+  return start;
 }
 
-/*-------------------------------------------------------------------.
-| Format the final part of a trace line and print it all.  Print     |
-| details for macro NAME with ID, given arguemnts ARGV and expansion |
-| EXPANDED.  Used from expand_macro ().                              |
-`-------------------------------------------------------------------*/
+/*------------------------------------------------------------------.
+| If requested by the trace state in INFO, format the final part of |
+| a trace line.  Then print all collected information from START,   |
+| returned from a prior trace_pre().  Used from call_macro ().      |
+`------------------------------------------------------------------*/
 
 void
-trace_post (const char *name, int id, macro_arguments *argv,
-           const input_block *expanded)
+trace_post (unsigned int start, const call_info *info)
 {
-  int argc = arg_argc (argv);
-
-  if (debug_level & DEBUG_TRACE_CALL)
+  assert (info->trace);
+  if (info->debug_level & DEBUG_TRACE_EXPANSION)
     {
-      trace_header (id);
-      trace_format ("%s%s", name, (argc > 1) ? "(...)" : "");
+      obstack_grow (&trace, " -> ", 4);
+      if (info->debug_level & DEBUG_TRACE_QUOTE)
+       obstack_grow (&trace, curr_quote.str1, curr_quote.len1);
+      input_print (&trace);
+      if (info->debug_level & DEBUG_TRACE_QUOTE)
+       obstack_grow (&trace, curr_quote.str2, curr_quote.len2);
     }
-
-  if (expanded && (debug_level & DEBUG_TRACE_EXPANSION))
-    trace_format (" -> %l%B%r", expanded);
-  trace_flush ();
+  trace_flush (start);
 }
diff --git a/src/input.c b/src/input.c
index df5a791..c1f609b 100644
--- a/src/input.c
+++ b/src/input.c
@@ -80,6 +80,8 @@ enum input_type
 
 typedef enum input_type input_type;
 
+typedef struct input_block input_block;
+
 /* A block of input to be scanned.  */
 struct input_block
 {
@@ -331,6 +333,7 @@ push_string_init (void)
   next->type = INPUT_STRING;
   next->file = current_file;
   next->line = current_line;
+  next->u.u_s.len = 0;
 
   return current_input;
 }
@@ -500,26 +503,20 @@ push_token (token_data *token, int level, bool inuse)
 }
 
 /*-------------------------------------------------------------------.
-| Last half of push_string ().  If next is now NULL, a call to       |
-| push_file () or push_macro () has invalidated the previous call to |
-| push_string_init (), so we just give up.  If the new object is     |
-| void, we do not push it.  The function push_string_finish ()       |
-| returns an opaque pointer to the finished object, which can then   |
-| be printed with input_print when tracing is enabled.  This pointer |
-| is only for temporary use, since reading the next token will       |
-| invalidate the object.                                             |
+| Last half of push_string ().  All remaining unfinished text on the |
+| obstack returned from push_string_init is collected into the input |
+| stack.                                                             |
 `-------------------------------------------------------------------*/
 
-const input_block *
+void
 push_string_finish (void)
 {
-  input_block *ret = NULL;
   size_t len = obstack_object_size (current_input);
 
   if (next == NULL)
     {
       assert (!len);
-      return NULL;
+      return;
     }
 
   if (len || next->type == INPUT_CHAIN)
@@ -534,12 +531,10 @@ push_string_finish (void)
       next->prev = isp;
       isp = next;
       input_change = true;
-      ret = isp;
     }
   else
     obstack_free (current_input, next);
   next = NULL;
-  return ret;
 }
 
 /*--------------------------------------------------------------.
@@ -735,24 +730,30 @@ pop_wrapup (void)
 | tracing.                                                      |
 `--------------------------------------------------------------*/
 void
-input_print (struct obstack *obs, const input_block *input)
+input_print (struct obstack *obs)
 {
+  size_t len = obstack_object_size (current_input);
   size_t maxlen = max_debug_argument_length;
   token_chain *chain;
 
-  assert (input);
-  switch (input->type)
+  if (!next)
+    {
+      assert (!len);
+      return;
+    }
+  switch (next->type)
     {
     case INPUT_STRING:
-      shipout_string_trunc (obs, input->u.u_s.str, input->u.u_s.len, &maxlen);
+      assert (!next->u.u_s.len);
       break;
     case INPUT_FILE:
+      assert (!len);
       obstack_grow (obs, "<file: ", strlen ("<file: "));
-      obstack_grow (obs, input->file, strlen (input->file));
+      obstack_grow (obs, next->file, strlen (next->file));
       obstack_1grow (obs, '>');
       break;
     case INPUT_CHAIN:
-      chain = input->u.u_c.chain;
+      chain = next->u.u_c.chain;
       while (chain)
        {
          switch (chain->type)
@@ -785,6 +786,9 @@ input_print (struct obstack *obs, const input_block *input)
       assert (!"input_print");
       abort ();
     }
+  if (len)
+    shipout_string_trunc (obs, (char *) obstack_base (current_input), len,
+                         &maxlen);
 }
 
 
diff --git a/src/m4.c b/src/m4.c
index 2ad82c2..84cb8e0 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -238,24 +238,25 @@ static void
 usage (int status)
 {
   if (status != EXIT_SUCCESS)
-    xfprintf (stderr, "Try `%s --help' for more information.\n", program_name);
+    xfprintf (stderr, _("Try `%s --help' for more information.\n"),
+              program_name);
   else
     {
-      xprintf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
-      fputs ("\
+      xprintf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
+      fputs (_("\
 Process macros in FILEs.  If no FILE or if FILE is `-', standard input\n\
 is read.\n\
-", stdout);
-      fputs ("\
-\n\
+"), stdout);
+      puts ("");
+      fputs (_("\
 Mandatory or optional arguments to long options are mandatory or optional\n\
 for short options too.\n\
 \n\
 Operation modes:\n\
       --help                   display this help and exit\n\
       --version                output version information and exit\n\
-", stdout);
-      xprintf ("\
+"), stdout);
+      xprintf (_("\
   -E, --fatal-warnings         once: warnings become errors, twice: stop\n\
                                execution at first error\n\
   -i, --interactive            unbuffer output, ignore interrupts\n\
@@ -264,67 +265,75 @@ Operation modes:\n\
       --warn-macro-sequence[=REGEXP]\n\
                                warn if macro definition matches REGEXP,\n\
                                default %s\n\
-", DEFAULT_MACRO_SEQUENCE);
+"), DEFAULT_MACRO_SEQUENCE);
 #ifdef ENABLE_CHANGEWORD
-      fputs ("\
+      fputs (_("\
   -W, --word-regexp=REGEXP     use REGEXP for macro name syntax\n\
-", stdout);
+"), stdout);
 #endif
-      fputs ("\
-\n\
+      puts ("");
+      fputs (_("\
 Preprocessor features:\n\
   -D, --define=NAME[=VALUE]    define NAME as having VALUE, or empty\n\
   -I, --include=DIRECTORY      append DIRECTORY to include path\n\
   -s, --synclines              generate `#line NUM \"FILE\"' lines\n\
   -U, --undefine=NAME          undefine NAME\n\
-", stdout);
-      fputs ("\
-\n\
+"), stdout);
+      puts ("");
+      fputs (_("\
 Limits control:\n\
   -G, --traditional            suppress all GNU extensions\n\
   -H, --hashsize=PRIME         set symbol lookup hash table size [509]\n\
   -L, --nesting-limit=NUMBER   change artificial nesting limit [1024]\n\
-", stdout);
-      fputs ("\
-\n\
+"), stdout);
+      puts ("");
+      fputs (_("\
 Frozen state files:\n\
   -F, --freeze-state=FILE      produce a frozen state on FILE at end\n\
   -R, --reload-state=FILE      reload a frozen state from FILE at start\n\
-", stdout);
-      fputs ("\
-\n\
+"), stdout);
+      puts ("");
+      fputs (_("\
 Debugging:\n\
   -d, --debug[=FLAGS]          set debug level (no FLAGS implies `aeq')\n\
       --debugfile=FILE         redirect debug and trace output\n\
   -l, --arglength=NUM          restrict macro tracing size\n\
   -t, --trace=NAME             trace NAME when it is defined\n\
-", stdout);
-      fputs ("\
-\n\
+"), stdout);
+      puts ("");
+      fputs (_("\
 FLAGS is any of:\n\
-  a   show actual arguments\n\
-  c   show before collect, after collect and after call\n\
-  e   show expansion\n\
-  f   say current input file name\n\
-  i   show changes in input files\n\
-  l   say current input line number\n\
-  p   show results of path searches\n\
-  q   quote values as necessary, with a or e flag\n\
-  t   trace for all macro calls, not only traceon'ed\n\
-  x   add a unique macro call id, useful with c flag\n\
+  a   show actual arguments in trace\n\
+  c   show collection line in trace\n\
+  e   show expansion in trace\n\
+  f   include current input file name in trace and debug\n\
+  i   show changes in input files in debug\n\
+  l   include current input line number in trace and debug\n\
+"), stdout);
+      fputs (_("\
+  p   show results of path searches in debug\n\
+  q   quote values in dumpdef and trace, useful with a or e\n\
+  t   trace all macro calls, regardless of per-macro traceon state\n\
+  x   include unique macro call id in trace, useful with c\n\
   V   shorthand for all of the above flags\n\
-", stdout);
-      fputs ("\
-\n\
+"), stdout);
+      puts ("");
+      fputs (_("\
 If defined, the environment variable `M4PATH' is a colon-separated list\n\
 of directories included after any specified by `-I'.\n\
-", stdout);
-      fputs ("\
-\n\
+"), stdout);
+      puts ("");
+      fputs (_("\
 Exit status is 0 for success, 1 for failure, 63 for frozen file version\n\
 mismatch, or whatever value was passed to the m4exit macro.\n\
-", stdout);
-      xprintf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
+"), stdout);
+      puts ("");
+      /* TRANSLATORS: the placeholder indicates the bug-reporting
+        address for this application.  Please add _another line_
+        saying "Report translation bugs to <...>\n" with the address
+        for translation bugs (typically your translation team's web
+        or email address).  */
+      xprintf (_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     }
   exit (status);
 }
diff --git a/src/m4.h b/src/m4.h
index b3cb7e1..b2e55e5 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -96,7 +96,6 @@ typedef struct string_pair string_pair;
 #define obstack_chunk_free     free
 
 /* These must come first.  */
-typedef struct input_block input_block;
 typedef struct token_data token_data;
 typedef struct macro_arguments macro_arguments;
 typedef void builtin_func (struct obstack *, int, macro_arguments *);
@@ -139,6 +138,21 @@ extern const char *user_word_regexp;       /* -W */
 #endif
 
 /* Error handling.  */
+
+/* A structure containing context that was valid when a macro call
+   started collecting arguments; used for tracing and error messages
+   even when the global context changes in the meantime.  */
+struct call_info
+{
+  const char *file;    /* The file containing the macro invocation.  */
+  int line;            /* The line the macro was called on.  */
+  int call_id;         /* The unique sequence call id of the macro.  */
+  int trace : 1;       /* True to trace this macro.  */
+  int debug_level : 31;        /* The debug level for tracing the macro call.  
*/
+  const char *name;    /* The macro name.  */
+};
+typedef struct call_info call_info;
+
 extern int retcode;
 extern const char *program_name;
 
@@ -245,10 +259,9 @@ void debug_flush_files (void);
 bool debug_set_output (const char *, const char *);
 void debug_message_prefix (void);
 
-void trace_prepre (const char *, int);
-void trace_pre (const char *, int, macro_arguments *);
-void trace_post (const char *, int, macro_arguments *,
-                const input_block *);
+void trace_prepre (const call_info *);
+unsigned int trace_pre (macro_arguments *);
+void trace_post (unsigned int, const call_info *);
 
 
 /* File: input.c  --- lexical definitions.  */
@@ -398,11 +411,11 @@ void append_macro (struct obstack *, builtin_func *, 
token_chain **,
 void push_macro (struct obstack *, builtin_func *);
 struct obstack *push_string_init (void);
 bool push_token (token_data *, int, bool);
-const input_block *push_string_finish (void);
+void push_string_finish (void);
 struct obstack *push_wrapup_init (token_chain ***);
 void push_wrapup_finish (void);
 bool pop_wrapup (void);
-void input_print (struct obstack *, const input_block *);
+void input_print (struct obstack *);
 
 /* current input file, and line */
 extern const char *current_file;
@@ -498,11 +511,12 @@ void hack_all_symbols (hack_symbol *, void *);
 extern int expansion_level;
 
 void expand_input (void);
-void call_macro (symbol *, int, macro_arguments *, struct obstack *);
+void call_macro (symbol *, macro_arguments *, struct obstack *);
 size_t adjust_refcount (int, bool);
 
 bool arg_adjust_refcount (macro_arguments *, bool);
 unsigned int arg_argc (macro_arguments *);
+const call_info *arg_info (macro_arguments *);
 token_data_type arg_type (macro_arguments *, unsigned int);
 const char *arg_text (macro_arguments *, unsigned int, bool);
 bool arg_equal (macro_arguments *, unsigned int, unsigned int);
diff --git a/src/macro.c b/src/macro.c
index 8290818..2a5b52a 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -59,6 +59,9 @@ struct macro_arguments
      object, or 0 if quote_age changed during parsing or if any of the
      arguments might contain content that can affect rescan.  */
   unsigned int quote_age;
+  /* The context of this macro call during expansion, and NULL in a
+     back-reference.  */
+  call_info *info;
   int level; /* Which obstack owns this argv.  */
   unsigned int arraylen; /* True length of allocated elements in array.  */
   /* Used as a variable-length array, storing information about each
@@ -176,7 +179,7 @@ static size_t stacks_count;
 int expansion_level = 0;
 
 /* The number of the current call of expand_macro ().  */
-static int macro_call_id = 0;
+static int macro_call_id;
 
 /* The empty string token.  */
 static token_data empty_token;
@@ -476,7 +479,7 @@ expand_argument (struct obstack *obs, token_data *argp, 
const char *caller)
 `-------------------------------------------------------------------------*/
 
 static macro_arguments *
-collect_arguments (symbol *sym, struct obstack *arguments,
+collect_arguments (symbol *sym, call_info *info, struct obstack *arguments,
                   struct obstack *argv_stack)
 {
   token_data td;
@@ -495,6 +498,7 @@ collect_arguments (symbol *sym, struct obstack *arguments,
   args.argv0 = SYMBOL_NAME (sym);
   args.argv0_len = strlen (args.argv0);
   args.quote_age = quote_age ();
+  args.info = info;
   args.level = expansion_level - 1;
   args.arraylen = 0;
   obstack_grow (argv_stack, &args, offsetof (macro_arguments, array));
@@ -564,31 +568,37 @@ collect_arguments (symbol *sym, struct obstack *arguments,
 }
 
 
-/*-----------------------------------------------------------------.
-| Call the macro SYM, which is either a builtin function or a user |
-| macro (via the expansion function expand_user_macro () in        |
-| builtin.c).  There are ARGC arguments to the call, stored in the |
-| ARGV table.  The expansion is left on the obstack EXPANSION.     |
-`-----------------------------------------------------------------*/
+/*-------------------------------------------------------------------.
+| Call the macro SYM, which is either a builtin function or a user   |
+| macro (via the expansion function expand_user_macro () in          |
+| builtin.c).  The arguments are provided by ARGV.  The expansion is |
+| left on the obstack EXPANSION.  Macro tracing is also handled      |
+| here.                                                              |
+`-------------------------------------------------------------------*/
 
 void
-call_macro (symbol *sym, int argc, macro_arguments *argv,
-           struct obstack *expansion)
+call_macro (symbol *sym, macro_arguments *argv, struct obstack *expansion)
 {
+  unsigned int trace_start = 0;
+
+  if (argv->info->trace)
+    trace_start = trace_pre (argv);
   switch (SYMBOL_TYPE (sym))
     {
     case TOKEN_FUNC:
-      SYMBOL_FUNC (sym) (expansion, argc, argv);
+      SYMBOL_FUNC (sym) (expansion, argv->argc, argv);
       break;
 
     case TOKEN_TEXT:
-      expand_user_macro (expansion, sym, argc, argv);
+      expand_user_macro (expansion, sym, argv->argc, argv);
       break;
 
     default:
       assert (!"call_macro");
       abort ();
     }
+  if (argv->info->trace)
+    trace_post (trace_start, argv->info);
 }
 
 /*-------------------------------------------------------------------------.
@@ -609,19 +619,10 @@ expand_macro (symbol *sym)
   void *argv_base;             /* Base of stacks[i].argv on entry.  */
   macro_arguments *argv;       /* Arguments to the called macro.  */
   struct obstack *expansion;   /* Collects the macro's expansion.  */
-  const input_block *expanded; /* The resulting expansion, for tracing.  */
-  bool traced;                 /* True if this macro is traced.  */
-  int my_call_id;              /* Sequence id for this macro.  */
   int level = expansion_level; /* Expansion level of this macro.  */
+  call_info my_call_info;      /* Context of this macro.  */
 
-  /* Report errors at the location where the open parenthesis (if any)
-     was found, but after expansion, restore global state back to the
-     location of the close parenthesis.  This is safe since we
-     guarantee that macro expansion does not alter the state of
-     current_file/current_line (dnl, include, and sinclude are special
-     cased in the input engine to ensure this fact).  */
-  const char *loc_open_file = current_file;
-  int loc_open_line = current_line;
+  /* TODO - make m4_warn use optional call_info, so we don't need these.  */
   const char *loc_close_file;
   int loc_close_line;
 
@@ -661,35 +662,37 @@ expand_macro (symbol *sym)
              _("recursion limit of %d exceeded, use -L<N> to change it"),
              nesting_limit);
 
-  macro_call_id++;
-  my_call_id = macro_call_id;
-
-  traced = (debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym);
-  if (traced && (debug_level & DEBUG_TRACE_CALL))
-    trace_prepre (SYMBOL_NAME (sym), my_call_id);
-
-  argv = collect_arguments (sym, stacks[level].args, stacks[level].argv);
+  /* Collect context in effect at start of macro, even if global state
+     changes in the meantime.  */
+  my_call_info.file = current_file;
+  my_call_info.line = current_line;
+  my_call_info.call_id = ++macro_call_id;
+  my_call_info.trace = (debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym);
+  my_call_info.debug_level = debug_level;
+  my_call_info.name = SYMBOL_NAME (sym);
+  trace_prepre (&my_call_info);
+
+  /* Collect the arguments.  */
+  argv = collect_arguments (sym, &my_call_info, stacks[level].args,
+                           stacks[level].argv);
   args_scratch = obstack_finish (stacks[level].args);
 
-  /* The actual macro call.  */
+  /*  Temporarily reset the location so that error messages are
+      tracked to the macro name.  */
   loc_close_file = current_file;
   loc_close_line = current_line;
-  current_file = loc_open_file;
-  current_line = loc_open_line;
-
-  if (traced)
-    trace_pre (SYMBOL_NAME (sym), my_call_id, argv);
+  current_file = my_call_info.file;
+  current_line = my_call_info.line;
 
+  /* The actual macro call.  */
   expansion = push_string_init ();
-  call_macro (sym, argv->argc, argv, expansion);
-  expanded = push_string_finish ();
-
-  if (traced)
-    trace_post (SYMBOL_NAME (sym), my_call_id, argv, expanded);
+  call_macro (sym, argv, expansion);
+  push_string_finish ();
 
   /* Cleanup.  */
   current_file = loc_close_file;
   current_line = loc_close_line;
+  argv->info = NULL;
 
   --expansion_level;
   --SYMBOL_PENDING_EXPANSIONS (sym);
@@ -710,8 +713,9 @@ expand_macro (symbol *sym)
          obstack_free (stacks[level].args, args_scratch);
          if (debug_macro_level & PRINT_ARGCOUNT_CHANGES)
            xfprintf (debug, "m4debug: -%d- `%s' in use, level=%d, "
-                     "refcount=%zu, argcount=%zu\n", my_call_id, argv->argv0,
-                     level, stacks[level].refcount, stacks[level].argcount);
+                     "refcount=%zu, argcount=%zu\n", my_call_info.call_id,
+                     argv->argv0, level, stacks[level].refcount,
+                     stacks[level].argcount);
        }
       else
        {
@@ -870,6 +874,16 @@ arg_argc (macro_arguments *argv)
   return argv->argc;
 }
 
+/* Given ARGV, return the call context in effect when argument
+   collection began.  Only safe to call while the macro is being
+   expanded.  */
+const call_info *
+arg_info (macro_arguments *argv)
+{
+  assert (argv->info);
+  return argv->info;
+}
+
 /* Given ARGV, return the type of argument ARG.  Arg 0 is always text,
    and indices beyond argc are likewise treated as text.  */
 token_data_type
@@ -1394,22 +1408,23 @@ make_argv_ref_token (token_data *token, struct obstack 
*obs, int level,
 /* Create a new argument object using the same obstack as ARGV; thus,
    the new object will automatically be freed when the original is
    freed.  Explicitly set the macro name (argv[0]) from ARGV0 with
-   length ARGV0_LEN.  If SKIP, set argv[1] of the new object to
-   argv[2] of the old, otherwise the objects share all arguments.  If
+   length ARGV0_LEN, and discard argv[1] of the wrapped ARGV.  If
    FLATTEN, any non-text in ARGV is flattened to an empty string when
-   referenced through the new object.  */
+   referenced through the new object.  If TRACE, then trace the macro,
+   regardless of global trace state.  */
 macro_arguments *
 make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len,
-              bool skip, bool flatten)
+              bool flatten, bool trace)
 {
   macro_arguments *new_argv;
   token_data *token;
   token_data *new_token;
-  unsigned int i = skip ? 2 : 1;
   struct obstack *obs = arg_scratch ();
+  call_info *info;
 
+  info = (call_info *) obstack_copy (obs, argv->info, sizeof *info);
   new_token = (token_data *) obstack_alloc (obs, sizeof *token);
-  token = make_argv_ref_token (new_token, obs, expansion_level - 1, argv, i,
+  token = make_argv_ref_token (new_token, obs, expansion_level - 1, argv, 2,
                               flatten, NULL);
   if (!token)
     {
@@ -1433,11 +1448,14 @@ make_argv_ref (macro_arguments *argv, const char 
*argv0, size_t argv0_len,
       new_argv->flatten = flatten;
       new_argv->has_func = argv->has_func;
     }
-  new_argv->argc = argv->argc - (i - 1);
+  new_argv->argc = argv->argc - 1;
   new_argv->inuse = false;
   new_argv->argv0 = argv0;
   new_argv->argv0_len = argv0_len;
   new_argv->quote_age = argv->quote_age;
+  new_argv->info = info;
+  info->trace = (argv->info->debug_level & DEBUG_TRACE_ALL) || trace;
+  info->name = argv0;
   new_argv->level = argv->level;
   return new_argv;
 }
-- 
1.5.5.1


reply via email to

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