diff options
author | Eric Blake <ebb9@byu.net> | 2007-12-13 10:37:51 -0700 |
---|---|---|
committer | Eric Blake <ebb9@byu.net> | 2008-05-08 12:45:52 -0600 |
commit | ec804cba9ceeef7ca863d163eeae3fca669ccd16 (patch) | |
tree | 5051bb2d863465cc862beca5049f85227f72e8d7 | |
parent | 8a47a2029b7eb60ac61abb1b6423d4a67b371281 (diff) | |
download | m4-ec804cba9ceeef7ca863d163eeae3fca669ccd16.tar.gz |
Stage23: allow tracing indirect macro names
-rw-r--r-- | NEWS | 13 | ||||
-rw-r--r-- | doc/m4.texinfo | 116 | ||||
-rw-r--r-- | examples/null.err | bin | 18 -> 51 bytes | |||
-rw-r--r-- | examples/null.m4 | bin | 5747 -> 5876 bytes | |||
-rw-r--r-- | examples/null.out | bin | 402 -> 404 bytes | |||
-rw-r--r-- | src/builtin.c | 14 | ||||
-rw-r--r-- | src/debug.c | 168 | ||||
-rw-r--r-- | src/input.c | 42 | ||||
-rw-r--r-- | src/m4.c | 95 | ||||
-rw-r--r-- | src/m4.h | 30 | ||||
-rw-r--r-- | src/macro.c | 120 |
11 files changed, 360 insertions, 238 deletions
@@ -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 Binary files differindex 61518e88..d8258182 100644 --- a/examples/null.err +++ b/examples/null.err diff --git a/examples/null.m4 b/examples/null.m4 Binary files differindex 79f4715f..10d15aed 100644 --- a/examples/null.m4 +++ b/examples/null.m4 diff --git a/examples/null.out b/examples/null.out Binary files differindex aca4b785..cd3764f7 100644 --- a/examples/null.out +++ b/examples/null.out 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); } @@ -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); } @@ -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; } |