summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2007-12-13 10:37:51 -0700
committerEric Blake <ebb9@byu.net>2008-05-08 12:45:52 -0600
commitec804cba9ceeef7ca863d163eeae3fca669ccd16 (patch)
tree5051bb2d863465cc862beca5049f85227f72e8d7
parent8a47a2029b7eb60ac61abb1b6423d4a67b371281 (diff)
downloadm4-ec804cba9ceeef7ca863d163eeae3fca669ccd16.tar.gz
Stage23: allow tracing indirect macro names
-rw-r--r--NEWS13
-rw-r--r--doc/m4.texinfo116
-rw-r--r--examples/null.errbin18 -> 51 bytes
-rw-r--r--examples/null.m4bin5747 -> 5876 bytes
-rw-r--r--examples/null.outbin402 -> 404 bytes
-rw-r--r--src/builtin.c14
-rw-r--r--src/debug.c168
-rw-r--r--src/input.c42
-rw-r--r--src/m4.c95
-rw-r--r--src/m4.h30
-rw-r--r--src/macro.c120
11 files changed, 360 insertions, 238 deletions
diff --git a/NEWS b/NEWS
index 41d70737..0e5ad969 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,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 8d83fb0f..aa3383bc 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -2299,7 +2299,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
-@error{}m4trace: -1- define(`mydivnum', `<divnum><divnum>')
+@error{}m4trace: -1- define(`mydivnum', `<divnum><divnum>') -> `'
@result{}
traceoff(`defn', `define')dumpdef(`mydivnum')
@error{}mydivnum:@tabchar{}`'
@@ -3703,6 +3703,16 @@ does not transfer tracing status.
@example
$ @kbd{m4 -d}
+traceon(`traceon')
+@result{}
+traceon(`traceoff')
+@error{}m4trace: -1- traceon(`traceoff') -> `'
+@result{}
+traceoff(`traceoff')
+@error{}m4trace: -1- traceoff(`traceoff') -> `'
+@result{}
+traceoff(`traceon')
+@result{}
traceon(`eval', `m4_divnum')
@result{}
define(`m4_eval', defn(`eval'))
@@ -3718,7 +3728,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
+@acronym{POSIX}, 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}).
+
+@example
+$ @kbd{m4 -d}
+define(`my_defn', defn(`defn'))undefine(`defn')
+@result{}
+define(`foo', `bar')traceon(`foo', `defn', `my_defn')
+@result{}
+foo
+@error{}m4trace: -1- foo -> `bar'
+@result{}bar
+indir(`foo')
+@error{}m4trace: -1- foo -> `bar'
+@result{}bar
+my_defn(`foo')
+@error{}m4trace: -1- my_defn(`foo') -> ``bar''
+@result{}bar
+indir(`my_defn', `foo')
+@error{}m4trace: -1- my_defn(`foo') -> ``bar''
+@result{}bar
+builtin(`defn', `foo')
+@result{}bar
+debugmode(`+cxt')
+@result{}
+builtin(`defn', builtin(`shift', `', `foo'))
+@error{}m4trace: -1- id 12: builtin ...
+@error{}m4trace: -2- id 13: builtin ...
+@error{}m4trace: -2- id 13: builtin(`shift', `', `foo') -> ``foo''
+@error{}m4trace: -1- id 12: builtin(`defn', `foo') -> ``bar''
+@result{}bar
+indir(`my_defn', indir(`shift', `', `foo'))
+@error{}m4trace: -1- id 14: indir ...
+@error{}m4trace: -2- id 15: indir ...
+@error{}m4trace: -2- id 15: shift ...
+@error{}m4trace: -2- id 15: shift(`', `foo') -> ``foo''
+@error{}m4trace: -2- id 15: indir(`shift', `', `foo') -> ``foo''
+@error{}m4trace: -1- id 14: my_defn ...
+@error{}m4trace: -1- id 14: my_defn(`foo') -> ``bar''
+@error{}m4trace: -1- id 14: indir(`my_defn', `foo') -> ``bar''
+@result{}bar
+@end example
@node Debug Levels
@section Controlling debugging output
@@ -3746,11 +3805,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}).
+@comment FIXME - is it worth adding @item b, which turns on backslash
+@comment 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
@@ -3787,7 +3849,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.
@@ -3825,17 +3891,18 @@ debugmode()
foo
@error{}m4trace: -1- foo -> `FOO'
@result{}FOO
-debugmode(`V')
-@result{}
-foo(`ignored')
-@error{}m4trace:stdin:6: -1- id 6: foo ...
-@error{}m4trace:stdin:6: -1- id 6: foo(`ignored') -> ???
-@error{}m4trace:stdin:6: -1- id 6: foo(...) -> `FOO'
+debugmode(`V')debugmode(`-q')
+@error{}m4trace:stdin:5: -1- id 6: debugmode ...
+@error{}m4trace:stdin:5: -1- id 6: debugmode(`-q') -> `'
+@result{}
+foo(
+`ignored')
+@error{}m4trace:stdin:6: -1- id 7: foo ...
+@error{}m4trace:stdin:6: -1- id 7: foo(ignored) -> FOO
@result{}FOO
debugmode
-@error{}m4trace:stdin:7: -1- id 7: debugmode ...
-@error{}m4trace:stdin:7: -1- id 7: debugmode -> ???
-@error{}
+@error{}m4trace:stdin:8: -1- id 8: debugmode ...
+@error{}m4trace:stdin:8: -1- id 8: debugmode ->@w{ }
@result{}
foo
@error{}m4trace: -1- foo
@@ -3843,7 +3910,7 @@ foo
debugmode(`+l')
@result{}
foo
-@error{}m4trace:10: -1- foo
+@error{}m4trace:11: -1- foo
@result{}FOO
@end example
@@ -3852,17 +3919,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.
-@comment options: -l6
+@comment 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>'
-@error{}m4trace: -1- indir(`echo', <changequote>) -> ``<changequote>''
+@error{}m4trace: -1- echo(<changequote>) -> ``<changequote>''
@result{}
@end example
@@ -7261,6 +7328,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
+@acronym{GNU} M4; however, the intent is that any future changes in
+trace output will only occur under the direction of additional
+@code{debugmode} flags (@pxref{Debug Levels}).
+
@item
@acronym{POSIX} requires @code{eval} (@pxref{Eval}) to treat all
operators with the same precedence as C. However, earlier versions of
diff --git a/examples/null.err b/examples/null.err
index 61518e88..d8258182 100644
--- a/examples/null.err
+++ b/examples/null.err
Binary files differ
diff --git a/examples/null.m4 b/examples/null.m4
index 79f4715f..10d15aed 100644
--- a/examples/null.m4
+++ b/examples/null.m4
Binary files differ
diff --git a/examples/null.out b/examples/null.out
index aca4b785..cd3764f7 100644
--- a/examples/null.out
+++ b/examples/null.out
Binary files differ
diff --git a/src/builtin.c b/src/builtin.c
index 142f0dc6..fb787cd5 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -467,7 +467,7 @@ define_user_macro (const char *name, size_t name_len, const char *text,
offset = regs->end[0];
m4_warn (0, NULL,
_("definition of `%s' contains sequence `%.*s'"),
- name, regs->end[0] - regs->start[0],
+ name, (int) (regs->end[0] - regs->start[0]),
defn + regs->start[0]);
}
}
@@ -940,7 +940,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);
}
}
@@ -974,8 +974,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);
}
}
@@ -1669,7 +1671,9 @@ m4_traceon (struct obstack *obs, int argc, macro_arguments *argv)
m4_warn (0, me, _("invalid macro name ignored"));
continue;
}
- s = lookup_symbol (ARG (i), SYMBOL_INSERT);
+ s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
+ if (!s)
+ s = lookup_symbol (ARG (i), SYMBOL_INSERT);
set_trace (s, obs);
}
}
diff --git a/src/debug.c b/src/debug.c
index fde6c499..0298fc88 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 f5c2deeb..eef6626e 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
{
@@ -334,6 +336,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;
}
@@ -503,26 +506,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)
@@ -537,12 +534,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 2ad82c2b..84cb8e07 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 2b728872..98b5b151 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -95,7 +95,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 *);
@@ -138,6 +137,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;
@@ -244,10 +258,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. */
@@ -397,11 +410,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;
@@ -497,11 +510,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 4dcbdf3e..20dc7447 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;
}