m4-patches
[Top][All Lists]
Advanced

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

argv_ref patch 24: allow NUL in macro names


From: Eric Blake
Subject: argv_ref patch 24: allow NUL in macro names
Date: Mon, 02 Jun 2008 21:50:58 -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 in the series.  This starts a subseries of patches to allow
transparent NUL handling through more of m4.  POSIX is silent on the
matter (m4 is only required to handle text files, and the presence of an
embedded NUL automatically disqualifies a file as text), and no other m4
implementation handles NUL characters sanely, but GNU Coding Standards
recommend being transparent when possible.  This patch focuses on allowing
NUL in a macro name.

I'm still not settled on how to generate NUL from within m4 - I'm thinking
that adding \ escapes to format might be the way to go, but don't want to
break backwards compatibility if it can be helped.  So all of the tests in
argv_ref that deal with NUL rely on printf(1) or a pre-formed file with
embedded NUL.

This patch doesn't really use too much extra memory, and the extra
bookkeeping for handling NUL might cause a slight slowdown but not
drastic.  [On the side, cygwin's strlen and memchr functions are now
several times faster on unaligned data as a result of evaluating the
timing impacts - word operations are faster than byte operations on modern
hardware.]  On the master branch, I split it into three patches -
reworking m4_error to take a call_info * instead of a char * (so that the
length can be passed in alongside the name), changing the symbol table to
support NUL, and changing all macro name output (warnings, dumpdef, trace)
to handle or quote difficult characters while avoiding extra munging of
the global location variables.  I still might add a debugmode(b) option
later on that enables \ quoting of dumpdef/trace output, and possibly even
make debugfile turn 'b' on or off automatically based on whether the new
output file is a tty, but that is not in argv_ref yet.

2008-06-02  Eric Blake  <address@hidden>

        Stage 24: Allow embedded NUL in macro names.
        Replace const char *macro_name with const m4_call_info *call.  Use
        length rather than NUL-termination when tracking macro names,
        including in diagnostics.  Quote instances of problematic
        characters in macro names when presented to user.
        Memory impact: none.
        Speed impact: slight penalty, due to more bookkeeping.
        * m4/gnulib-cache.m4: Import xmemdup0 module.
        * src/m4.h (define_builtin, push_string_init, push_wrapup_init)
        (lookup_symbol): Add parameters.
        (struct call_info): Add name_len member.
        (struct token_data) [ENABLE_CHANGEWORD]: Add original_len member.
        (struct symbol): Add len member.
        (TOKEN_DATA_ORIG_LEN, SYMBOL_NAME_LEN): New accessors.
        (m4_error_at_line, m4_warn_at_line): Delete.
        (m4_error, m4_warn, debug_set_output, skip_line, set_word_regexp)
        (bad_argc, evaluate): Adjust parameter type.
        * src/m4.c (m4_error_at_line, m4_warn_at_line): Delete.
        (m4_verror_at_line, m4_error, m4_warn): Adjust parameter type.
        (main): Adjust caller.
        * src/symtab.c (profile_strcmp) [DEBUG_SYMTAB]: Rename...
        (profile_memcmp): ...and accomodate NUL.
        (hash, lookup_symbol): Add parameter to track length.
        (symtab_debug, symtab_print_list): Adjust callers.
        * src/input.c (push_string_init, push_wrapup_init): Take location
        as parameter rather than using global state.
        (skip_line, set_word_regexp, next_token): Adjust parameter type.
        (peek_input, next_char_1, match_input, lex_debug): Adjust
        callers.
        * src/macro.c (struct macro_arguments): Delete argv0 and argv0_len
        members, now covered by info.
        (expand_input, expand_token, expand_argument, collect_arguments)
        (expand_macro, arg_text, arg_empty, arg_len, make_argv_ref)
        (push_arg, wrap_args): Adjust callers.
        * src/builtin.c (define_builtin): Add parameter.
        (bad_argc, numeric_arg, mkstemp_helper, substitute): Adjust
        parameter type.
        (define_user_macro, builtin_init, define_macro, m4_undefine)
        (m4_popdef, m4_ifdef, m4_ifelse, dumpdef_cmp, m4_dumpdef)
        (m4_builtin, m4_indir, m4_defn, m4_syscmd, m4_esyscmd, m4_eval)
        (m4_incr, m4_decr, m4_divert, m4_divnum, m4_undivert, m4_shift)
        (m4_changequote, m4_changecom, m4_changeword, include)
        (m4_maketemp, m4_mkstemp, m4_errprint, m4___file__, m4___line__)
        (m4___program__, m4_m4exit, m4_m4wrap, m4_traceon, m4_traceoff)
        (m4_debugmode, m4_debugfile, m4_len, m4_index, m4_substr)
        (m4_translit, m4_format, m4_regexp, m4_patsubst): Adjust all
        callers.
        * src/debug.c (debug_set_file, debug_set_output): Adjust parameter
        type.
        (trace_flush, trace_prepre, trace_pre): Adjust all callers.
        * src/eval.c (logical_or_term, logical_and_term, or_term)
        (xor_term, and_term, equality_term, cmp_term, shift_term)
        (add_term, mult_term, exp_term, unary_term, simple_term)
        (evaluate): Adjust parameter type.
        * src/format.c (arg_int, arg_long, arg_double): Likewise.
        (expand_format): Adjust caller.
        * doc/m4.texinfo (Using frozen files): Test this.
        * examples/null.m4: Likewise.
        * examples/null.err: Adjust expected output.
        * examples/null.out: Likewise.

- --
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

iEYEARECAAYFAkhEv6IACgkQ84KuGfSFAYCIjgCbBAPRTREJcR1B2cxsq+1d4x4u
fEMAnix0icRiKPEEXHqF0fhxlQ9g+HFP
=yDU0
-----END PGP SIGNATURE-----
From 3e33a6b3b9b2529939136c34b1e88bd3ee87a172 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 21 May 2008 06:59:01 -0600
Subject: [PATCH] Stage 24a: Use full call context in error reporting.

* m4/m4module.h (m4_error, m4_warn): Change parameter type.
(m4_error_at_line, m4_warn_at_line): Delete.
(m4_debug_set_output, m4_skip_line, m4_bad_argc, m4_numeric_arg)
(m4_parse_truth_arg, m4_symbol_value_lookup): Adjust all clients.
* m4/m4private.h (m4__next_token): Likewise.
* m4/utility.c (m4_verror_at_line): Alter parameter, and use
caller's location if caller is provided.
(m4_error, m4_warn): Change parameter type.
(m4_error_at_line, m4_warn_at_line): Delete.
(m4_bad_argc, m4_numeric_arg, m4_parse_truth_arg)
(m4_symbol_value_lookup): Adjust all callers.
* m4/debug.c (set_debug_file, m4_debug_set_output): Likewise.
* m4/input.c (m4_skip_line, m4__next_token): Likewise.
* m4/macro.c (expand_argument, collect_arguments, m4_macro_call)
(process_macro): Likewise.
* modules/m4.h (m4_make_temp_func): Likewise.
* modules/evalparse.c (m4_evaluate): Likewise.
* modules/format.c (arg_int, arg_long, arg_double, format):
Likewise.
* modules/gnu.c (builtin, changeresyntax, changesyntax, debugfile)
(debuglen, debugmode, esyscmd, indir, mkdtemp, patsubst, regexp)
(renamesyms, syncoutput, regexp_compile, substitute)
(regexp_substitute, m4_resyntax_encode_safe): Likewise.
* modules/m4.c (define, undefine, pushdef, popdef, ifdef, ifelse)
(m4_dump_symbols, defn, syscmd, incr, decr, divert, undivert, dnl)
(include, maketemp, mkstemp, m4exit, traceon, traceoff, substr):
Likewise.
* modules/stdlib.c (setenv, getpwuid, srand): Likewise.
* modules/time.c (ctime, gmtime, localtime, mktime, strftime):
Likewise.
* tests/options.at (--syncoutput): Add test for invalid
command-line argument.  Split xfailed portion...
(--syncoutput and diversions): ...into new test.
(unknown option): New test.
* tests/freeze.at (reloading unknown builtin): Update expected
output.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog           |   43 +++++++++++++++++++++
 m4/debug.c          |   26 ++++++------
 m4/input.c          |   83 +++++++++++++++++++++++++---------------
 m4/m4module.h       |   26 +++++-------
 m4/m4private.h      |    3 +-
 m4/macro.c          |   22 +++++------
 m4/utility.c        |  105 ++++++++++++++-------------------------------------
 modules/evalparse.c |    2 +-
 modules/format.c    |    8 ++--
 modules/gnu.c       |   61 ++++++++++++++---------------
 modules/m4.c        |   89 ++++++++++++++++++++++---------------------
 modules/m4.h        |    2 +-
 modules/stdlib.c    |    6 +-
 modules/time.c      |   10 ++--
 src/main.c          |    6 ++-
 tests/freeze.at     |    2 +-
 tests/options.at    |   24 ++++++++++++
 17 files changed, 278 insertions(+), 240 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 176c711..0545f86 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,48 @@
 2008-06-02  Eric Blake  <address@hidden>
 
+       Stage 24a: Use full call context in error reporting.
+       Replace const char *macro_name with const m4_call_info *call, so
+       that the next patch can pass the length of macro_name with
+       embedded NUL.  Simplifies handling of global state.
+       Memory impact: none.
+       Speed impact: none noticed.
+       * m4/m4module.h (m4_error, m4_warn): Change parameter type.
+       (m4_error_at_line, m4_warn_at_line): Delete.
+       (m4_debug_set_output, m4_skip_line, m4_bad_argc, m4_numeric_arg)
+       (m4_parse_truth_arg, m4_symbol_value_lookup): Adjust all clients.
+       * m4/m4private.h (m4__next_token): Likewise.
+       * m4/utility.c (m4_verror_at_line): Alter parameter, and use
+       caller's location if caller is provided.
+       (m4_error, m4_warn): Change parameter type.
+       (m4_error_at_line, m4_warn_at_line): Delete.
+       (m4_bad_argc, m4_numeric_arg, m4_parse_truth_arg)
+       (m4_symbol_value_lookup): Adjust all callers.
+       * m4/debug.c (set_debug_file, m4_debug_set_output): Likewise.
+       * m4/input.c (m4_skip_line, m4__next_token): Likewise.
+       * m4/macro.c (expand_argument, collect_arguments, m4_macro_call)
+       (process_macro): Likewise.
+       * modules/m4.h (m4_make_temp_func): Likewise.
+       * modules/evalparse.c (m4_evaluate): Likewise.
+       * modules/format.c (arg_int, arg_long, arg_double, format):
+       Likewise.
+       * modules/gnu.c (builtin, changeresyntax, changesyntax, debugfile)
+       (debuglen, debugmode, esyscmd, indir, mkdtemp, patsubst, regexp)
+       (renamesyms, syncoutput, regexp_compile, substitute)
+       (regexp_substitute, m4_resyntax_encode_safe): Likewise.
+       * modules/m4.c (define, undefine, pushdef, popdef, ifdef, ifelse)
+       (m4_dump_symbols, defn, syscmd, incr, decr, divert, undivert, dnl)
+       (include, maketemp, mkstemp, m4exit, traceon, traceoff, substr):
+       Likewise.
+       * modules/stdlib.c (setenv, getpwuid, srand): Likewise.
+       * modules/time.c (ctime, gmtime, localtime, mktime, strftime):
+       Likewise.
+       * tests/options.at (--syncoutput): Add test for invalid
+       command-line argument.  Split xfailed portion...
+       (--syncoutput and diversions): ...into new test.
+       (unknown option): New test.
+       * tests/freeze.at (reloading unknown builtin): Update expected
+       output.
+
        Adjust to recent gnulib change.
        * configure.ac (AB_INIT): Delete, now that gnulib does this.
 
diff --git a/m4/debug.c b/m4/debug.c
index e6c8ffb..d7e7e83 100644
--- a/m4/debug.c
+++ b/m4/debug.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1991, 1992, 1993, 1994, 2006, 2007 Free Software
-   Foundation, Inc.
+   Copyright (C) 1991, 1992, 1993, 1994, 2006, 2007, 2008 Free
+   Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -26,7 +26,7 @@
 #include "m4private.h"
 #include "close-stream.h"
 
-static void set_debug_file (m4 *, const char *, FILE *);
+static void set_debug_file (m4 *, const m4_call_info *, FILE *);
 
 
 
@@ -131,9 +131,9 @@ m4_debug_decode (m4 *context, int previous, const char 
*opts)
 
 /* 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 errors on behalf of MACRO.  */
+   correct relative position.  Report errors on behalf of CALLER.  */
 static void
-set_debug_file (m4 *context, const char *macro, FILE *fp)
+set_debug_file (m4 *context, const m4_call_info *caller, FILE *fp)
 {
   FILE *debug_file;
   struct stat stdout_stat, debug_stat;
@@ -143,7 +143,7 @@ set_debug_file (m4 *context, const char *macro, FILE *fp)
   debug_file = m4_get_debug_file (context);
   if (debug_file != NULL && debug_file != stderr && debug_file != stdout
       && close_stream (debug_file) != 0)
-    m4_error (context, 0, errno, macro, _("error writing to debug stream"));
+    m4_error (context, 0, errno, caller, _("error writing to debug stream"));
 
   debug_file = fp;
   m4_set_debug_file (context, fp);
@@ -162,7 +162,7 @@ set_debug_file (m4 *context, const char *macro, FILE *fp)
          && stdout_stat.st_ino != 0)
        {
          if (debug_file != stderr && close_stream (debug_file) != 0)
-           m4_error (context, 0, errno, macro,
+           m4_error (context, 0, errno, caller,
                      _("error writing to debug stream"));
          m4_set_debug_file (context, stdout);
        }
@@ -172,18 +172,18 @@ set_debug_file (m4 *context, const char *macro, FILE *fp)
 /* Change the debug output to file NAME.  If NAME is NULL, debug
    output is reverted to stderr, and if empty debug output is
    discarded.  Return true iff the output stream was changed.  Report
-   errors on behalf of MACRO.  */
+   errors on behalf of CALLER.  */
 bool
-m4_debug_set_output (m4 *context, const char *macro, const char *name)
+m4_debug_set_output (m4 *context, const m4_call_info *caller, const char *name)
 {
   FILE *fp;
 
   assert (context);
 
   if (name == NULL)
-    set_debug_file (context, macro, stderr);
+    set_debug_file (context, caller, stderr);
   else if (*name == '\0')
-    set_debug_file (context, macro, NULL);
+    set_debug_file (context, caller, NULL);
   else
     {
       fp = fopen (name, "a");
@@ -191,9 +191,9 @@ m4_debug_set_output (m4 *context, const char *macro, const 
char *name)
        return false;
 
       if (set_cloexec_flag (fileno (fp), true) != 0)
-       m4_warn (context, errno, macro,
+       m4_warn (context, errno, caller,
                 _("cannot protect debug file across forks"));
-      set_debug_file (context, macro, fp);
+      set_debug_file (context, caller, fp);
     }
   return true;
 }
diff --git a/m4/input.c b/m4/input.c
index a021e70..d2a508e 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -1334,30 +1334,18 @@ unget_input (int ch)
 }
 
 /* skip_line () simply discards all immediately following characters,
-   up to the first newline.  It is only used from m4_dnl ().  NAME is
-   the spelling of argv[0], for use in any warning message.  */
+   up to the first newline.  It is only used from m4_dnl ().  Report
+   errors on behalf of CALLER.  */
 void
-m4_skip_line (m4 *context, const char *name)
+m4_skip_line (m4 *context, const m4_call_info *caller)
 {
   int ch;
-  const char *file = m4_get_current_file (context);
-  int line = m4_get_current_line (context);
 
   while ((ch = next_char (context, false, false, false)) != CHAR_EOF
         && ch != '\n')
     ;
   if (ch == CHAR_EOF)
-    /* current_file changed; use the previous value we cached.  */
-    m4_warn_at_line (context, 0, file, line, name,
-                    _("end of file treated as newline"));
-  /* On the rare occasion that dnl crosses include file boundaries
-     (either the input file did not end in a newline, or changesyntax
-     was used), calling next_char can update current_file and
-     current_line, and that update will be undone as we return to
-     expand_macro.  This tells next_char () to restore the location.  */
-  if (file != m4_get_current_file (context)
-      || line != m4_get_current_line (context))
-    input_change = true;
+    m4_warn (context, 0, caller, _("end of file treated as newline"));
 }
 
 
@@ -1521,13 +1509,12 @@ m4_input_exit (void)
    m4__next_token () is called.  */
 m4__token_type
 m4__next_token (m4 *context, m4_symbol_value *token, int *line,
-               m4_obstack *obs, bool allow_argv, const char *caller)
+               m4_obstack *obs, bool allow_argv, const m4_call_info *caller)
 {
   int ch;
   int quote_level;
   m4__token_type type;
-  const char *file;
-  int dummy;
+  const char *file = NULL;
   size_t len;
   /* The obstack where token data is stored.  Generally token_stack,
      for tokens where argument collection might not use the literal
@@ -1536,8 +1523,6 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
   m4_obstack *obs_safe = &token_stack;
 
   assert (next == NULL);
-  if (!line)
-    line = &dummy;
   memset (token, '\0', sizeof *token);
   do {
     obstack_free (&token_stack, token_bottom);
@@ -1545,6 +1530,11 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
     /* Must consume an input character.  */
     ch = next_char (context, false, allow_argv && m4__quote_age (M4SYNTAX),
                    false);
+    if (line)
+      {
+       *line = m4_get_current_line (context);
+       file = m4_get_current_file (context);
+      }
     if (ch == CHAR_EOF)                        /* EOF */
       {
 #ifdef DEBUG_INPUT
@@ -1570,9 +1560,6 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
        return M4_TOKEN_ARGV;
       }
 
-    file = m4_get_current_file (context);
-    *line = m4_get_current_line (context);
-
     if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ESCAPE))
       {                                        /* ESCAPED WORD */
        obstack_1grow (&token_stack, ch);
@@ -1607,8 +1594,16 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
            ch = next_char (context, obs && m4__quote_age (M4SYNTAX), false,
                            false);
            if (ch == CHAR_EOF)
-             m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
-                               _("end of file in string"));
+             {
+               if (!caller)
+                 {
+                   assert (line);
+                   m4_set_current_file (context, file);
+                   m4_set_current_line (context, *line);
+                 }
+               m4_error (context, EXIT_FAILURE, 0, caller,
+                         _("end of file in string"));
+             }
            if (ch == CHAR_BUILTIN)
              init_builtin_token (context, obs, obs ? token : NULL);
            else if (ch == CHAR_QUOTE)
@@ -1640,8 +1635,16 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
          {
            ch = next_char (context, false, false, false);
            if (ch == CHAR_EOF)
-             m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
-                               _("end of file in string"));
+             {
+               if (!caller)
+                 {
+                   assert (line);
+                   m4_set_current_file (context, file);
+                   m4_set_current_line (context, *line);
+                 }
+               m4_error (context, EXIT_FAILURE, 0, caller,
+                         _("end of file in string"));
+             }
            if (ch == CHAR_BUILTIN)
              init_builtin_token (context, obs, obs ? token : NULL);
            else if (MATCH (context, ch, context->syntax->quote.str2, true))
@@ -1670,8 +1673,16 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
          {
            ch = next_char (context, false, false, false);
            if (ch == CHAR_EOF)
-             m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
-                               _("end of file in comment"));
+             {
+               if (!caller)
+                 {
+                   assert (line);
+                   m4_set_current_file (context, file);
+                   m4_set_current_line (context, *line);
+                 }
+               m4_error (context, EXIT_FAILURE, 0, caller,
+                         _("end of file in comment"));
+             }
            if (ch == CHAR_BUILTIN)
              {
                init_builtin_token (context, NULL, NULL);
@@ -1699,8 +1710,16 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
          {
            ch = next_char (context, false, false, false);
            if (ch == CHAR_EOF)
-             m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
-                               _("end of file in comment"));
+             {
+               if (!caller)
+                 {
+                   assert (line);
+                   m4_set_current_file (context, file);
+                   m4_set_current_line (context, *line);
+                 }
+               m4_error (context, EXIT_FAILURE, 0, caller,
+                         _("end of file in comment"));
+             }
            if (ch == CHAR_BUILTIN)
              {
                init_builtin_token (context, NULL, NULL);
diff --git a/m4/m4module.h b/m4/m4module.h
index 96b76ea..fb404ea 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -159,24 +159,19 @@ struct m4_string_pair
    `m4_macro_args *argv' are in scope.  */
 #define M4ARGLEN(i) m4_arg_len (context, argv, i)
 
-extern bool    m4_bad_argc        (m4 *, int, const char *, size_t, size_t,
+extern bool    m4_bad_argc        (m4 *, int, const m4_call_info *, size_t,
+                                   size_t, bool);
+extern bool    m4_numeric_arg     (m4 *, const m4_call_info *, const char *,
+                                   int *);
+extern bool    m4_parse_truth_arg (m4 *, const m4_call_info *, const char *,
                                    bool);
-extern bool    m4_numeric_arg     (m4 *, const char *, const char *, int *);
-extern bool    m4_parse_truth_arg (m4 *, const char *, const char *, bool);
-extern m4_symbol *m4_symbol_value_lookup (m4 *, const char *,
-                                         m4_macro_args *, size_t, bool);
+extern m4_symbol *m4_symbol_value_lookup (m4 *, m4_macro_args *, size_t, bool);
 
 /* Error handling.  */
-extern void m4_error (m4 *, int, int, const char *, const char *, ...)
+extern void m4_error (m4 *, int, int, const m4_call_info *, const char *, ...)
   M4_GNUC_PRINTF (5, 6);
-extern void m4_error_at_line (m4 *, int, int, const char *, int,
-                             const char *, const char *, ...)
-  M4_GNUC_PRINTF (7, 8);
-extern void m4_warn  (m4 *, int, const char *, const char *, ...)
+extern void m4_warn  (m4 *, int, const m4_call_info *, const char *, ...)
   M4_GNUC_PRINTF (4, 5);
-extern void m4_warn_at_line  (m4 *, int, const char *, int, const char *,
-                             const char *, ...)
-  M4_GNUC_PRINTF (6, 7);
 
 extern const char *    m4_get_program_name (void);
 extern void            m4_set_program_name (const char *);
@@ -409,7 +404,8 @@ enum {
 #define m4_is_debug_bit(C,B)   ((m4_get_debug_level_opt (C) & (B)) != 0)
 
 extern int     m4_debug_decode         (m4 *, int, const char *);
-extern bool    m4_debug_set_output     (m4 *, const char *, const char *);
+extern bool    m4_debug_set_output     (m4 *, const m4_call_info *,
+                                        const char *);
 extern void    m4_debug_message_prefix (m4 *);
 extern void    m4_debug_message        (m4 *, int, const char *, ...)
   M4_GNUC_PRINTF (3, 4);
@@ -491,7 +487,7 @@ extern int  m4_set_syntax   (m4_syntax_table*, char, char, 
const char*);
 
 extern void    m4_input_init   (m4 *context);
 extern void    m4_input_exit   (void);
-extern void    m4_skip_line    (m4 *context, const char *);
+extern void    m4_skip_line    (m4 *context, const m4_call_info *);
 
 /* push back input */
 
diff --git a/m4/m4private.h b/m4/m4private.h
index 526a0ef..c092016 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -564,7 +564,8 @@ extern      bool            m4__push_symbol (m4 *, 
m4_symbol_value *, size_t,
 extern m4_obstack      *m4__push_wrapup_init (m4 *, m4__symbol_chain ***);
 extern void            m4__push_wrapup_finish (void);
 extern m4__token_type  m4__next_token (m4 *, m4_symbol_value *, int *,
-                                       m4_obstack *, bool, const char *);
+                                       m4_obstack *, bool,
+                                       const m4_call_info *);
 extern bool            m4__next_token_is_open (m4 *);
 
 /* Fast macro versions of macro argv accessor functions,
diff --git a/m4/macro.c b/m4/macro.c
index 1e88299..43c3ad8 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -128,7 +128,7 @@ 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);
 static bool    expand_argument   (m4 *, m4_obstack *, m4_symbol_value *,
-                                 const char *);
+                                 const m4_call_info *);
 static void    process_macro    (m4 *, m4_symbol_value *, m4_obstack *, int,
                                  m4_macro_args *);
 
@@ -290,12 +290,11 @@ expand_token (m4 *context, m4_obstack *obs, 
m4__token_type type,
    Report errors on behalf of CALLER.  */
 static bool
 expand_argument (m4 *context, m4_obstack *obs, m4_symbol_value *argp,
-                const char *caller)
+                const m4_call_info *caller)
 {
   m4__token_type type;
   m4_symbol_value token;
   int paren_level = 0;
-  const char *file = m4_get_current_file (context);
   int line = m4_get_current_line (context);
   size_t len;
   unsigned int age = m4__quote_age (M4SYNTAX);
@@ -362,8 +361,8 @@ expand_argument (m4 *context, m4_obstack *obs, 
m4_symbol_value *argp,
          break;
 
        case M4_TOKEN_EOF:
-         m4_error_at_line (context, EXIT_FAILURE, 0, file, line, caller,
-                           _("end of file in argument list"));
+         m4_error (context, EXIT_FAILURE, 0, caller,
+                   _("end of file in argument list"));
          break;
 
        case M4_TOKEN_WORD:
@@ -584,12 +583,12 @@ collect_arguments (m4 *context, m4_call_info *info, 
m4_symbol *symbol,
   if (m4__next_token_is_open (context))
     {
       /* Gobble parenthesis, then collect arguments.  */
-      m4__next_token (context, &token, NULL, NULL, false, info->name);
+      m4__next_token (context, &token, NULL, NULL, false, info);
       do
        {
          tokenp = (m4_symbol_value *) obstack_alloc (arguments,
                                                      sizeof *tokenp);
-         more_args = expand_argument (context, arguments, tokenp, info->name);
+         more_args = expand_argument (context, arguments, tokenp, info);
 
          if ((m4_is_symbol_value_text (tokenp)
               && !m4_get_symbol_value_len (tokenp))
@@ -661,7 +660,7 @@ m4_macro_call (m4 *context, m4_symbol_value *value, 
m4_obstack *expansion,
 
   if (argv->info->trace)
     trace_start = trace_pre (context, argv);
-  if (!m4_bad_argc (context, argv->argc, argv->argv0,
+  if (!m4_bad_argc (context, argv->argc, argv->info,
                    VALUE_MIN_ARGS (value), VALUE_MAX_ARGS (value),
                    BIT_TEST (VALUE_FLAGS (value),
                              VALUE_SIDE_EFFECT_ARGS_BIT)))
@@ -672,7 +671,7 @@ m4_macro_call (m4 *context, m4_symbol_value *value, 
m4_obstack *expansion,
        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),
+       m4_warn (context, 0, argv->info,
                 _("builtin `%s' requested by frozen file not found"),
                 m4_get_symbol_value_placeholder (value));
       else
@@ -785,9 +784,8 @@ process_macro (m4 *context, m4_symbol_value *value, 
m4_obstack *obs,
                }
              else
                {
-                 m4_error (context, 0, 0, M4ARG (0),
-                           _("unterminated parameter reference: %s"),
-                           key);
+                 m4_error (context, 0, 0, argv->info,
+                           _("unterminated parameter reference: %s"), key);
                }
 
              len -= endp - text;
diff --git a/m4/utility.c b/m4/utility.c
index 2cd4d18..b15e17f 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -41,8 +41,8 @@ static const char *skip_space (m4 *, const char *);
    Return true if the macro is guaranteed to expand to the empty
    string, false otherwise.  */
 bool
-m4_bad_argc (m4 *context, int argc, const char *caller, size_t min, size_t max,
-            bool side_effect)
+m4_bad_argc (m4 *context, int argc, const m4_call_info *caller, size_t min,
+            size_t max, bool side_effect)
 {
   if (argc - 1 < min)
     {
@@ -74,7 +74,8 @@ skip_space (m4 *context, const char *arg)
 /* FIXME: Convert this to use gnulib's xstrtoimax, xstrtoumax.
    Otherwise, we are arbitrarily limiting integer values.  */
 bool
-m4_numeric_arg (m4 *context, const char *caller, const char *arg, int *valuep)
+m4_numeric_arg (m4 *context, const m4_call_info *caller, const char *arg,
+               int *valuep)
 {
   char *endp;
 
@@ -96,10 +97,10 @@ m4_numeric_arg (m4 *context, const char *caller, const char 
*arg, int *valuep)
 }
 
 /* Parse ARG as a truth value.  If unrecognized, issue a warning on
-   behalf of ME and return PREVIOUS; otherwise return the parsed
+   behalf of CALLER and return PREVIOUS; otherwise return the parsed
    value.  */
 bool
-m4_parse_truth_arg (m4 *context, const char *arg, const char *me,
+m4_parse_truth_arg (m4 *context, const m4_call_info *caller, const char *arg,
                    bool previous)
 {
   /* 0, no, off, blank... */
@@ -115,17 +116,17 @@ m4_parse_truth_arg (m4 *context, const char *arg, const 
char *me,
       || ((arg[0] == 'o' || arg[0] == 'O')
          && (arg[1] == 'n' || arg[1] == 'N')))
     return true;
-  m4_warn (context, 0, me, _("unknown directive `%s'"), arg);
+  m4_warn (context, 0, caller, _("unknown directive `%s'"), arg);
   return previous;
 }
 
 /* Helper method to look up a symbol table entry given an argument.
-   Warn on behalf of CALLER if VALUE is not a text argument, or if
+   Warn on behalf of ARGV if VALUE is not a text argument, or if
    MUST_EXIST and no macro exists by the name in VALUE.  Return the
    result of the lookup, or NULL.  */
 m4_symbol *
-m4_symbol_value_lookup (m4 *context, const char *caller,
-                       m4_macro_args *argv, size_t i, bool must_exist)
+m4_symbol_value_lookup (m4 *context, m4_macro_args *argv, size_t i,
+                       bool must_exist)
 {
   m4_symbol *result = NULL;
   if (m4_is_arg_text (argv, i))
@@ -133,26 +134,30 @@ m4_symbol_value_lookup (m4 *context, const char *caller,
       const char *name = M4ARG (i);
       result = m4_symbol_lookup (M4SYMTAB, name);
       if (must_exist && !result)
-       m4_warn (context, 0, caller, _("undefined macro `%s'"), name);
+       m4_warn (context, 0, argv->info, _("undefined macro `%s'"), name);
     }
   else
-    m4_warn (context, 0, caller, _("invalid macro name ignored"));
+    m4_warn (context, 0, argv->info, _("invalid macro name ignored"));
   return result;
 }
 
 /* Helper for all error reporting.  Report message based on FORMAT and
-   ARGS, on behalf of MACRO, at the optional location FILE and LINE.
-   If ERRNUM, decode the errno value as part of the message.  If
-   STATUS, exit immediately with that status.  If WARN, prepend
-   'Warning: '.  */
+   ARGS, on behalf of CALLER (if any), otherwise at the global
+   position in CONTEXT.  If ERRNUM, decode the errno value as part of
+   the message.  If STATUS, exit immediately with that status.  If
+   WARN, prepend 'Warning: '.  */
 static void
 m4_verror_at_line (m4 *context, bool warn, int status, int errnum,
-                  const char *file, int line, const char *macro,
-                  const char *format, va_list args)
+                  const m4_call_info *caller, const char *format,
+                  va_list args)
 {
   char *full = NULL;
   char *safe_macro = NULL;
+  const char *macro = caller ? caller->name : NULL;
+  const char *file = caller ? caller->file : m4_get_current_file (context);
+  int line = caller ? caller->line : m4_get_current_line (context);
 
+  assert (file || !line);
   /* Sanitize MACRO, since we are turning around and using it in a
      format string.  The allocation is overly conservative, but
      problematic macro names only occur via indir or changesyntax.  */
@@ -192,43 +197,19 @@ m4_verror_at_line (m4 *context, bool warn, int status, 
int errnum,
    any other arguments, and the program name and location (if we are
    currently parsing an input file) are automatically prepended.  If
    ERRNUM is non-zero, include strerror output in the message.  If
-   MACRO, prepend the message with the macro where the message
+   CALLER, prepend the message with the macro where the message
    occurred.  If STATUS is non-zero, or if errors are fatal, call exit
    immediately; otherwise, remember that an error occurred so that m4
    cannot exit with success later on.*/
 void
-m4_error (m4 *context, int status, int errnum, const char *macro,
+m4_error (m4 *context, int status, int errnum, const m4_call_info *caller,
          const char *format, ...)
 {
   va_list args;
-  int line = m4_get_current_line (context);
-  assert (m4_get_current_file (context) || !line);
   va_start (args, format);
   if (status == EXIT_SUCCESS && m4_get_warnings_exit_opt (context))
     status = EXIT_FAILURE;
-  m4_verror_at_line (context, false, status, errnum,
-                    m4_get_current_file (context), line, macro, format, args);
-  va_end (args);
-}
-
-/* Issue an error.  The message is printf-style, based on FORMAT and
-   any other arguments, and the program name and location (from FILE
-   and LINE) are automatically prepended.  If ERRNUM is non-zero,
-   include strerror output in the message.  If STATUS is non-zero, or
-   if errors are fatal, call exit immediately; otherwise, remember
-   that an error occurred so that m4 cannot exit with success later
-   on.  If MACRO, prepend the message with the macro where the message
-   occurred.  */
-void
-m4_error_at_line (m4 *context, int status, int errnum, const char *file,
-                 int line, const char *macro, const char *format, ...)
-{
-  va_list args;
-  va_start (args, format);
-  if (status == EXIT_SUCCESS && m4_get_warnings_exit_opt (context))
-    status = EXIT_FAILURE;
-  m4_verror_at_line (context, false, status, errnum, file, line, macro,
-                    format, args);
+  m4_verror_at_line (context, false, status, errnum, caller, format, args);
   va_end (args);
 }
 
@@ -236,39 +217,13 @@ m4_error_at_line (m4 *context, int status, int errnum, 
const char *file,
    printf-style, based on FORMAT and any other arguments, and the
    program name, location (if we are currently parsing an input file),
    and "Warning:" are automatically prepended.  If ERRNUM is non-zero,
-   include strerror output in the message.  If MACRO, prepend the
+   include strerror output in the message.  If CALLER, prepend the
    message with the macro where the message occurred.  If warnings are
    fatal, call exit immediately, otherwise exit status is
    unchanged.  */
 void
-m4_warn (m4 *context, int errnum, const char *macro, const char *format, ...)
-{
-  if (!m4_get_suppress_warnings_opt (context))
-    {
-      va_list args;
-      int status = EXIT_SUCCESS;
-      int line = m4_get_current_line (context);
-      assert (m4_get_current_file (context) || !line);
-      va_start (args, format);
-      if (m4_get_warnings_exit_opt (context))
-       status = EXIT_FAILURE;
-      m4_verror_at_line (context, true, status, errnum,
-                        m4_get_current_file (context), line, macro, format,
-                        args);
-      va_end (args);
-    }
-}
-
-/* Issue a warning, if they are not being suppressed.  The message is
-   printf-style, based on FORMAT and any other arguments, and the
-   program name, location (from FILE and LINE), and "Warning:" are
-   automatically prepended.  If ERRNUM is non-zero, include strerror
-   output in the message.  If MACRO, prepend the message with the
-   macro where the message occurred.  If warnings are fatal, call exit
-   immediately, otherwise exit status is unchanged.  */
-void
-m4_warn_at_line (m4 *context, int errnum, const char *file, int line,
-                const char *macro, const char *format, ...)
+m4_warn (m4 *context, int errnum, const m4_call_info *caller,
+        const char *format, ...)
 {
   if (!m4_get_suppress_warnings_opt (context))
     {
@@ -277,13 +232,11 @@ m4_warn_at_line (m4 *context, int errnum, const char 
*file, int line,
       va_start (args, format);
       if (m4_get_warnings_exit_opt (context))
        status = EXIT_FAILURE;
-      m4_verror_at_line (context, true, status, errnum, file, line, macro,
-                        format, args);
+      m4_verror_at_line (context, true, status, errnum, caller, format, args);
       va_end (args);
     }
 }
 
-
 /* Wrap the gnulib progname module, to avoid exporting a global
    variable from a library.  Retrieve the program name for use in
    error messages and the __program__ macro.  */
diff --git a/modules/evalparse.c b/modules/evalparse.c
index 890816d..8ad7182 100644
--- a/modules/evalparse.c
+++ b/modules/evalparse.c
@@ -887,7 +887,7 @@ simple_term (m4 *context, eval_token et, number *v1)
 void
 m4_evaluate (m4 *context, m4_obstack *obs, size_t argc, m4_macro_args *argv)
 {
-  const char * me      = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   const char * str     = M4ARG (1);
   int          radix   = 10;
   int          min     = 1;
diff --git a/modules/format.c b/modules/format.c
index 4ff72b9..f5695e4 100644
--- a/modules/format.c
+++ b/modules/format.c
@@ -28,7 +28,7 @@
 
 /* Parse STR as an integer, reporting warnings on behalf of ME.  */
 static int
-arg_int (struct m4 *context, const char *me, const char *str)
+arg_int (struct m4 *context, const m4_call_info *me, const char *str)
 {
   char *endp;
   long value;
@@ -53,7 +53,7 @@ arg_int (struct m4 *context, const char *me, const char *str)
 
 /* Parse STR as a long, reporting warnings on behalf of ME.  */
 static long
-arg_long (struct m4 *context, const char *me, const char *str)
+arg_long (struct m4 *context, const m4_call_info *me, const char *str)
 {
   char *endp;
   long value;
@@ -78,7 +78,7 @@ arg_long (struct m4 *context, const char *me, const char *str)
 
 /* Parse STR as a double, reporting warnings on behalf of ME.  */
 static double
-arg_double (struct m4 *context, const char *me, const char *str)
+arg_double (struct m4 *context, const m4_call_info *me, const char *str)
 {
   char *endp;
   double value;
@@ -121,7 +121,7 @@ arg_double (struct m4 *context, const char *me, const char 
*str)
 static void
 format (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv)
 {
-  const char *me = M4ARG (0);          /* Macro name.  */
+  const m4_call_info *me = m4_arg_info (argv);
   const char *f;                       /* Format control string.  */
   const char *fmt;                     /* Position within f.  */
   char fstart[] = "%'+- 0#*.*hhd";     /* Current format spec.  */
diff --git a/modules/gnu.c b/modules/gnu.c
index c28a100..78c69b5 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -130,7 +130,7 @@ static m4_pattern_buffer regex_cache[REGEX_CACHE_SIZE];
    CALLER, and return NULL.  */
 
 static m4_pattern_buffer *
-regexp_compile (m4 *context, const char *caller, const char *regexp,
+regexp_compile (m4 *context, const m4_call_info *caller, const char *regexp,
                size_t len, int resyntax)
 {
   /* regex_cache is guaranteed to start life 0-initialized, which
@@ -231,7 +231,7 @@ regexp_search (m4_pattern_buffer *buf, const char *string, 
const int size,
    on behalf of CALLER.  BUF may be NULL for the empty regex.  */
 
 static void
-substitute (m4 *context, m4_obstack *obs, const char *caller,
+substitute (m4 *context, m4_obstack *obs, const m4_call_info *caller,
            const char *victim, const char *repl, m4_pattern_buffer *buf)
 {
   int ch;
@@ -285,7 +285,7 @@ substitute (m4 *context, m4_obstack *obs, const char 
*caller,
    copying the input if no changes are made.  */
 
 static bool
-regexp_substitute (m4 *context, m4_obstack *obs, const char *caller,
+regexp_substitute (m4 *context, m4_obstack *obs, const m4_call_info *caller,
                   const char *victim, size_t len, const char *regexp,
                   m4_pattern_buffer *buf, const char *replace,
                   bool optimize)
@@ -402,7 +402,7 @@ M4BUILTIN_HANDLER (__program__)
  **/
 M4BUILTIN_HANDLER (builtin)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   const char *name;
   m4_symbol_value *value;
 
@@ -440,27 +440,27 @@ M4BUILTIN_HANDLER (builtin)
       else
        {
          const m4_builtin *bp = m4_get_symbol_value_builtin (value);
-         if (!m4_bad_argc (context, argc - 1, name,
+         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),
+                                      flatten, false);
+         if (!m4_bad_argc (context, argc - 1, m4_arg_info (new_argv),
                            bp->min_args, bp->max_args,
                            (bp->flags & M4_BUILTIN_SIDE_EFFECT) != 0))
-           {
-             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),
-                                          flatten, false);
-             bp->func (context, obs, argc - 1, new_argv);
-           }
+           bp->func (context, obs, argc - 1, new_argv);
          free (value);
        }
     }
 }
 
 
-/* Change the current regexp syntax.  Currently this affects the
-   builtins: `patsubst', `regexp' and `renamesyms'.  */
+/* Change the current regexp syntax to SPEC, or report failure on
+   behalf of CALLER.  Currently this affects the builtins: `patsubst',
+   `regexp' and `renamesyms'.  */
 
 static int
-m4_resyntax_encode_safe (m4 *context, const char *caller, const char *spec)
+m4_resyntax_encode_safe (m4 *context, const m4_call_info *caller,
+                        const char *spec)
 {
   int resyntax = m4_regexp_syntax_encode (spec);
 
@@ -476,7 +476,8 @@ m4_resyntax_encode_safe (m4 *context, const char *caller, 
const char *spec)
  **/
 M4BUILTIN_HANDLER (changeresyntax)
 {
-  int resyntax = m4_resyntax_encode_safe (context, M4ARG (0), M4ARG (1));
+  int resyntax = m4_resyntax_encode_safe (context, m4_arg_info (argv),
+                                         M4ARG (1));
 
   if (resyntax >= 0)
     m4_set_regexp_syntax_opt (context, resyntax);
@@ -493,7 +494,7 @@ M4BUILTIN_HANDLER (changeresyntax)
  **/
 M4BUILTIN_HANDLER (changesyntax)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   M4_MODULE_IMPORT (m4, m4_expand_ranges);
 
   if (m4_expand_ranges)
@@ -536,7 +537,7 @@ M4BUILTIN_HANDLER (changesyntax)
  **/
 M4BUILTIN_HANDLER (debugfile)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
 
   if (argc == 1)
     m4_debug_set_output (context, me, NULL);
@@ -558,7 +559,7 @@ M4BUILTIN_HANDLER (debuglen)
 {
   int i;
   size_t s;
-  if (!m4_numeric_arg (context, M4ARG (0), M4ARG (1), &i))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &i))
     return;
   /* FIXME - make m4_numeric_arg more powerful - we want to accept
      suffixes, and limit the result to size_t.  */
@@ -584,8 +585,8 @@ M4BUILTIN_HANDLER (debugmode)
     {
       new_debug_level = m4_debug_decode (context, debug_level, M4ARG (1));
       if (new_debug_level < 0)
-       m4_error (context, 0, 0, M4ARG (0), _("bad debug flags: `%s'"),
-                 M4ARG (1));
+       m4_error (context, 0, 0, m4_arg_info (argv),
+                 _("bad debug flags: `%s'"), M4ARG (1));
       else
        m4_set_debug_level_opt (context, new_debug_level);
     }
@@ -601,7 +602,7 @@ M4BUILTIN_HANDLER (debugmode)
 
 M4BUILTIN_HANDLER (esyscmd)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   M4_MODULE_IMPORT (m4, m4_set_sysval);
   M4_MODULE_IMPORT (m4, m4_sysval_flush);
 
@@ -667,7 +668,7 @@ M4BUILTIN_HANDLER (format)
  **/
 M4BUILTIN_HANDLER (indir)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   if (!m4_is_arg_text (argv, 1))
     m4_warn (context, 0, me, _("invalid macro name ignored"));
   else
@@ -701,7 +702,8 @@ M4BUILTIN_HANDLER (mkdtemp)
   M4_MODULE_IMPORT (m4, m4_make_temp);
 
   if (m4_make_temp)
-    m4_make_temp (context, obs, M4ARG (0), M4ARG (1), M4ARGLEN (1), true);
+    m4_make_temp (context, obs, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+                 true);
   else
     assert (!"Unable to import from m4 module");
 }
@@ -719,13 +721,12 @@ M4BUILTIN_HANDLER (mkdtemp)
  **/
 M4BUILTIN_HANDLER (patsubst)
 {
-  const char *me;              /* name of this macro */
+  const m4_call_info *me = m4_arg_info (argv);
   const char *pattern;         /* regular expression */
   const char *replace;         /* replacement */
   m4_pattern_buffer *buf;      /* compiled regular expression */
   int resyntax;
 
-  me = M4ARG (0);
   pattern = M4ARG (2);
   replace = M4ARG (3);
 
@@ -766,7 +767,7 @@ M4BUILTIN_HANDLER (patsubst)
  **/
 M4BUILTIN_HANDLER (regexp)
 {
-  const char *me;              /* name of this macro */
+  const m4_call_info *me = m4_arg_info (argv);
   const char *victim;          /* string to search */
   const char *pattern;         /* regular expression */
   const char *replace;         /* optional replacement string */
@@ -775,7 +776,6 @@ M4BUILTIN_HANDLER (regexp)
   size_t len;                  /* length of first argument */
   int resyntax;
 
-  me = M4ARG (0);
   pattern = M4ARG (2);
   replace = M4ARG (3);
   resyntax = m4_get_regexp_syntax_opt (context);
@@ -850,7 +850,7 @@ M4BUILTIN_HANDLER (renamesyms)
 
   if (m4_dump_symbols)
     {
-      const char *me;          /* name of this macro */
+      const m4_call_info *me = m4_arg_info (argv);
       const char *regexp;      /* regular expression string */
       const char *replace;     /* replacement expression string */
 
@@ -860,7 +860,6 @@ M4BUILTIN_HANDLER (renamesyms)
 
       int resyntax;
 
-      me      = M4ARG (0);
       regexp  = M4ARG (1);
       replace = M4ARG (2);
 
@@ -937,6 +936,6 @@ M4BUILTIN_HANDLER (m4symbols)
 M4BUILTIN_HANDLER (syncoutput)
 {
   bool value = m4_get_syncoutput_opt (context);
-  value = m4_parse_truth_arg (context, M4ARG (1), M4ARG (0), value);
+  value = m4_parse_truth_arg (context, m4_arg_info (argv), M4ARG (1), value);
   m4_set_syncoutput_opt (context, value);
 }
diff --git a/modules/m4.c b/modules/m4.c
index 5cb6d11..bc93ee0 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -47,13 +47,13 @@
 #define m4_expand_ranges       m4_LTX_m4_expand_ranges
 #define m4_make_temp           m4_LTX_m4_make_temp
 
-extern void m4_set_sysval    (int value);
-extern void m4_sysval_flush  (m4 *context, bool report);
-extern void m4_dump_symbols  (m4 *context, m4_dump_symbol_data *data,
-                             size_t argc, m4_macro_args *argv, bool complain);
-extern const char *m4_expand_ranges (const char *s, m4_obstack *obs);
-extern void m4_make_temp     (m4 *context, m4_obstack *obs, const char *macro,
-                             const char *name, size_t len, bool dir);
+extern void m4_set_sysval    (int);
+extern void m4_sysval_flush  (m4 *, bool);
+extern void m4_dump_symbols  (m4 *, m4_dump_symbol_data *, size_t,
+                             m4_macro_args *, bool);
+extern const char *m4_expand_ranges (const char *, m4_obstack *);
+extern void m4_make_temp     (m4 *, m4_obstack *, const m4_call_info *,
+                             const char *, size_t, bool);
 
 /* Maintain each of the builtins implemented in this modules along
    with their details in a single table for easy maintenance.
@@ -153,47 +153,49 @@ M4INIT_HANDLER (m4)
 
 M4BUILTIN_HANDLER (define)
 {
+  const m4_call_info *me = m4_arg_info (argv);
+
   if (m4_is_arg_text (argv, 1))
     {
       m4_symbol_value *value = m4_symbol_value_create ();
 
       if (m4_symbol_value_copy (context, value, m4_arg_symbol (argv, 2)))
-       m4_warn (context, 0, M4ARG (0), _("cannot concatenate builtins"));
+       m4_warn (context, 0, me, _("cannot concatenate builtins"));
       m4_symbol_define (M4SYMTAB, M4ARG (1), value);
     }
   else
-    m4_warn (context, 0, M4ARG (0), _("invalid macro name ignored"));
+    m4_warn (context, 0, me, _("invalid macro name ignored"));
 }
 
 M4BUILTIN_HANDLER (undefine)
 {
-  const char *me = M4ARG (0);
   size_t i;
   for (i = 1; i < argc; i++)
-    if (m4_symbol_value_lookup (context, me, argv, i, true))
+    if (m4_symbol_value_lookup (context, argv, i, true))
       m4_symbol_delete (M4SYMTAB, M4ARG (i));
 }
 
 M4BUILTIN_HANDLER (pushdef)
 {
+  const m4_call_info *me = m4_arg_info (argv);
+
   if (m4_is_arg_text (argv, 1))
     {
       m4_symbol_value *value = m4_symbol_value_create ();
 
       if (m4_symbol_value_copy (context, value, m4_arg_symbol (argv, 2)))
-       m4_warn (context, 0, M4ARG (0), _("cannot concatenate builtins"));
+       m4_warn (context, 0, me, _("cannot concatenate builtins"));
       m4_symbol_pushdef (M4SYMTAB, M4ARG (1), value);
     }
   else
-    m4_warn (context, 0, M4ARG (0), _("invalid macro name ignored"));
+    m4_warn (context, 0, me, _("invalid macro name ignored"));
 }
 
 M4BUILTIN_HANDLER (popdef)
 {
-  const char *me = M4ARG (0);
   size_t i;
   for (i = 1; i < argc; i++)
-    if (m4_symbol_value_lookup (context, me, argv, i, true))
+    if (m4_symbol_value_lookup (context, argv, i, true))
       m4_symbol_popdef (M4SYMTAB, M4ARG (i));
 }
 
@@ -205,13 +207,12 @@ M4BUILTIN_HANDLER (popdef)
 M4BUILTIN_HANDLER (ifdef)
 {
   m4_push_arg (context, obs, argv,
-              (m4_symbol_value_lookup (context, M4ARG (0), argv, 1, false)
-               ? 2 : 3));
+              m4_symbol_value_lookup (context, argv, 1, false) ? 2 : 3);
 }
 
 M4BUILTIN_HANDLER (ifelse)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   size_t i;
 
   /* The valid ranges of argc for ifelse is discontinuous, we cannot
@@ -290,7 +291,6 @@ void
 m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, size_t argc,
                 m4_macro_args *argv, bool complain)
 {
-  const char *me = M4ARG (0);
   assert (obstack_object_size (data->obs) == 0);
   data->size = obstack_room (data->obs) / sizeof (const char *);
 
@@ -303,7 +303,7 @@ m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, 
size_t argc,
 
       for (i = 1; i < argc; i++)
        {
-         symbol = m4_symbol_value_lookup (context, me, argv, i, complain);
+         symbol = m4_symbol_value_lookup (context, argv, i, complain);
          if (symbol)
            dump_symbol_CB (NULL, M4ARG (i), symbol, data);
        }
@@ -353,12 +353,12 @@ M4BUILTIN_HANDLER (dumpdef)
    macro-definition token on the input stack.  */
 M4BUILTIN_HANDLER (defn)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   size_t i;
 
   for (i = 1; i < argc; i++)
     {
-      m4_symbol *symbol = m4_symbol_value_lookup (context, me, argv, i, true);
+      m4_symbol *symbol = m4_symbol_value_lookup (context, argv, i, true);
 
       if (!symbol)
        ;
@@ -368,9 +368,9 @@ M4BUILTIN_HANDLER (defn)
       else if (m4_is_symbol_func (symbol))
        m4_push_builtin (context, obs, m4_get_symbol_value (symbol));
       else if (m4_is_symbol_placeholder (symbol))
-       m4_warn (context, 0, M4ARG (i),
-                _("builtin `%s' requested by frozen file not found"),
-                m4_get_symbol_placeholder (symbol));
+       m4_warn (context, 0, me,
+                _("%s: builtin `%s' requested by frozen file not found"),
+                M4ARG (i), m4_get_symbol_placeholder (symbol));
       else
        {
          assert (!"Bad token data type in m4_defn");
@@ -480,7 +480,7 @@ M4BUILTIN_HANDLER (syscmd)
 {
   if (m4_get_safer_opt (context))
     {
-      m4_error (context, 0, 0, M4ARG (0), _("disabled by --safer"));
+      m4_error (context, 0, 0, m4_arg_info (argv), _("disabled by --safer"));
       return;
     }
 
@@ -510,7 +510,7 @@ M4BUILTIN_HANDLER (incr)
 {
   int value;
 
-  if (!m4_numeric_arg (context, M4ARG (0), M4ARG (1), &value))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &value))
     return;
 
   m4_shipout_int (obs, value + 1);
@@ -520,7 +520,7 @@ M4BUILTIN_HANDLER (decr)
 {
   int value;
 
-  if (!m4_numeric_arg (context, M4ARG (0), M4ARG (1), &value))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &value))
     return;
 
   m4_shipout_int (obs, value - 1);
@@ -536,7 +536,8 @@ M4BUILTIN_HANDLER (divert)
 {
   int i = 0;
 
-  if (argc >= 2 && !m4_numeric_arg (context, M4ARG (0), M4ARG (1), &i))
+  if (argc >= 2 && !m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1),
+                                   &i))
     return;
   m4_make_diversion (context, i);
   m4_divert_text (context, NULL, M4ARG (2), M4ARGLEN (2),
@@ -557,7 +558,7 @@ M4BUILTIN_HANDLER (divnum)
 M4BUILTIN_HANDLER (undivert)
 {
   size_t i = 0;
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
 
   if (argc == 1)
     m4_undivert_all (context);
@@ -596,7 +597,7 @@ M4BUILTIN_HANDLER (undivert)
    lives in input.c.  */
 M4BUILTIN_HANDLER (dnl)
 {
-  m4_skip_line (context, M4ARG (0));
+  m4_skip_line (context, m4_arg_info (argv));
 }
 
 /* Shift all arguments one to the left, discarding the first argument.
@@ -641,7 +642,7 @@ include (m4 *context, int argc, m4_macro_args *argv, bool 
silent)
   if (fp == NULL)
     {
       if (!silent)
-       m4_error (context, 0, errno, M4ARG (0), _("cannot open `%s'"),
+       m4_error (context, 0, errno, m4_arg_info (argv), _("cannot open `%s'"),
                  M4ARG (1));
       return;
     }
@@ -667,10 +668,10 @@ M4BUILTIN_HANDLER (sinclude)
 
 /* Add trailing `X' to PATTERN of length LEN as necessary, then
    securely create the temporary file system object.  If DIR, create a
-   directory instead of a file.  Report errors on behalf of MACRO.  If
+   directory instead of a file.  Report errors on behalf of CALLER.  If
    successful, output the quoted resulting name on OBS.  */
 void
-m4_make_temp (m4 *context, m4_obstack *obs, const char *macro,
+m4_make_temp (m4 *context, m4_obstack *obs, const m4_call_info *caller,
              const char *pattern, size_t len, bool dir)
 {
   int fd;
@@ -680,7 +681,7 @@ m4_make_temp (m4 *context, m4_obstack *obs, const char 
*macro,
 
   if (m4_get_safer_opt (context))
     {
-      m4_error (context, 0, 0, macro, _("disabled by --safer"));
+      m4_error (context, 0, 0, caller, _("disabled by --safer"));
       return;
     }
 
@@ -704,7 +705,7 @@ m4_make_temp (m4 *context, m4_obstack *obs, const char 
*macro,
       /* This use of _() will need to change if xgettext ever changes
         its undocumented behavior of parsing both string options.  */
 
-      m4_error (context, 0, errno, macro,
+      m4_error (context, 0, errno, caller,
                _(dir ? "cannot create directory from template `%s'"
                  : "cannot create file from template `%s'"),
                pattern);
@@ -723,7 +724,8 @@ m4_make_temp (m4 *context, m4_obstack *obs, const char 
*macro,
 /* Use the first argument as at template for a temporary file name.  */
 M4BUILTIN_HANDLER (maketemp)
 {
-  m4_warn (context, 0, M4ARG (0), _("recommend using mkstemp instead"));
+  const m4_call_info *me = m4_arg_info (argv);
+  m4_warn (context, 0, me, _("recommend using mkstemp instead"));
   if (m4_get_posixly_correct_opt (context))
     {
       /* POSIX states "any trailing 'X' characters [are] replaced with
@@ -757,13 +759,14 @@ M4BUILTIN_HANDLER (maketemp)
        }
     }
   else
-    m4_make_temp (context, obs, M4ARG (0), M4ARG (1), M4ARGLEN (1), false);
+    m4_make_temp (context, obs, me, M4ARG (1), M4ARGLEN (1), false);
 }
 
 /* Use the first argument as a template for a temporary file name.  */
 M4BUILTIN_HANDLER (mkstemp)
 {
-  m4_make_temp (context, obs, M4ARG (0), M4ARG (1), M4ARGLEN (1), false);
+  m4_make_temp (context, obs, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+               false);
 }
 
 /* Print all arguments on standard error.  */
@@ -792,7 +795,7 @@ M4BUILTIN_HANDLER (errprint)
    arguments are present.  */
 M4BUILTIN_HANDLER (m4exit)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   int exit_code = EXIT_SUCCESS;
 
   /* Warn on bad arguments, but still exit.  */
@@ -836,7 +839,7 @@ M4BUILTIN_HANDLER (m4wrap)
 
 M4BUILTIN_HANDLER (traceon)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   size_t i;
 
   if (argc == 1)
@@ -853,7 +856,7 @@ M4BUILTIN_HANDLER (traceon)
 /* Disable tracing of all specified macros, or all, if none is specified.  */
 M4BUILTIN_HANDLER (traceoff)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   size_t i;
 
   if (argc == 1)
@@ -902,7 +905,7 @@ M4BUILTIN_HANDLER (index)
    substring extends to the end of the first argument.  */
 M4BUILTIN_HANDLER (substr)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   const char *str = M4ARG (1);
   int start = 0;
   int length;
diff --git a/modules/m4.h b/modules/m4.h
index e1c3f96..eeefb18 100644
--- a/modules/m4.h
+++ b/modules/m4.h
@@ -43,7 +43,7 @@ typedef void m4_dump_symbols_func (m4 *context, 
m4_dump_symbol_data *data,
                                   bool complain);
 typedef const char *m4_expand_ranges_func (const char *s, m4_obstack *obs);
 typedef void m4_make_temp_func (m4 *context, m4_obstack *obs,
-                               const char *macro, const char *name,
+                               const m4_call_info *macro, const char *name,
                                size_t len, bool dir);
 
 END_C_DECLS
diff --git a/modules/stdlib.c b/modules/stdlib.c
index c24fa2b..0fd492f 100644
--- a/modules/stdlib.c
+++ b/modules/stdlib.c
@@ -108,7 +108,7 @@ M4BUILTIN_HANDLER (setenv)
   int overwrite = 1;
 
   if (argc >= 4)
-    if (!m4_numeric_arg (context, M4ARG (0), M4ARG (3), &overwrite))
+    if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (3), &overwrite))
       return;
 
 #if HAVE_SETENV
@@ -206,7 +206,7 @@ M4BUILTIN_HANDLER (getpwuid)
   struct passwd *pw;
   int uid;
 
-  if (!m4_numeric_arg (context, M4ARG (0), M4ARG (1), &uid))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &uid))
     return;
 
   pw = getpwuid (uid);
@@ -261,7 +261,7 @@ M4BUILTIN_HANDLER (srand)
     seed = time (0L) * getpid ();
   else
     {
-      if (!m4_numeric_arg (context, M4ARG (0), M4ARG (1), &seed))
+      if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &seed))
        return;
     }
 
diff --git a/modules/time.c b/modules/time.c
index 4febc8c..62f3da6 100644
--- a/modules/time.c
+++ b/modules/time.c
@@ -104,7 +104,7 @@ M4BUILTIN_HANDLER (ctime)
 
   if (argc == 2)
     {
-      m4_numeric_arg (context, M4ARG (0), M4ARG (1), &i);
+      m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &i);
       t = i;
     }
   else
@@ -152,7 +152,7 @@ M4BUILTIN_HANDLER (gmtime)
   time_t t;
   int i;
 
-  if (!m4_numeric_arg (context, M4ARG (0), M4ARG (1), &i))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &i))
     return;
 
   t = i;
@@ -167,7 +167,7 @@ M4BUILTIN_HANDLER (localtime)
   time_t t;
   int i;
 
-  if (!m4_numeric_arg (context, M4ARG (0), M4ARG (1), &i))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &i))
     return;
 
   t = i;
@@ -180,7 +180,7 @@ M4BUILTIN_HANDLER (localtime)
  **/
 M4BUILTIN_HANDLER (mktime)
 {
-  const char *me = M4ARG (0);
+  const m4_call_info *me = m4_arg_info (argv);
   struct tm tm;
   time_t t;
 
@@ -216,7 +216,7 @@ M4BUILTIN_HANDLER (strftime)
   char *buf;
   int l;
 
-  if (!m4_numeric_arg (context, M4ARG (0), M4ARG (2), &l))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (2), &l))
     return;
 
   t = l;
diff --git a/src/main.c b/src/main.c
index 104c256..e59183e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -748,9 +748,11 @@ main (int argc, char *const *argv, char *const *envp)
        case SYNCOUTPUT_OPTION:
          {
            bool previous = m4_get_syncoutput_opt (context);
+           m4_call_info info = {0};
+           info.name = "--syncoutput";
+           info.name_len = strlen (info.name);
            m4_set_syncoutput_opt (context,
-                                  m4_parse_truth_arg (context, arg,
-                                                      "--syncoutput",
+                                  m4_parse_truth_arg (context, &info, arg,
                                                       previous));
          }
          break;
diff --git a/tests/freeze.at b/tests/freeze.at
index 44de59d..a90e24e 100644
--- a/tests/freeze.at
+++ b/tests/freeze.at
@@ -506,7 +506,7 @@ AT_CHECK_M4([-R frozen.m4f input.m4], 0,
 
 a
 ]],
-[[m4:input.m4:4: Warning: a: builtin `b' requested by frozen file not found
+[[m4:input.m4:4: Warning: defn: a: builtin `b' requested by frozen file not 
found
 m4:input.m4:6: Warning: a: builtin `b' requested by frozen file not found
 m4:input.m4:8: Warning: a: builtin `b' requested by frozen file not found
 m4:input.m4:10: Warning: builtin: undefined builtin `b'
diff --git a/tests/options.at b/tests/options.at
index 5484f14..66c4ead 100644
--- a/tests/options.at
+++ b/tests/options.at
@@ -248,6 +248,20 @@ AT_CHECK_M4([in -Q], [0], [[__line__
 
 AT_CLEANUP
 
+## -------------- ##
+## unknown option ##
+## -------------- ##
+
+AT_SETUP([unknown option])
+
+AT_CHECK_M4([--unknown], [1], [], [stderr])
+AT_CHECK([sed 's/Try.*help/Try `m4 --help/' stderr], [0],
+[[m4: unrecognized option `--unknown'
+Try `m4 --help' for more information.
+]])
+
+AT_CLEANUP
+
 
 ## --------- ##
 ## debugfile ##
@@ -788,6 +802,16 @@ hi
 hi
 ]])
 
+dnl test parse error
+AT_CHECK_M4([--syncoutput=huh in], [0],
+[[hi
+]], [[m4: Warning: --syncoutput: unknown directive `huh'
+]])
+
+AT_CLEANUP
+
+AT_SETUP([--syncoutput and diversions])
+
 dnl synclines should always start at the beginning of a line.
 dnl this does not (yet) happen reliably when diversions do not
 dnl always end in newline
-- 
1.5.5.1


From a3aa897fc496179ff7bdd0fe6cd42a9e298e8473 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 28 May 2008 06:41:53 -0600
Subject: [PATCH] Stage 24b: Allow embedded NUL in macro names.

* m4/m4module.h (m4_symtab_apply_func, m4_symbol_lookup)
(m4_symbol_pushdef, m4_symbol_define, m4_symbol_popdef)
(m4_symbol_rename, m4_symbol_delete): Add length parameter.
(m4_string): New type.
* ltdl/m4/gnulib-cache.m4: Import xmemdup0 module.
* m4/hash.c (m4_hash_string_hash, m4_hash_string_cmp): Account for
length.
* m4/symtab.c (m4_symtab_delete, m4_symtab_apply, symtab_fetch)
(m4__symtab_remove_module_references, symbol_destroy_CB)
(m4_symbol_lookup, m4_symbol_pushdef, m4_symbol_define)
(m4_symbol_popdef, m4_symbol_rename, m4_set_symbol_name_traced)
(m4_symbol_delete): Likewise.
* modules/m4.h (struct m4_dump_symbol_data): Adjust type to allow
passing length.
* m4/macro.c (expand_token): Adjust all callers.
* m4/module.c (install_builtin_table, install_macro_table):
Likewise.
* m4/utility.c (m4_symbol_value_lookup): Likewise.
* modules/gnu.c (indir, renamesyms, m4symbols): Likewise.
* modules/m4.c (define, undefine, pushdef, popdef)
(dumpdef_cmp_CB, dump_symbol_CB, m4_dump_symbols, dumpdef)
(traceon, traceoff): Likewise.
* src/main.c (main): Likewise.
* src/freeze.c (dump_symbol_CB, reload_frozen_state): Likewise.
* tests/freeze.at (reloading nul): Augment test.
* tests/null.out: Adjust expected output.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog               |   32 ++++++++
 ltdl/m4/gnulib-cache.m4 |    4 +-
 m4/hash.c               |   30 +++++---
 m4/m4module.h           |   38 ++++++----
 m4/macro.c              |    2 +-
 m4/module.c             |    4 +-
 m4/symtab.c             |  186 ++++++++++++++++++++++++++++++++---------------
 m4/utility.c            |    2 +-
 modules/gnu.c           |   18 +++--
 modules/m4.c            |   72 ++++++++++++-------
 modules/m4.h            |    2 +-
 src/freeze.c            |   26 +++---
 src/main.c              |   36 ++++-----
 tests/freeze.at         |    5 +-
 tests/null.out          |    2 +-
 15 files changed, 296 insertions(+), 163 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0545f86..e9c8607 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,37 @@
 2008-06-02  Eric Blake  <address@hidden>
 
+       Stage 24b: Allow embedded NUL in macro names.
+       Use length rather than NUL-termination when tracking macro names.
+       All APIs dealing with symbol table changed.
+       Memory impact: none.
+       Speed impact: slight penalty, due to more bookkeeping.
+       * m4/m4module.h (m4_symtab_apply_func, m4_symbol_lookup)
+       (m4_symbol_pushdef, m4_symbol_define, m4_symbol_popdef)
+       (m4_symbol_rename, m4_symbol_delete): Add length parameter.
+       (m4_string): New type.
+       * ltdl/m4/gnulib-cache.m4: Import xmemdup0 module.
+       * m4/hash.c (m4_hash_string_hash, m4_hash_string_cmp): Account for
+       length.
+       * m4/symtab.c (m4_symtab_delete, m4_symtab_apply, symtab_fetch)
+       (m4__symtab_remove_module_references, symbol_destroy_CB)
+       (m4_symbol_lookup, m4_symbol_pushdef, m4_symbol_define)
+       (m4_symbol_popdef, m4_symbol_rename, m4_set_symbol_name_traced)
+       (m4_symbol_delete): Likewise.
+       * modules/m4.h (struct m4_dump_symbol_data): Adjust type to allow
+       passing length.
+       * m4/macro.c (expand_token): Adjust all callers.
+       * m4/module.c (install_builtin_table, install_macro_table):
+       Likewise.
+       * m4/utility.c (m4_symbol_value_lookup): Likewise.
+       * modules/gnu.c (indir, renamesyms, m4symbols): Likewise.
+       * modules/m4.c (define, undefine, pushdef, popdef)
+       (dumpdef_cmp_CB, dump_symbol_CB, m4_dump_symbols, dumpdef)
+       (traceon, traceoff): Likewise.
+       * src/main.c (main): Likewise.
+       * src/freeze.c (dump_symbol_CB, reload_frozen_state): Likewise.
+       * tests/freeze.at (reloading nul): Augment test.
+       * tests/null.out: Adjust expected output.
+
        Stage 24a: Use full call context in error reporting.
        Replace const char *macro_name with const m4_call_info *call, so
        that the next patch can pass the length of macro_name with
diff --git a/ltdl/m4/gnulib-cache.m4 b/ltdl/m4/gnulib-cache.m4
index 7350e21..f5f442c 100644
--- a/ltdl/m4/gnulib-cache.m4
+++ b/ltdl/m4/gnulib-cache.m4
@@ -15,11 +15,11 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --local-dir=local --lib=libgnu 
--source-base=gnu --m4-base=ltdl/m4 --doc-base=doc --aux-dir=build-aux 
--with-tests --libtool --macro-prefix=M4 assert autobuild avltree-oset 
binary-io clean-temp cloexec close-stream closein config-h configmake dirname 
error exit fdl fflush filenamecat flexmember fopen-safer fseeko gendocs gettext 
git-version-gen gnumakefile gnupload gpl-3.0 intprops memmem mkstemp obstack 
progname propername quote regex regexprops-generic sprintf-posix stdbool 
stdlib-safer strnlen strtod strtol tempname unlocked-io vasnprintf-posix verror 
xalloc xalloc-die xprintf-posix xstrndup xvasprintf-posix
+#   gnulib-tool --import --dir=. --local-dir=local --lib=libgnu 
--source-base=gnu --m4-base=ltdl/m4 --doc-base=doc --aux-dir=build-aux 
--with-tests --libtool --macro-prefix=M4 assert autobuild avltree-oset 
binary-io clean-temp cloexec close-stream closein config-h configmake dirname 
error exit fdl fflush filenamecat flexmember fopen-safer fseeko gendocs gettext 
git-version-gen gnumakefile gnupload gpl-3.0 intprops memmem mkstemp obstack 
progname propername quote regex regexprops-generic sprintf-posix stdbool 
stdlib-safer strnlen strtod strtol tempname unlocked-io vasnprintf-posix verror 
xalloc xalloc-die xmemdup0 xprintf-posix xstrndup xvasprintf-posix
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([local])
-gl_MODULES([assert autobuild avltree-oset binary-io clean-temp cloexec 
close-stream closein config-h configmake dirname error exit fdl fflush 
filenamecat flexmember fopen-safer fseeko gendocs gettext git-version-gen 
gnumakefile gnupload gpl-3.0 intprops memmem mkstemp obstack progname 
propername quote regex regexprops-generic sprintf-posix stdbool stdlib-safer 
strnlen strtod strtol tempname unlocked-io vasnprintf-posix verror xalloc 
xalloc-die xprintf-posix xstrndup xvasprintf-posix])
+gl_MODULES([assert autobuild avltree-oset binary-io clean-temp cloexec 
close-stream closein config-h configmake dirname error exit fdl fflush 
filenamecat flexmember fopen-safer fseeko gendocs gettext git-version-gen 
gnumakefile gnupload gpl-3.0 intprops memmem mkstemp obstack progname 
propername quote regex regexprops-generic sprintf-posix stdbool stdlib-safer 
strnlen strtod strtol tempname unlocked-io vasnprintf-posix verror xalloc 
xalloc-die xmemdup0 xprintf-posix xstrndup xvasprintf-posix])
 gl_AVOID([])
 gl_SOURCE_BASE([gnu])
 gl_M4_BASE([ltdl/m4])
diff --git a/m4/hash.c b/m4/hash.c
index ec3238f..c07b62d 100644
--- a/m4/hash.c
+++ b/m4/hash.c
@@ -635,20 +635,22 @@ m4_hash_apply (m4_hash *hash, m4_hash_apply_func *func, 
void *userdata)
 }
 
 
-/* Using a string as the hash key is common enough that we provide
-   implementations here for use in client hash table routines.  */
+/* Using a string (char * and size_t pair) as the hash key is common
+   enough that we provide implementations here for use in client hash
+   table routines.  */
 
-/* Return a hash value for a string, consistent with gnulib's hash
-   module.  */
+/* Return a hash value for a string, similar to gnulib's hash module,
+   but with length factored in.  */
 size_t
-m4_hash_string_hash (const void *key)
+m4_hash_string_hash (const void *ptr)
 {
-  size_t val = 0;
-  const char *ptr = (const char *) key;
-  char ch;
+  const m4_string *key = (const m4_string *) ptr;
+  const char *s = key->str;
+  size_t len = key->len;
+  size_t val = len;
 
-  while ((ch = *ptr++) != '\0')
-    val = (val << 7) + (val >> (sizeof (val) * CHAR_BIT - 7)) + ch;
+  while (len--)
+    val = (val << 7) + (val >> (sizeof val * CHAR_BIT - 7)) + to_uchar (*s++);
   return val;
 }
 
@@ -657,5 +659,11 @@ m4_hash_string_hash (const void *key)
 int
 m4_hash_string_cmp (const void *key, const void *try)
 {
-  return (strcmp ((const char *) key, (const char *) try));
+  const m4_string *a = (const m4_string *) key;
+  const m4_string *b = (const m4_string *) try;
+  if (a->len < b->len)
+    return -1;
+  if (b->len < a->len)
+    return 1;
+  return memcmp (a->str, b->str, a->len);
 }
diff --git a/m4/m4module.h b/m4/m4module.h
index fb404ea..1bd90c7 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -37,6 +37,7 @@ typedef struct m4_macro               m4_macro;
 typedef struct m4_macro_args   m4_macro_args;
 typedef struct m4_module       m4_module;
 typedef struct obstack         m4_obstack;
+typedef struct m4_string       m4_string;
 typedef struct m4_string_pair  m4_string_pair;
 typedef struct m4_symbol       m4_symbol;
 typedef struct m4_symbol_value m4_symbol_value;
@@ -80,6 +81,13 @@ struct m4_macro
   size_t      max_args;
 };
 
+/* Describe a single string, such as a macro name.  */
+struct m4_string
+{
+  char *str;           /* Array of characters, possibly including NUL.  */
+  size_t len;          /* Length of string.  */
+};
+
 /* Describe a pair of strings, such as begin and end quotes.  */
 struct m4_string_pair
 {
@@ -251,7 +259,7 @@ extern void     m4__module_exit    (m4 *);
 /* --- SYMBOL TABLE MANAGEMENT --- */
 
 
-typedef void *m4_symtab_apply_func (m4_symbol_table *, const char *,
+typedef void *m4_symtab_apply_func (m4_symbol_table *, const char *, size_t,
                                    m4_symbol *, void *);
 
 extern m4_symbol_table *m4_symtab_create  (size_t);
@@ -259,25 +267,27 @@ extern void         m4_symtab_delete  (m4_symbol_table*);
 extern void *    m4_symtab_apply   (m4_symbol_table*, bool,
                                     m4_symtab_apply_func*, void*);
 
-extern m4_symbol *m4_symbol_lookup  (m4_symbol_table*, const char *);
-extern m4_symbol *m4_symbol_pushdef (m4_symbol_table*,
-                                    const char *, m4_symbol_value *);
-extern m4_symbol *m4_symbol_define  (m4_symbol_table*,
-                                    const char *, m4_symbol_value *);
-extern void       m4_symbol_popdef  (m4_symbol_table*, const char *);
-extern m4_symbol *m4_symbol_rename  (m4_symbol_table*, const char *,
-                                    const char *);
+extern m4_symbol *m4_symbol_lookup  (m4_symbol_table*, const char *, size_t);
+extern m4_symbol *m4_symbol_pushdef (m4_symbol_table*, const char *, size_t,
+                                    m4_symbol_value *);
+extern m4_symbol *m4_symbol_define  (m4_symbol_table*, const char *, size_t,
+                                    m4_symbol_value *);
+extern void       m4_symbol_popdef  (m4_symbol_table*, const char *, size_t);
+extern m4_symbol *m4_symbol_rename  (m4_symbol_table*, const char *, size_t,
+                                    const char *, size_t);
 
-extern void       m4_symbol_delete  (m4_symbol_table*, const char *);
+extern void       m4_symbol_delete  (m4_symbol_table*, const char *, size_t);
 
-#define m4_symbol_delete(symtab, name)                 M4_STMT_START { \
-       while (m4_symbol_lookup ((symtab), (name)))                     \
-           m4_symbol_popdef ((symtab), (name));        } M4_STMT_END
+#define m4_symbol_delete(symtab, name, len)            M4_STMT_START   \
+  {                                                                    \
+    while (m4_symbol_lookup (symtab, name, len))                       \
+      m4_symbol_popdef (symtab, name, len);                            \
+  } M4_STMT_END
 
 extern m4_symbol_value *m4_get_symbol_value      (m4_symbol*);
 extern bool            m4_get_symbol_traced      (m4_symbol*);
 extern bool            m4_set_symbol_name_traced (m4_symbol_table*,
-                                                  const char *, bool);
+                                                  const char *, size_t, bool);
 extern void    m4_symbol_print         (m4 *, m4_symbol *, m4_obstack *,
                                         const m4_string_pair *, bool, size_t,
                                         bool);
diff --git a/m4/macro.c b/m4/macro.c
index 43c3ad8..f28b3f6 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -252,7 +252,7 @@ expand_token (m4 *context, m4_obstack *obs, m4__token_type 
type,
            len2--;
          }
 
-       symbol = m4_symbol_lookup (M4SYMTAB, textp);
+       symbol = m4_symbol_lookup (M4SYMTAB, textp, len2);
        assert (!symbol || !m4_is_symbol_void (symbol));
        if (symbol == NULL
            || (symbol->value->type == M4_SYMBOL_FUNC
diff --git a/m4/module.c b/m4/module.c
index 2bda5f6..58dad59 100644
--- a/m4/module.c
+++ b/m4/module.c
@@ -148,7 +148,7 @@ install_builtin_table (m4 *context, m4_module *module)
       if (m4_get_prefix_builtins_opt (context))
        name = xasprintf ("m4_%s", name);
 
-      m4_symbol_pushdef (M4SYMTAB, name, value);
+      m4_symbol_pushdef (M4SYMTAB, name, strlen (name), value);
 
       if (m4_get_prefix_builtins_opt (context))
        free ((char *) name);
@@ -185,7 +185,7 @@ install_macro_table (m4 *context, m4_module *module)
          VALUE_MIN_ARGS (value) = mp->min_args;
          VALUE_MAX_ARGS (value) = mp->max_args;
 
-         m4_symbol_pushdef (M4SYMTAB, mp->name, value);
+         m4_symbol_pushdef (M4SYMTAB, mp->name, strlen (mp->name), value);
        }
 
       m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
diff --git a/m4/symtab.c b/m4/symtab.c
index 76ff1cb..025fbcd 100644
--- a/m4/symtab.c
+++ b/m4/symtab.c
@@ -21,6 +21,7 @@
 #include <config.h>
 
 #include "m4private.h"
+#include "xmemdup0.h"
 
 /* Define this to see runtime debug info.  Implied by DEBUG.  */
 /*#define DEBUG_SYM */
@@ -51,15 +52,15 @@ struct m4_symbol_table {
   m4_hash *table;
 };
 
-static m4_symbol *symtab_fetch         (m4_symbol_table*, const char *);
-static void      symbol_popval         (m4_symbol *symbol);
-static void *    symbol_destroy_CB     (m4_symbol_table *symtab,
-                                        const char *name,
-                                        m4_symbol *symbol, void *ignored);
-static void *    arg_destroy_CB        (m4_hash *hash, const void *name,
-                                        void *arg, void *ignored);
-static void *    arg_copy_CB           (m4_hash *src, const void *name,
-                                        void *arg, m4_hash *dest);
+static m4_symbol *symtab_fetch         (m4_symbol_table*, const char *,
+                                        size_t);
+static void      symbol_popval         (m4_symbol *);
+static void *    symbol_destroy_CB     (m4_symbol_table *, const char *,
+                                        size_t, m4_symbol *, void *);
+static void *    arg_destroy_CB        (m4_hash *, const void *, void *,
+                                        void *);
+static void *    arg_copy_CB           (m4_hash *, const void *, void *,
+                                        m4_hash *);
 
 
 /* -- SYMBOL TABLE MANAGEMENT --
@@ -108,9 +109,11 @@ m4_symtab_apply (m4_symbol_table *symtab, bool 
include_trace,
     {
       m4_symbol *symbol = m4_get_hash_iterator_value (place);
       if (symbol->value || include_trace)
-       result = func (symtab, (const char *) m4_get_hash_iterator_key (place),
-                      symbol, userdata);
-
+       {
+         const m4_string *key
+           = (const m4_string *) m4_get_hash_iterator_key (place);
+         result = func (symtab, key->str, key->len, symbol, userdata);
+       }
       if (result != NULL)
        {
          m4_free_hash_iterator (symtab->table, place);
@@ -121,25 +124,36 @@ m4_symtab_apply (m4_symbol_table *symtab, bool 
include_trace,
   return result;
 }
 
-/* Ensure that NAME exists in the table, creating an entry if needed.  */
+/* Ensure that NAME of length LEN exists in the table, creating an
+   entry if needed.  */
 static m4_symbol *
-symtab_fetch (m4_symbol_table *symtab, const char *name)
+symtab_fetch (m4_symbol_table *symtab, const char *name, size_t len)
 {
   m4_symbol **psymbol;
   m4_symbol *symbol;
+  m4_string key;
 
   assert (symtab);
   assert (name);
 
-  psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, name);
+  /* Safe to cast away const, since m4_hash_lookup doesn't modify
+     key.  */
+  key.str = (char *) name;
+  key.len = len;
+  psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, &key);
   if (psymbol)
     {
       symbol = *psymbol;
     }
   else
     {
+      /* Use xmemdup0 rather than memdup so that debugging the symbol
+        table is easier.  */
+      m4_string *new_key = (m4_string *) xmalloc (sizeof *new_key);
+      new_key->str = xmemdup0 (name, len);
+      new_key->len = len;
       symbol = (m4_symbol *) xzalloc (sizeof *symbol);
-      m4_hash_insert (symtab->table, xstrdup (name), symbol);
+      m4_hash_insert (symtab->table, new_key, symbol);
     }
 
   return symbol;
@@ -182,7 +196,11 @@ m4__symtab_remove_module_references (m4_symbol_table 
*symtab,
 
          /* Purge the live reference if necessary.  */
          if (SYMBOL_MODULE (symbol) == module)
-           m4_symbol_popdef (symtab, m4_get_hash_iterator_key (place));
+           {
+             const m4_string *key
+               = (const m4_string *) m4_get_hash_iterator_key (place);
+             m4_symbol_popdef (symtab, key->str, key->len);
+           }
        }
     }
 }
@@ -193,17 +211,19 @@ m4__symtab_remove_module_references (m4_symbol_table 
*symtab,
    on every symbol so that m4_symbol_popdef() doesn't try to preserve
    the table entry.  */
 static void *
-symbol_destroy_CB (m4_symbol_table *symtab, const char *name,
-                  m4_symbol *symbol, void *ignored)
+symbol_destroy_CB (m4_symbol_table *symtab, const char *name, size_t len,
+                  m4_symbol *symbol, void *ignored M4_GNUC_UNUSED)
 {
-  char *key = xstrdup ((char *) name);
+  m4_string key;
+  key.str = xmemdup0 (name, len);
+  key.len = len;
 
   symbol->traced = false;
 
-  while (key && m4_hash_lookup (symtab->table, key))
-    m4_symbol_popdef (symtab, key);
+  while (m4_hash_lookup (symtab->table, &key))
+    m4_symbol_popdef (symtab, key.str, key.len);
 
-  free (key);
+  free (key.str);
 
   return NULL;
 }
@@ -215,11 +235,19 @@ symbol_destroy_CB (m4_symbol_table *symtab, const char 
*name,
    The following functions manipulate individual symbols within
    an existing table.  */
 
-/* Return the symbol associated to NAME, or else NULL.  */
+/* Return the symbol associated to NAME of length LEN, or else
+   NULL.  */
 m4_symbol *
-m4_symbol_lookup (m4_symbol_table *symtab, const char *name)
+m4_symbol_lookup (m4_symbol_table *symtab, const char *name, size_t len)
 {
-  m4_symbol **psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, name);
+  m4_string key;
+  m4_symbol **psymbol;
+
+  /* Safe to cast away const, since m4_hash_lookup doesn't modify
+     key.  */
+  key.str = (char *) name;
+  key.len = len;
+  psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, &key);
 
   /* If just searching, return status of search -- if only an empty
      struct is returned, that is treated as a failed lookup.  */
@@ -227,11 +255,12 @@ m4_symbol_lookup (m4_symbol_table *symtab, const char 
*name)
 }
 
 
-/* Insert NAME into the symbol table.  If there is already a symbol
-   associated with NAME, push the new VALUE on top of the value stack
-   for this symbol.  Otherwise create a new association.  */
+/* Insert NAME of length LEN into the symbol table.  If there is
+   already a symbol associated with NAME, push the new VALUE on top of
+   the value stack for this symbol.  Otherwise create a new
+   association.  */
 m4_symbol *
-m4_symbol_pushdef (m4_symbol_table *symtab, const char *name,
+m4_symbol_pushdef (m4_symbol_table *symtab, const char *name, size_t len,
                   m4_symbol_value *value)
 {
   m4_symbol *symbol;
@@ -240,7 +269,7 @@ m4_symbol_pushdef (m4_symbol_table *symtab, const char 
*name,
   assert (name);
   assert (value);
 
-  symbol               = symtab_fetch (symtab, name);
+  symbol               = symtab_fetch (symtab, name, len);
   VALUE_NEXT (value)   = m4_get_symbol_value (symbol);
   symbol->value                = value;
 
@@ -249,11 +278,12 @@ m4_symbol_pushdef (m4_symbol_table *symtab, const char 
*name,
   return symbol;
 }
 
-/* Return the symbol associated with NAME in the symbol table, creating
-   a new symbol if necessary.  In either case set the symbol's VALUE.  */
+/* Return the symbol associated with NAME of length LEN in the symbol
+   table, creating a new symbol if necessary.  In either case set the
+   symbol's VALUE.  */
 m4_symbol *
-m4_symbol_define (m4_symbol_table *symtab,
-                 const char *name, m4_symbol_value *value)
+m4_symbol_define (m4_symbol_table *symtab, const char *name, size_t len,
+                 m4_symbol_value *value)
 {
   m4_symbol *symbol;
 
@@ -261,7 +291,7 @@ m4_symbol_define (m4_symbol_table *symtab,
   assert (name);
   assert (value);
 
-  symbol = symtab_fetch (symtab, name);
+  symbol = symtab_fetch (symtab, name, len);
   if (m4_get_symbol_value (symbol))
     symbol_popval (symbol);
 
@@ -274,12 +304,19 @@ m4_symbol_define (m4_symbol_table *symtab,
 }
 
 /* Pop the topmost value stack entry from the symbol associated with
-   NAME, deleting it from the table entirely if that was the last
-   remaining value in the stack.  */
+   NAME of length LEN, deleting it from the table entirely if that was
+   the last remaining value in the stack.  */
 void
-m4_symbol_popdef (m4_symbol_table *symtab, const char *name)
+m4_symbol_popdef (m4_symbol_table *symtab, const char *name, size_t len)
 {
-  m4_symbol **psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, name);
+  m4_string key;
+  m4_symbol **psymbol;
+
+  /* Safe to cast away const, since m4_hash_lookup doesn't modify
+     key.  */
+  key.str = (char *) name;
+  key.len = len;
+  psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, &key);
 
   assert (psymbol);
   assert (*psymbol);
@@ -290,8 +327,11 @@ m4_symbol_popdef (m4_symbol_table *symtab, const char 
*name)
      symbol value stack was successfully removed.  */
   if (!m4_get_symbol_value (*psymbol) && !m4_get_symbol_traced (*psymbol))
     {
+      m4_string *old_key;
       DELETE (*psymbol);
-      free (m4_hash_remove (symtab->table, name));
+      old_key = (m4_string *) m4_hash_remove (symtab->table, &key);
+      free (old_key->str);
+      free (old_key);
     }
 }
 
@@ -355,30 +395,41 @@ m4_symbol_value_delete (m4_symbol_value *value)
     }
 }
 
+/* Rename the entire stack of values associated with NAME and LEN1 to
+   NEWNAME and LEN2.  */
 m4_symbol *
-m4_symbol_rename (m4_symbol_table *symtab, const char *name,
-                 const char *newname)
+m4_symbol_rename (m4_symbol_table *symtab, const char *name, size_t len1,
+                 const char *newname, size_t len2)
 {
   m4_symbol *symbol    = NULL;
   m4_symbol **psymbol;
+  m4_string key;
+  m4_string *pkey;
 
   assert (symtab);
   assert (name);
   assert (newname);
 
+  /* Safe to cast away const, since m4_hash_lookup doesn't modify
+     key.  */
+  key.str = (char *) name;
+  key.len = len1;
   /* Use a low level hash fetch, so we can save the symbol value when
      removing the symbol name from the symbol table.  */
-  psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, name);
+  psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, &key);
 
   if (psymbol)
     {
       symbol = *psymbol;
 
       /* Remove the old name from the symbol table.  */
-      free (m4_hash_remove (symtab->table, name));
-      assert (!m4_hash_lookup (symtab->table, name));
+      pkey = (m4_string *) m4_hash_remove (symtab->table, &key);
+      assert (pkey && !m4_hash_lookup (symtab->table, &key));
+      free (pkey->str);
 
-      m4_hash_insert (symtab->table, xstrdup (newname), *psymbol);
+      pkey->str = xmemdup0 (newname, len2);
+      pkey->len = len2;
+      m4_hash_insert (symtab->table, pkey, *psymbol);
     }
   /* else
        NAME does not name a symbol in symtab->table!  */
@@ -521,14 +572,14 @@ arg_copy_CB (m4_hash *src, const void *name, void *arg, 
m4_hash *dest)
   return NULL;
 }
 
-/* Set the tracing status of the symbol NAME to TRACED.  This takes a
-   name, rather than a symbol, since we hide macros that are traced
-   but otherwise undefined from normal lookups, but still can affect
-   their tracing status.  Return true iff the macro was previously
-   traced.  */
+/* Set the tracing status of the symbol NAME of length LEN to TRACED.
+   This takes a name, rather than a symbol, since we hide macros that
+   are traced but otherwise undefined from normal lookups, but still
+   can affect their tracing status.  Return true iff the macro was
+   previously traced.  */
 bool
 m4_set_symbol_name_traced (m4_symbol_table *symtab, const char *name,
-                          bool traced)
+                          size_t len, bool traced)
 {
   m4_symbol *symbol;
   bool result;
@@ -537,11 +588,17 @@ m4_set_symbol_name_traced (m4_symbol_table *symtab, const 
char *name,
   assert (name);
 
   if (traced)
-    symbol = symtab_fetch (symtab, name);
+    symbol = symtab_fetch (symtab, name, len);
   else
     {
-      m4_symbol **psymbol = (m4_symbol **) m4_hash_lookup (symtab->table,
-                                                          name);
+      m4_string key;
+      m4_symbol **psymbol;
+
+      /* Safe to cast away const, since m4_hash_lookup doesn't modify
+        key.  */
+      key.str = (char *) name;
+      key.len = len;
+      psymbol = (m4_symbol **) m4_hash_lookup (symtab->table, &key);
       if (!psymbol)
        return false;
       symbol = *psymbol;
@@ -552,9 +609,18 @@ m4_set_symbol_name_traced (m4_symbol_table *symtab, const 
char *name,
   if (!traced && !m4_get_symbol_value (symbol))
     {
       /* Free an undefined entry once it is no longer traced.  */
+      m4_string key;
+      m4_string *old_key;
       assert (result);
       free (symbol);
-      free (m4_hash_remove (symtab->table, name));
+
+      /* Safe to cast away const, since m4_hash_lookup doesn't modify
+        key.  */
+      key.str = (char *) name;
+      key.len = len;
+      old_key = (m4_string *) m4_hash_remove (symtab->table, &key);
+      free (old_key->str);
+      free (old_key);
     }
 
   return result;
@@ -707,10 +773,10 @@ m4_symbol_print (m4 *context, m4_symbol *symbol, 
m4_obstack *obs,
 /* Pop all values from the symbol associated with NAME.  */
 #undef m4_symbol_delete
 void
-m4_symbol_delete (m4_symbol_table *symtab, const char *name)
+m4_symbol_delete (m4_symbol_table *symtab, const char *name, size_t len)
 {
-  while (m4_symbol_lookup (symtab, name))
-    m4_symbol_popdef (symtab, name);
+  while (m4_symbol_lookup (symtab, name, len))
+    m4_symbol_popdef (symtab, name, len);
 }
 
 #undef m4_get_symbol_traced
diff --git a/m4/utility.c b/m4/utility.c
index b15e17f..7c91191 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -132,7 +132,7 @@ m4_symbol_value_lookup (m4 *context, m4_macro_args *argv, 
size_t i,
   if (m4_is_arg_text (argv, i))
     {
       const char *name = M4ARG (i);
-      result = m4_symbol_lookup (M4SYMTAB, name);
+      result = m4_symbol_lookup (M4SYMTAB, name, M4ARGLEN (i));
       if (must_exist && !result)
        m4_warn (context, 0, argv->info, _("undefined macro `%s'"), name);
     }
diff --git a/modules/gnu.c b/modules/gnu.c
index 78c69b5..a13ae11 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -674,7 +674,8 @@ M4BUILTIN_HANDLER (indir)
   else
     {
       const char *name = M4ARG (1);
-      m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, name);
+      size_t len = M4ARGLEN (1);
+      m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, name, len);
 
       if (symbol == NULL)
        m4_warn (context, 0, me, _("undefined macro `%s'"), name);
@@ -682,7 +683,7 @@ M4BUILTIN_HANDLER (indir)
        {
          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),
+         new_argv = m4_make_argv_ref (context, argv, name, len,
                                       m4_symbol_flatten_args (symbol),
                                       m4_get_symbol_traced (symbol));
          m4_trace_prepare (context, m4_arg_info (new_argv), value);
@@ -880,14 +881,14 @@ M4BUILTIN_HANDLER (renamesyms)
 
       for (; data.size > 0; --data.size, data.base++)
        {
-         const char *name = data.base[0];
+         const m4_string *key = &data.base[0];
 
-         if (regexp_substitute (context, data.obs, me, name, strlen (name),
+         if (regexp_substitute (context, data.obs, me, key->str, key->len,
                                 regexp, buf, replace, true))
            {
-             obstack_1grow (data.obs, '\0');
-             m4_symbol_rename (M4SYMTAB, name,
-                               (char *) obstack_finish (data.obs));
+             size_t newlen = obstack_object_size (data.obs);
+             m4_symbol_rename (M4SYMTAB, key->str, key->len,
+                               (char *) obstack_finish (data.obs), newlen);
            }
        }
     }
@@ -915,7 +916,8 @@ M4BUILTIN_HANDLER (m4symbols)
 
       for (; data.size > 0; --data.size, data.base++)
        {
-         m4_shipout_string (context, obs, data.base[0], SIZE_MAX, true);
+         m4_shipout_string (context, obs, data.base->str, data.base->len,
+                            true);
          if (data.size > 1)
            obstack_1grow (obs, ',');
        }
diff --git a/modules/m4.c b/modules/m4.c
index bc93ee0..d9e789e 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -101,7 +101,7 @@ typedef uintmax_t unumber;
 static void    include         (m4 *context, int argc, m4_macro_args *argv,
                                 bool silent);
 static int     dumpdef_cmp_CB  (const void *s1, const void *s2);
-static void *  dump_symbol_CB  (m4_symbol_table *ignored, const char *name,
+static void *  dump_symbol_CB  (m4_symbol_table *, const char *, size_t,
                                 m4_symbol *symbol, void *userdata);
 static const char *ntoa                (number value, int radix);
 static void    numb_obstack    (m4_obstack *obs, number value,
@@ -161,7 +161,7 @@ M4BUILTIN_HANDLER (define)
 
       if (m4_symbol_value_copy (context, value, m4_arg_symbol (argv, 2)))
        m4_warn (context, 0, me, _("cannot concatenate builtins"));
-      m4_symbol_define (M4SYMTAB, M4ARG (1), value);
+      m4_symbol_define (M4SYMTAB, M4ARG (1), M4ARGLEN (1), value);
     }
   else
     m4_warn (context, 0, me, _("invalid macro name ignored"));
@@ -172,7 +172,7 @@ M4BUILTIN_HANDLER (undefine)
   size_t i;
   for (i = 1; i < argc; i++)
     if (m4_symbol_value_lookup (context, argv, i, true))
-      m4_symbol_delete (M4SYMTAB, M4ARG (i));
+      m4_symbol_delete (M4SYMTAB, M4ARG (i), M4ARGLEN (i));
 }
 
 M4BUILTIN_HANDLER (pushdef)
@@ -185,7 +185,7 @@ M4BUILTIN_HANDLER (pushdef)
 
       if (m4_symbol_value_copy (context, value, m4_arg_symbol (argv, 2)))
        m4_warn (context, 0, me, _("cannot concatenate builtins"));
-      m4_symbol_pushdef (M4SYMTAB, M4ARG (1), value);
+      m4_symbol_pushdef (M4SYMTAB, M4ARG (1), M4ARGLEN (1), value);
     }
   else
     m4_warn (context, 0, me, _("invalid macro name ignored"));
@@ -196,7 +196,7 @@ M4BUILTIN_HANDLER (popdef)
   size_t i;
   for (i = 1; i < argc; i++)
     if (m4_symbol_value_lookup (context, argv, i, true))
-      m4_symbol_popdef (M4SYMTAB, M4ARG (i));
+      m4_symbol_popdef (M4SYMTAB, M4ARG (i), M4ARGLEN (i));
 }
 
 
@@ -255,16 +255,22 @@ M4BUILTIN_HANDLER (ifelse)
 static int
 dumpdef_cmp_CB (const void *s1, const void *s2)
 {
-  return strcmp (*(const char **) s1, *(const char **) s2);
+  const m4_string *a = (const m4_string *) s1;
+  const m4_string *b = (const m4_string *) s2;
+  int result = memcmp (a->str, b->str, a->len < b->len ? a->len : b->len);
+  if (!result)
+    result = a->len < b->len ? -1 : b->len < a->len;
+  return result;
 }
 
 /* The function m4_dump_symbols () is for use by "dumpdef".  It builds up a
    table of all defined symbol names.  */
 static void *
-dump_symbol_CB (m4_symbol_table *ignored, const char *name, m4_symbol *symbol,
-               void *userdata)
+dump_symbol_CB (m4_symbol_table *ignored M4_GNUC_UNUSED, const char *name,
+               size_t len, m4_symbol *symbol, void *userdata)
 {
   m4_dump_symbol_data *symbol_data = (m4_dump_symbol_data *) userdata;
+  m4_string *key;
 
   assert (name);
   assert (symbol);
@@ -272,16 +278,23 @@ dump_symbol_CB (m4_symbol_table *ignored, const char 
*name, m4_symbol *symbol,
 
   if (symbol_data->size == 0)
     {
-      obstack_ptr_grow (symbol_data->obs, name);
+      size_t offset = obstack_object_size (symbol_data->obs);
+      obstack_blank (symbol_data->obs, sizeof *symbol_data->base);
       symbol_data->size = (obstack_room (symbol_data->obs)
-                          / sizeof (const char *));
+                          / sizeof *symbol_data->base);
+      symbol_data->base = (m4_string *) (obstack_base (symbol_data->obs)
+                                        + offset);
     }
   else
     {
-      obstack_ptr_grow_fast (symbol_data->obs, name);
+      obstack_blank_fast (symbol_data->obs, sizeof *symbol_data->base);
       symbol_data->size--;
     }
 
+  /* Safe to cast away const, since m4_dump_symbols adds it back.  */
+  key = (m4_string *) symbol_data->base++;
+  key->str = (char *) name;
+  key->len = len;
   return NULL;
 }
 
@@ -292,7 +305,8 @@ m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, 
size_t argc,
                 m4_macro_args *argv, bool complain)
 {
   assert (obstack_object_size (data->obs) == 0);
-  data->size = obstack_room (data->obs) / sizeof (const char *);
+  data->size = obstack_room (data->obs) / sizeof *data->base;
+  data->base = (m4_string *) obstack_base (data->obs);
 
   if (argc == 1)
     m4_symtab_apply (M4SYMTAB, false, dump_symbol_CB, data);
@@ -305,13 +319,15 @@ m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, 
size_t argc,
        {
          symbol = m4_symbol_value_lookup (context, argv, i, complain);
          if (symbol)
-           dump_symbol_CB (NULL, M4ARG (i), symbol, data);
+           dump_symbol_CB (NULL, M4ARG (i), M4ARGLEN (i), symbol, data);
        }
     }
 
-  data->size = obstack_object_size (data->obs) / sizeof (const char *);
-  data->base = (const char **) obstack_finish (data->obs);
-  qsort (data->base, data->size, sizeof (const char *), dumpdef_cmp_CB);
+  data->size = obstack_object_size (data->obs) / sizeof *data->base;
+  data->base = (m4_string *) obstack_finish (data->obs);
+  /* Safe to cast away const, since we don't modify entries.  */
+  qsort ((m4_string *) data->base, data->size, sizeof *data->base,
+        dumpdef_cmp_CB);
 }
 
 
@@ -329,23 +345,27 @@ M4BUILTIN_HANDLER (dumpdef)
     quotes = m4_get_syntax_quotes (M4SYNTAX);
   data.obs = m4_arg_scratch (context);
   m4_dump_symbols (context, &data, argc, argv, true);
+  m4_sysval_flush (context, false);
 
   for (; data.size > 0; --data.size, data.base++)
     {
-      m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, data.base[0]);
+      m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, data.base->str,
+                                           data.base->len);
+      char *value;
       assert (symbol);
 
-      obstack_grow (obs, data.base[0], strlen (data.base[0]));
-      obstack_1grow (obs, ':');
-      obstack_1grow (obs, '\t');
+      /* TODO - add debugmode(b) option to control quoting style.  */
+      fwrite (data.base->str, 1, data.base->len, stderr);
+      fputc (':', stderr);
+      fputc ('\t', stderr);
       m4_symbol_print (context, symbol, obs, quotes, stack, arg_length,
                       module);
       obstack_1grow (obs, '\n');
+      obstack_1grow (obs, '\0');
+      value = (char *) obstack_finish (obs);
+      fputs (value, stderr);
+      obstack_free (obs, value);
     }
-
-  obstack_1grow (obs, '\0');
-  m4_sysval_flush (context, false);
-  fputs ((char *) obstack_finish (obs), stderr);
 }
 
 /* The macro "defn" returns the quoted definition of the macro named by
@@ -848,7 +868,7 @@ M4BUILTIN_HANDLER (traceon)
   else
     for (i = 1; i < argc; i++)
       if (m4_is_arg_text (argv, i))
-       m4_set_symbol_name_traced (M4SYMTAB, M4ARG (i), true);
+       m4_set_symbol_name_traced (M4SYMTAB, M4ARG (i), M4ARGLEN (i), true);
       else
        m4_warn (context, 0, me, _("invalid macro name ignored"));
 }
@@ -865,7 +885,7 @@ M4BUILTIN_HANDLER (traceoff)
   else
     for (i = 1; i < argc; i++)
       if (m4_is_arg_text (argv, i))
-       m4_set_symbol_name_traced (M4SYMTAB, M4ARG (i), false);
+       m4_set_symbol_name_traced (M4SYMTAB, M4ARG (i), M4ARGLEN (i), false);
       else
        m4_warn (context, 0, me, _("invalid macro name ignored"));
 }
diff --git a/modules/m4.h b/modules/m4.h
index eeefb18..a82584e 100644
--- a/modules/m4.h
+++ b/modules/m4.h
@@ -29,7 +29,7 @@ BEGIN_C_DECLS
 typedef struct
 {
   m4_obstack *obs;             /* obstack for table */
-  const char **base;           /* base of table */
+  const m4_string *base;       /* base of table */
   int size;                    /* size of table */
 } m4_dump_symbol_data;
 
diff --git a/src/freeze.c b/src/freeze.c
index 40de809..0373459 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -34,7 +34,7 @@ static        void  produce_syntax_dump       (FILE *, 
m4_syntax_table *, char);
 static void  produce_module_dump       (FILE *, m4_module *);
 static void  produce_symbol_dump       (m4 *, FILE *, m4_symbol_table *);
 static void *dump_symbol_CB            (m4_symbol_table *, const char *,
-                                        m4_symbol *, void *);
+                                        size_t, m4_symbol *, void *);
 static void  issue_expect_message      (m4 *, int);
 static int   decode_char               (m4 *, FILE *, bool *);
 
@@ -189,14 +189,14 @@ reverse_symbol_value_stack (m4_symbol_value *value)
   return result;
 }
 
-/* Dump the stack of values for SYMBOL, with name SYMBOL_NAME, located
-   in SYMTAB.  USERDATA is interpreted as the FILE* to dump to.  */
+/* Dump the stack of values for SYMBOL, with name SYMBOL_NAME and
+   length LEN, located in SYMTAB.  USERDATA is interpreted as the
+   FILE* to dump to.  */
 static void *
-dump_symbol_CB (m4_symbol_table *symtab, const char *symbol_name,
+dump_symbol_CB (m4_symbol_table *symtab, const char *symbol_name, size_t len,
                m4_symbol *symbol, void *userdata)
 {
   FILE *file = (FILE *) userdata;
-  size_t symbol_len = strlen (symbol_name);
   m4_symbol_value *value;
   m4_symbol_value *last;
 
@@ -211,12 +211,12 @@ dump_symbol_CB (m4_symbol_table *symtab, const char 
*symbol_name,
        {
          const char *text = m4_get_symbol_value_text (value);
          size_t text_len = m4_get_symbol_value_len (value);
-         xfprintf (file, "T%zu,%zu", symbol_len, text_len);
+         xfprintf (file, "T%zu,%zu", len, text_len);
          if (module)
            xfprintf (file, ",%zu", module_len);
          fputc ('\n', file);
 
-         produce_mem_dump (file, symbol_name, symbol_len);
+         produce_mem_dump (file, symbol_name, len);
          fputc ('\n', file);
          produce_mem_dump (file, text, text_len);
          fputc ('\n', file);
@@ -234,12 +234,12 @@ dump_symbol_CB (m4_symbol_table *symtab, const char 
*symbol_name,
            assert (!"INTERNAL ERROR: builtin not found in builtin table!");
          bp_len = strlen (bp->name);
 
-         xfprintf (file, "F%zu,%zu", symbol_len, bp_len);
+         xfprintf (file, "F%zu,%zu", len, bp_len);
          if (module)
            xfprintf (file, ",%zu", module_len);
          fputc ('\n', file);
 
-         produce_mem_dump (file, symbol_name, symbol_len);
+         produce_mem_dump (file, symbol_name, len);
          fputc ('\n', file);
          produce_mem_dump (file, bp->name, bp_len);
          fputc ('\n', file);
@@ -257,7 +257,7 @@ dump_symbol_CB (m4_symbol_table *symtab, const char 
*symbol_name,
     }
   reverse_symbol_value_stack (last);
   if (m4_get_symbol_traced (symbol))
-    xfprintf (file, "t%zu\n%s\n", symbol_len, symbol_name);
+    xfprintf (file, "t%zu\n%s\n", len, symbol_name);
   return NULL;
 }
 
@@ -698,7 +698,7 @@ ill-formed frozen file, version 2 directive `%c' 
encountered"), 'F');
                VALUE_MIN_ARGS (token) = 0;
                VALUE_MAX_ARGS (token) = -1;
              }
-           m4_symbol_pushdef (M4SYMTAB, string[0], token);
+           m4_symbol_pushdef (M4SYMTAB, string[0], number[0], token);
          }
          break;
 
@@ -794,7 +794,7 @@ ill-formed frozen file, version 2 directive `%c' 
encountered"), 't');
          GET_STRING (file, string[0], allocated[0], number[0], false);
          VALIDATE ('\n');
 
-         m4_set_symbol_name_traced (M4SYMTAB, string[0], true);
+         m4_set_symbol_name_traced (M4SYMTAB, string[0], number[0], true);
 
          break;
 
@@ -933,7 +933,7 @@ ill-formed frozen file, version 2 directive `%c' 
encountered"), 'T');
            VALUE_MODULE (token) = module;
            VALUE_MAX_ARGS (token) = -1;
 
-           m4_symbol_pushdef (M4SYMTAB, string[0], token);
+           m4_symbol_pushdef (M4SYMTAB, string[0], number[0], token);
          }
          break;
 
diff --git a/src/main.c b/src/main.c
index e59183e..7346d09 100644
--- a/src/main.c
+++ b/src/main.c
@@ -681,30 +681,21 @@ main (int argc, char *const *argv, char *const *envp)
          {
            m4_symbol_value *value = m4_symbol_value_create ();
 
-           /* defn->value is read-only, so we need a copy.  */
-           char *macro_name = xstrdup (arg);
-           char *macro_value = strchr (macro_name, '=');
-           size_t len = 0;
-
-           if (macro_value != NULL)
-             {
-               *macro_value++ = '\0';
-               len = strlen (macro_value);
-             }
-           m4_set_symbol_value_text (value, xstrdup (macro_value
-                                                     ? macro_value : ""),
-                                     len, 0);
+           const char *str = strchr (arg, '=');
+           size_t len = str ? str - arg : strlen (arg);
+
+           m4_set_symbol_value_text (value, xstrdup (str ? str + 1 : ""),
+                                     str ? strlen (str + 1) : 0, 0);
 
            if (defn->code == 'D')
-             m4_symbol_define (M4SYMTAB, macro_name, value);
+             m4_symbol_define (M4SYMTAB, arg, len, value);
            else
-             m4_symbol_pushdef (M4SYMTAB, macro_name, value);
-           free (macro_name);
+             m4_symbol_pushdef (M4SYMTAB, arg, len, value);
          }
          break;
 
        case 'U':
-         m4_symbol_delete (M4SYMTAB, arg);
+         m4_symbol_delete (M4SYMTAB, arg, strlen (arg));
          break;
 
        case 'd':
@@ -733,7 +724,7 @@ main (int argc, char *const *argv, char *const *envp)
          break;
 
        case 't':
-         m4_set_symbol_name_traced (M4SYMTAB, arg, true);
+         m4_set_symbol_name_traced (M4SYMTAB, arg, strlen (arg), true);
          break;
 
        case '\1':
@@ -741,8 +732,11 @@ main (int argc, char *const *argv, char *const *envp)
          break;
 
        case POPDEF_OPTION:
-         if (m4_symbol_lookup (M4SYMTAB, arg))
-           m4_symbol_popdef (M4SYMTAB, arg);
+         {
+           size_t len = strlen (arg);
+           if (m4_symbol_lookup (M4SYMTAB, arg, len))
+             m4_symbol_popdef (M4SYMTAB, arg, len);
+         }
          break;
 
        case SYNCOUTPUT_OPTION:
@@ -758,7 +752,7 @@ main (int argc, char *const *argv, char *const *envp)
          break;

        case TRACEOFF_OPTION:
-         m4_set_symbol_name_traced (M4SYMTAB, arg, false);
+         m4_set_symbol_name_traced (M4SYMTAB, arg, strlen (arg), false);
          break;
 
        case UNLOAD_MODULE_OPTION:
diff --git a/tests/freeze.at b/tests/freeze.at
index a90e24e..6d76d32 100644
--- a/tests/freeze.at
+++ b/tests/freeze.at
@@ -385,10 +385,11 @@ AT_SETUP([reloading nul])
 AT_KEYWORDS([frozen])
 
 dnl AT_DATA can't generate NUL bytes (at least, not in all shells)
-AT_CHECK([printf 'divert(1)undivert(null.out)' || exit 77],
+AT_CHECK([printf 'define(-\0-,hi)dnl
+divert(1)undivert(null.out)' || exit 77],
  [0], [stdout], [ignore])
 mv stdout frozen.m4
-echo 'divert(0)hi' > unfrozen.m4
+printf 'divert(0)indir(-\0-)\n' > unfrozen.m4
 
 # First generate the `expout' output by running over the sources before
 # freezing.
diff --git a/tests/null.out b/tests/null.out
index cd3764f..03f919b 100644
--- a/tests/null.out
+++ b/tests/null.out
@@ -7,7 +7,7 @@ builtin: 3
 defn: odd name: $1
 divert: --
 esyscmd: [] 0
-ifdef: yes: -- oops: --
+ifdef: yes: -- no: --
 ifelse: yes: --
 index: 2 -1 -1 8
 indir: odd name: 11 3
-- 
1.5.5.1


From 50c5eb094704ebfe272b695a89ad35280a40a716 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 29 May 2008 07:24:16 -0600
Subject: [PATCH] Stage 24c: Improve display of macro names with embedded NUL.

* m4/m4module.h (m4_push_string_init): Add parameters.
* m4/m4private.h (struct m4_macro_args): Remove argv0 and
argv0_len, now redundant with info.
(m4__push_wrapup_init): Add parameter.
* m4/input.c (m4_push_string_init, m4__push_wrapup_init): Track
location from caller, rather than context.
(composite_peek, composite_read, match_input): Adjust callers.
* m4/utility.c (m4_symbol_value_lookup): Quote macro name.
(m4_verror_at_line): Allow NUL in macro name.
* m4/macro.c (trace_flush, m4_trace_prepare, trace_pre): Allow NUL
in trace.
(expand_macro): No longer munge context location.
(collect_arguments, m4_arg_text, m4_arg_empty, m4_arg_len)
(m4_make_argv_ref, m4_push_arg, m4_wrap_args): Adjust callers.
* modules/gnu.c (builtin, indir): Likewise.
* tests/null.m4: Enhance test.
* tests/null.err: Adjust expected output.
* tests/null.out: Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog       |   25 ++++++++++++++++++++++
 m4/input.c      |   34 +++++++++++++++++-------------
 m4/m4module.h   |    2 +-
 m4/m4private.h  |    5 +--
 m4/macro.c      |   60 ++++++++++++++++++++++++++++--------------------------
 m4/utility.c    |   31 +++++++++++++++++-----------
 modules/gnu.c   |   25 +++++++++++++++-------
 tests/null.err  |   18 ++++++++++++++-
 tests/null.m4   |   31 +++++++++++++++++++---------
 tests/null.out  |    3 +-
 tests/others.at |    6 ++--
 11 files changed, 156 insertions(+), 84 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e9c8607..f06aee7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,30 @@
 2008-06-02  Eric Blake  <address@hidden>
 
+       Stage 24c: Improve display of macro names with embedded NUL.
+       Quote instances of problematic characters in macro names when
+       presented to user.  Track location via call_info rather than
+       munging context for every expansion.
+       Memory impact: none.
+       Speed impact: slight penalty, due to more bookkeeping.
+       * m4/m4module.h (m4_push_string_init): Add parameters.
+       * m4/m4private.h (struct m4_macro_args): Remove argv0 and
+       argv0_len, now redundant with info.
+       (m4__push_wrapup_init): Add parameter.
+       * m4/input.c (m4_push_string_init, m4__push_wrapup_init): Track
+       location from caller, rather than context.
+       (composite_peek, composite_read, match_input): Adjust callers.
+       * m4/utility.c (m4_symbol_value_lookup): Quote macro name.
+       (m4_verror_at_line): Allow NUL in macro name.
+       * m4/macro.c (trace_flush, m4_trace_prepare, trace_pre): Allow NUL
+       in trace.
+       (expand_macro): No longer munge context location.
+       (collect_arguments, m4_arg_text, m4_arg_empty, m4_arg_len)
+       (m4_make_argv_ref, m4_push_arg, m4_wrap_args): Adjust callers.
+       * modules/gnu.c (builtin, indir): Likewise.
+       * tests/null.m4: Enhance test.
+       * tests/null.err: Adjust expected output.
+       * tests/null.out: Likewise.
+
        Stage 24b: Allow embedded NUL in macro names.
        Use length rather than NUL-termination when tracking macro names.
        All APIs dealing with symbol table changed.
diff --git a/m4/input.c b/m4/input.c
index d2a508e..212c1c6 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -438,10 +438,12 @@ string_print (m4_input_block *me, m4 *context, m4_obstack 
*obs,
 }
 
 /* First half of m4_push_string ().  The pointer next points to the
-   new input_block.  Return the obstack that will collect the
-   expansion text.  */
+   new input_block.  FILE and LINE describe the location where the
+   macro starts that is generating the expansion (even if the location
+   has advanced in the meantime).  Return the obstack that will
+   collect the expansion text.  */
 m4_obstack *
-m4_push_string_init (m4 *context)
+m4_push_string_init (m4 *context, const char *file, int line)
 {
   /* Free any memory occupied by completely parsed input.  */
   assert (!next);
@@ -450,8 +452,8 @@ m4_push_string_init (m4 *context)
   /* Reserve the next location on the obstack.  */
   next = (m4_input_block *) obstack_alloc (current_input, sizeof *next);
   next->funcs = &string_funcs;
-  next->file = m4_get_current_file (context);
-  next->line = m4_get_current_line (context);
+  next->file = file;
+  next->line = line;
   next->u.u_s.len = 0;
 
   return current_input;
@@ -691,7 +693,7 @@ composite_peek (m4_input_block *me, m4 *context, bool 
allow_argv)
          /* Rather than directly parse argv here, we push another
             input block containing the next unparsed argument from
             argv.  */
-         m4_push_string_init (context);
+         m4_push_string_init (context, me->file, me->line);
          m4__push_arg_quote (context, current_input, chain->u.u_a.argv,
                              chain->u.u_a.index,
                              m4__quote_cache (M4SYNTAX, NULL,
@@ -759,7 +761,7 @@ composite_read (m4_input_block *me, m4 *context, bool 
allow_quote,
          /* Rather than directly parse argv here, we push another
             input block containing the next unparsed argument from
             argv.  */
-         m4_push_string_init (context);
+         m4_push_string_init (context, me->file, me->line);
          m4__push_arg_quote (context, current_input, chain->u.u_a.argv,
                              chain->u.u_a.index,
                              m4__quote_cache (M4SYNTAX, NULL,
@@ -1012,10 +1014,11 @@ m4_input_print (m4 *context, m4_obstack *obs, int 
debug_level)
 
 /* Return an obstack ready for direct expansion of wrapup text, and
    set *END to the location that should be updated if any builtin
-   tokens are wrapped.  This should be followed by
-   m4__push_wrapup_finish ().  */
+   tokens are wrapped.  Store the location of CALLER with the wrapped
+   text.  This should be followed by m4__push_wrapup_finish ().  */
 m4_obstack *
-m4__push_wrapup_init (m4 *context, m4__symbol_chain ***end)
+m4__push_wrapup_init (m4 *context, const m4_call_info *caller,
+                     m4__symbol_chain ***end)
 {
   m4_input_block *i;
   m4__symbol_chain *chain;
@@ -1032,8 +1035,8 @@ m4__push_wrapup_init (m4 *context, m4__symbol_chain 
***end)
       i = (m4_input_block *) obstack_alloc (wrapup_stack, sizeof *i);
       i->prev = wsp;
       i->funcs = &composite_funcs;
-      i->file = m4_get_current_file (context);
-      i->line = m4_get_current_line (context);
+      i->file = caller->file;
+      i->line = caller->line;
       i->u.u_c.chain = i->u.u_c.end = NULL;
       wsp = i;
     }
@@ -1046,8 +1049,8 @@ m4__push_wrapup_init (m4 *context, m4__symbol_chain 
***end)
   chain->next = NULL;
   chain->type = M4__CHAIN_LOC;
   chain->quote_age = 0;
-  chain->u.u_l.file = m4_get_current_file (context);
-  chain->u.u_l.line = m4_get_current_line (context);
+  chain->u.u_l.file = caller->file;
+  chain->u.u_l.line = caller->line;
   *end = &i->u.u_c.end;
   return wrapup_stack;
 }
@@ -1396,7 +1399,8 @@ match_input (m4 *context, const char *s, bool consume)
     }
 
   /* Failed or shouldn't consume, push back input.  */
-  st = m4_push_string_init (context);
+  st = m4_push_string_init (context, m4_get_current_file (context),
+                           m4_get_current_line (context));
   obstack_grow (st, t, n);
   m4_push_string_finish ();
   return result;
diff --git a/m4/m4module.h b/m4/m4module.h
index 1bd90c7..346463d 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -503,7 +503,7 @@ extern      void    m4_skip_line    (m4 *context, const 
m4_call_info *);
 
 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_obstack      *m4_push_string_init    (m4 *, const char *, int);
 extern void    m4_push_string_finish   (void);
 extern bool    m4_pop_wrapup   (m4 *);
 extern void    m4_input_print  (m4 *, m4_obstack *, int);
diff --git a/m4/m4private.h b/m4/m4private.h
index c092016..e1c8163 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -307,8 +307,6 @@ struct m4_macro_args
   bool_bitfield flatten : 1;
   /* True if any token contains builtins.  */
   bool_bitfield has_func : 1;
-  const char *argv0; /* The macro name being expanded.  */
-  size_t argv0_len; /* Length of argv0.  */
   /* The value of quote_age for all tokens, or 0 if quote_age changed
      during parsing or any token is potentially unsafe and requires a
      rescan.  */
@@ -561,7 +559,8 @@ extern      void            m4__append_builtin (m4_obstack 
*, const m4__builtin *,
                                            m4__symbol_chain **);
 extern bool            m4__push_symbol (m4 *, m4_symbol_value *, size_t,
                                         bool);
-extern m4_obstack      *m4__push_wrapup_init (m4 *, m4__symbol_chain ***);
+extern m4_obstack      *m4__push_wrapup_init (m4 *, const m4_call_info *,
+                                              m4__symbol_chain ***);
 extern void            m4__push_wrapup_finish (void);
 extern m4__token_type  m4__next_token (m4 *, m4_symbol_value *, int *,
                                        m4_obstack *, bool,
diff --git a/m4/macro.c b/m4/macro.c
index f28b3f6..1436ea9 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -443,10 +443,6 @@ expand_macro (m4 *context, const char *name, size_t len, 
m4_symbol *symbol)
   m4__macro_arg_stacks *stack; /* Storage for this macro.  */
   m4_call_info info;           /* Context of this macro call.  */
 
-  /* TODO - use m4_call_info to avoid temporary munging of global state.  */
-  const char *loc_close_file;
-  int loc_close_line;
-
   /* Obstack preparation.  */
   level = context->expansion_level;
   if (context->stacks_count <= level)
@@ -506,17 +502,11 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
   args_scratch = obstack_finish (stack->args);
 
   /* 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, info.file);
-  m4_set_current_line (context, info.line);
-  expansion = m4_push_string_init (context);
+  expansion = m4_push_string_init (context, info.file, info.line);
   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;
@@ -538,7 +528,8 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
          if (debug_macro_level & PRINT_ARGCOUNT_CHANGES)
            xfprintf (stderr, "m4debug: -%d- `%s' in use, level=%d, "
                      "refcount=%zu, argcount=%zu\n", info.call_id,
-                     argv->argv0, level, stack->refcount, stack->argcount);
+                     argv->info->name, level, stack->refcount,
+                     stack->argcount);
        }
       else
        {
@@ -571,14 +562,12 @@ collect_arguments (m4 *context, m4_call_info *info, 
m4_symbol *symbol,
   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, info->name, info->name_len);
-  args.argv0_len = info->name_len;
+  info->name = (char *) obstack_copy0 (arguments, info->name, 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));
-  info->name = args.argv0;
 
   if (m4__next_token_is_open (context))
     {
@@ -890,16 +879,17 @@ static void
 trace_flush (m4 *context, unsigned int start)
 {
   char *str;
+  size_t len = obstack_object_size (&context->trace_messages);
   FILE *file = m4_get_debug_file (context);
 
   if (file)
     {
-      obstack_1grow (&context->trace_messages, '\0');
+      /* TODO - quote nonprintable characters if debug is tty?  */
       str = (char *) obstack_base (&context->trace_messages);
-      fprintf (file, "%s\n", &str[start]);
+      fwrite (&str[start], 1, len - start, file);
+      fputc ('\n', file);
     }
-  start -= obstack_object_size (&context->trace_messages);
-  obstack_blank (&context->trace_messages, start);
+  obstack_blank (&context->trace_messages, start - len);
 }
 
 /* Do pre-argument-collection tracing for the macro described in INFO.
@@ -917,7 +907,8 @@ m4_trace_prepare (m4 *context, const m4_call_info *info,
   if (info->trace && (info->debug_level & M4_DEBUG_TRACE_CALL))
     {
       unsigned int start = trace_header (context, info);
-      trace_format (context, "%s ... = ", info->name);
+      obstack_grow (&context->trace_messages, info->name, info->name_len);
+      obstack_grow (&context->trace_messages, " ... = ", 7);
       m4__symbol_value_print (context, value, &context->trace_messages, quotes,
                              false, NULL, &arg_length, module);
       trace_flush (context, start);
@@ -935,7 +926,8 @@ trace_pre (m4 *context, m4_macro_args *argv)
   unsigned int start = trace_header (context, argv->info);
 
   assert (argv->info->trace);
-  trace_format (context, "%s", M4ARG (0));
+  obstack_grow (&context->trace_messages, argv->info->name,
+               argv->info->name_len);
 
   if (1 < m4_arg_argc (argv) && (trace_level & M4_DEBUG_TRACE_ARGS))
     {
@@ -1258,7 +1250,10 @@ m4_arg_text (m4 *context, m4_macro_args *argv, size_t 
arg, bool flatten)
   m4_obstack *obs;
 
   if (arg == 0)
-    return argv->argv0;
+    {
+      assert (argv->info);
+      return argv->info->name;
+    }
   if (argv->argc <= arg)
     return "";
   value = arg_symbol (argv, arg, NULL, flatten);
@@ -1453,8 +1448,12 @@ m4_arg_equal (m4 *context, m4_macro_args *argv, size_t 
indexa, size_t indexb)
 bool
 m4_arg_empty (m4_macro_args *argv, size_t arg)
 {
-  return (arg ? m4_arg_symbol (argv, arg) == &empty_symbol
-         : !argv->argv0_len);
+  if (!arg)
+    {
+      assert (argv->info);
+      return !argv->info->name_len;
+    }
+  return m4_arg_symbol (argv, arg) == &empty_symbol;
 }
 
 /* Given ARGV, return the length of argument ARG.  Abort if the
@@ -1467,7 +1466,10 @@ m4_arg_len (m4 *context, m4_macro_args *argv, size_t arg)
   size_t len;
 
   if (arg == 0)
-    return argv->argv0_len;
+    {
+      assert (argv->info);
+      return argv->info->name_len;
+    }
   if (argv->argc <= arg)
     return 0;
   value = m4_arg_symbol (argv, arg);
@@ -1629,8 +1631,6 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, const 
char *argv0,
     }
   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;
@@ -1649,7 +1649,9 @@ m4_push_arg (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, size_t arg)
 
   if (arg == 0)
     {
-      m4_set_symbol_value_text (&value, argv->argv0, argv->argv0_len, 0);
+      assert (argv->info);
+      m4_set_symbol_value_text (&value, argv->info->name, argv->info->name_len,
+                               0);
       if (m4__push_symbol (context, &value, context->expansion_level - 1,
                           argv->inuse))
        arg_mark (argv);
@@ -1721,7 +1723,7 @@ m4_wrap_args (m4 *context, m4_macro_args *argv)
   if (limit == 2 && m4_arg_empty (argv, 1))
     return;
 
-  obs = m4__push_wrapup_init (context, &end);
+  obs = m4__push_wrapup_init (context, argv->info, &end);
   for (i = 1; i < limit; i++)
     {
       if (i != 1)
diff --git a/m4/utility.c b/m4/utility.c
index 7c91191..c104779 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -132,9 +132,11 @@ m4_symbol_value_lookup (m4 *context, m4_macro_args *argv, 
size_t i,
   if (m4_is_arg_text (argv, i))
     {
       const char *name = M4ARG (i);
-      result = m4_symbol_lookup (M4SYMTAB, name, M4ARGLEN (i));
+      size_t len = M4ARGLEN (i);
+      result = m4_symbol_lookup (M4SYMTAB, name, len);
       if (must_exist && !result)
-       m4_warn (context, 0, argv->info, _("undefined macro `%s'"), name);
+       m4_warn (context, 0, argv->info, _("undefined macro %s"),
+                quotearg_style_mem (locale_quoting_style, name, len));
     }
   else
     m4_warn (context, 0, argv->info, _("invalid macro name ignored"));
@@ -154,6 +156,7 @@ m4_verror_at_line (m4 *context, bool warn, int status, int 
errnum,
   char *full = NULL;
   char *safe_macro = NULL;
   const char *macro = caller ? caller->name : NULL;
+  size_t len = caller ? caller->name_len : 0;
   const char *file = caller ? caller->file : m4_get_current_file (context);
   int line = caller ? caller->line : m4_get_current_line (context);
 
@@ -161,29 +164,33 @@ m4_verror_at_line (m4 *context, bool warn, int status, 
int errnum,
   /* Sanitize MACRO, since we are turning around and using it in a
      format string.  The allocation is overly conservative, but
      problematic macro names only occur via indir or changesyntax.  */
-  if (macro && strchr (macro, '%'))
+  if (macro && memchr (macro, '%', len))
     {
-      char *p = safe_macro = xcharalloc (2 * strlen (macro) + 1);
-      do
+      char *p = safe_macro = xcharalloc (2 * len);
+      const char *end = macro + len;
+      while (macro != end)
        {
          if (*macro == '%')
-           *p++ = '%';
+           {
+             *p++ = '%';
+             len++;
+           }
          *p++ = *macro++;
        }
-      while (*macro);
-      *p = '\0';
     }
+  if (macro)
+    /* Use slot 1, so that the rest of the code can use the simpler
+       quotearg interface in slot 0.  */
+    macro = quotearg_n_mem (1, safe_macro ? safe_macro : macro, len);
   /* Prepend warning and the macro name, as needed.  But if that fails
      for non-memory reasons (unlikely), then still use the original
      format.  */
   if (warn && macro)
-    full = xasprintf (_("Warning: %s: %s"),
-                     quotearg (safe_macro ? safe_macro : macro), format);
+    full = xasprintf (_("Warning: %s: %s"), macro, format);
   else if (warn)
     full = xasprintf (_("Warning: %s"), format);
   else if (macro)
-    full = xasprintf (_("%s: %s"),
-                     quotearg (safe_macro ? safe_macro : macro), format);
+    full = xasprintf (_("%s: %s"), macro, format);
   verror_at_line (status, errnum, line ? file : NULL, line,
                  full ? full : format, args);
   free (full);
diff --git a/modules/gnu.c b/modules/gnu.c
index a13ae11..ead1b62 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -29,6 +29,7 @@
 #endif
 
 #include "modules/m4.h"
+#include "quotearg.h"
 
 /* Rename exported symbols for dlpreload()ing.  */
 #define m4_builtin_table       gnu_LTX_m4_builtin_table
@@ -404,7 +405,8 @@ M4BUILTIN_HANDLER (builtin)
 {
   const m4_call_info *me = m4_arg_info (argv);
   const char *name;
-  m4_symbol_value *value;
+  size_t len;
+  m4_symbol_value *value = NULL;
 
   if (!m4_is_arg_text (argv, 1))
     {
@@ -419,9 +421,12 @@ M4BUILTIN_HANDLER (builtin)
              return;
            }
          name = M4ARG (2);
-         value = m4_builtin_find_by_name (NULL, name);
+         len = M4ARGLEN (2);
+         if (len == strlen (name))
+           value = m4_builtin_find_by_name (NULL, name);
          if (value == NULL)
-           m4_warn (context, 0, me, _("undefined builtin `%s'"), name);
+           m4_warn (context, 0, me, _("undefined builtin %s"),
+                    quotearg_style_mem (locale_quoting_style, name, len));
          else
            {
              m4_push_builtin (context, obs, value);
@@ -434,16 +439,19 @@ M4BUILTIN_HANDLER (builtin)
   else
     {
       name = M4ARG (1);
-      value = m4_builtin_find_by_name (NULL, name);
+      len = M4ARGLEN (1);
+      if (len == strlen (name))
+       value = m4_builtin_find_by_name (NULL, name);
       if (value == NULL)
-       m4_warn (context, 0, me, _("undefined builtin `%s'"), name);
+       m4_warn (context, 0, me, _("undefined builtin %s"),
+                quotearg_style_mem (locale_quoting_style, name, len));
       else
        {
          const m4_builtin *bp = m4_get_symbol_value_builtin (value);
          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),
-                                      flatten, false);
+         new_argv = m4_make_argv_ref (context, argv, name, len, flatten,
+                                      false);
          if (!m4_bad_argc (context, argc - 1, m4_arg_info (new_argv),
                            bp->min_args, bp->max_args,
                            (bp->flags & M4_BUILTIN_SIDE_EFFECT) != 0))
@@ -678,7 +686,8 @@ M4BUILTIN_HANDLER (indir)
       m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, name, len);
 
       if (symbol == NULL)
-       m4_warn (context, 0, me, _("undefined macro `%s'"), name);
+       m4_warn (context, 0, me, _("undefined macro %s"),
+                quotearg_style_mem (locale_quoting_style, name, len));
       else
        {
          m4_macro_args *new_argv;
diff --git a/tests/null.err b/tests/null.err
index d825818..8bf1f4f 100644
--- a/tests/null.err
+++ b/tests/null.err
@@ -1,3 +1,17 @@
+builtin:
+m4:null.m4:19: Warning: builtin: undefined builtin `-\0-'
+defn:
+m4:null.m4:39: Warning: defn: undefined macro `\0-\0'
+dumpdef:
+m4:null.m4:51: Warning: dumpdef: undefined macro `\0-\0'
+:      `empty'
+-:     `dash'
+--:   `odd name: $1'
+--:   `odd name: $1'
+--:    `dashes'
 errprint: -- --
-traceon: fixme
-m4trace: -1- -(`-
+indir:
+m4:null.m4:81: Warning: indir: undefined macro `\0-\0'
+m4:null.m4:83: Warning: \0\0%%: extra arguments ignored: 1 > 0
+traceon:
+m4trace: -1- --(`--') -> `strange: --'
diff --git a/tests/null.m4 b/tests/null.m4
index 851d665..55bd3bd 100644
--- a/tests/null.m4
+++ b/tests/null.m4
@@ -14,8 +14,9 @@ define(`echo', address@hidden')define(`', `empty')dnl
 define(`-', `dash')define(`--', `dashes')dnl
 user: echo(--,`11')
 dnl All macros matching __*__ take no arguments, and never produce NUL.
-dnl First argument of builtin: not tested yet. No builtin includes NUL, so
-dnl   this needs to warn, but warning output needs quoting.
+dnl First argument of builtin:
+errprint(`builtin:
+')builtin(`--')dnl
 dnl Remaining arguments of builtin:
 `builtin:' builtin(`len', --)
 dnl Single-byte delimiter in changecom: not tested yet
@@ -33,7 +34,9 @@ dnl Warning from decr: not tested yet. NUL not a number, 
needs to warn
 dnl Macro name of define:
 define(`--', `odd name: $1')dnl
 dnl Definition of define: not tested yet
-dnl Undefined argument of defn: not tested yet
+dnl Undefined argument of defn:
+errprint(`defn:
+')defn(`-')dnl
 dnl Defined macro name in defn:
 `defn:' defn(`--')
 dnl Macro contents in defn: not tested yet
@@ -43,8 +46,11 @@ divert(`1')`divert:' --
 divert`'undivert(`1')dnl
 dnl Divnum takes no arguments, and never produces NUL.
 dnl Discarded by dnl: --
-dnl Undefined argument of dumpdef: not tested yet. Needs to quote properly.
-dnl Defined macro names in dumpdef: not tested yet
+dnl Undefined argument of dumpdef:
+errprint(`dumpdef:
+')dumpdef(`-')dnl
+dnl Defined macro names in dumpdef:
+dumpdef(`--', `-', `', `--', `--')dnl
 dnl Macro contents in dumpdef: not tested yet
 dnl Passed through errprint:
 errprint(`errprint:' --, `--
@@ -70,15 +76,20 @@ dnl Passed through index:
  index(`-', `-')
 dnl Defined first argument of indir:
 `indir:' indir(`--', 11)dnl
-dnl Undefined first argument of indir: not tested yet. Needs to warn
-dnl Warning issued via indir: not tested yet
+dnl Undefined first argument of indir:
+errprint(`indir:
+')indir(`-')dnl
+dnl Warning issued via indir:
+ define(`%%', defn(`divnum'))indir(`%%', `extra')dnl
 dnl Other arguments of indir:
  indir(`len', `--')
 dnl Passed through len:
 `len:' len() len(--)
 dnl Test m4exit separately from m4wrap; see above.
-dnl Undefined macro name in m4symbols: not tested yet, needs to warn
-dnl Defined macro name in m4symbols: not tested yet
+dnl Undefined macro name in m4symbols:
+`m4symbols:'m4symbols(`-', `-')dnl
+dnl Defined macro name in m4symbols:
+ m4symbols(`--')
 dnl Passed through m4wrap:
 m4wrap(``m4wrap:' --
 ')dnl
@@ -128,7 +139,7 @@ dnl Sysval takes no arguments, and never produces NUL.
 dnl Passed to traceoff:
 traceoff(`--', `')dnl
 dnl Macro name and arguments of traceon: not perfect yet
-`traceon:' errprint(`traceon: fixme
+`traceon:' errprint(`traceon:
 ')traceon(`--')indir(`--', `--')
 dnl Defined text of traceon: not tested yet, needs quoting
 dnl First argument of translit: not tested yet
diff --git a/tests/null.out b/tests/null.out
index 03f919b..3a96faa 100644
--- a/tests/null.out
+++ b/tests/null.out
@@ -10,8 +10,9 @@ esyscmd: [] 0
 ifdef: yes: -- no: --
 ifelse: yes: --
 index: 2 -1 -1 8
-indir: odd name: 11 3
+indir: odd name: 11 0 3
 len: 1 3
+m4symbols: --
 patsubst: .. -- abc -!- ---
 popdef: ok
 pushdef: ok
diff --git a/tests/others.at b/tests/others.at
index 3468044..781e07a 100644
--- a/tests/others.at
+++ b/tests/others.at
@@ -351,11 +351,11 @@ AT_SETUP([nul character])
 
 # We don't embed null.* in here, since it is harder to guarantee the
 # behavior of NUL through autom4te.
-cp "$abs_srcdir/null.out" expout
-cp "$abs_srcdir/null.err" experr
+sed "s|null.m4|$abs_srcdir/null.m4|" < "$abs_srcdir/null.out" > expout
+sed "s|null.m4|$abs_srcdir/null.m4|" < "$abs_srcdir/null.err" > experr
 
 dnl all but m4exit
-AT_CHECK_M4([-Dm4exit "$abs_srcdir/null.m4"], [0], [expout], [experr])
+AT_CHECK_M4([-Dm4exit -I "$abs_srcdir" null.m4], [0], [expout], [experr])
 
 dnl just m4exit
 AT_CHECK_M4(["$abs_srcdir/null.m4"], [2],
-- 
1.5.5.1

From f3fbfb92cc3f44af244f888b8be6b465493dc590 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 18 Dec 2007 16:13:16 -0700
Subject: [PATCH] Stage 24: Allow embedded NUL in macro names.

* m4/gnulib-cache.m4: Import xmemdup0 module.
* src/m4.h (define_builtin, push_string_init, push_wrapup_init)
(lookup_symbol): Add parameters.
(struct call_info): Add name_len member.
(struct token_data) [ENABLE_CHANGEWORD]: Add original_len member.
(struct symbol): Add len member.
(TOKEN_DATA_ORIG_LEN, SYMBOL_NAME_LEN): New accessors.
(m4_error_at_line, m4_warn_at_line): Delete.
(m4_error, m4_warn, debug_set_output, skip_line, set_word_regexp)
(bad_argc, evaluate): Adjust parameter type.
* src/m4.c (m4_error_at_line, m4_warn_at_line): Delete.
(m4_verror_at_line, m4_error, m4_warn): Adjust parameter type.
(main): Adjust caller.
* src/symtab.c (profile_strcmp) [DEBUG_SYMTAB]: Rename...
(profile_memcmp): ...and accomodate NUL.
(hash, lookup_symbol): Add parameter to track length.
(symtab_debug, symtab_print_list): Adjust callers.
* src/input.c (push_string_init, push_wrapup_init): Take location
as parameter rather than using global state.
(skip_line, set_word_regexp, next_token): Adjust parameter type.
(peek_input, next_char_1, match_input, lex_debug): Adjust
callers.
* src/macro.c (struct macro_arguments): Delete argv0 and argv0_len
members, now covered by info.
(expand_input, expand_token, expand_argument, collect_arguments)
(expand_macro, arg_text, arg_empty, arg_len, make_argv_ref)
(push_arg, wrap_args): Adjust callers.
* src/builtin.c (define_builtin): Add parameter.
(bad_argc, numeric_arg, mkstemp_helper, substitute): Adjust
parameter type.
(define_user_macro, builtin_init, define_macro, m4_undefine)
(m4_popdef, m4_ifdef, m4_ifelse, dumpdef_cmp, m4_dumpdef)
(m4_builtin, m4_indir, m4_defn, m4_syscmd, m4_esyscmd, m4_eval)
(m4_incr, m4_decr, m4_divert, m4_divnum, m4_undivert, m4_shift)
(m4_changequote, m4_changecom, m4_changeword, include)
(m4_maketemp, m4_mkstemp, m4_errprint, m4___file__, m4___line__)
(m4___program__, m4_m4exit, m4_m4wrap, m4_traceon, m4_traceoff)
(m4_debugmode, m4_debugfile, m4_len, m4_index, m4_substr)
(m4_translit, m4_format, m4_regexp, m4_patsubst): Adjust all
callers.
* src/debug.c (debug_set_file, debug_set_output): Adjust parameter
type.
(trace_flush, trace_prepre, trace_pre): Adjust all callers.
* src/eval.c (logical_or_term, logical_and_term, or_term)
(xor_term, and_term, equality_term, cmp_term, shift_term)
(add_term, mult_term, exp_term, unary_term, simple_term)
(evaluate): Adjust parameter type.
* src/format.c (arg_int, arg_long, arg_double): Likewise.
(expand_format): Adjust caller.
* doc/m4.texinfo (Using frozen files): Test this.
* examples/null.m4: Likewise.
* examples/null.err: Adjust expected output.
* examples/null.out: Likewise.

(cherry picked from commit 6167507cf07ddbc838e14e3d66803b360e63f6f2)

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog          |   61 ++++++++++++++++
 doc/m4.texinfo     |   16 ++++
 examples/null.err  |   18 ++++-
 examples/null.m4   |   27 +++++---
 examples/null.out  |    4 +-
 m4/gnulib-cache.m4 |    4 +-
 src/builtin.c      |  193 +++++++++++++++++++++++++++++----------------------
 src/debug.c        |   26 ++++---
 src/eval.c         |   56 ++++++++--------
 src/format.c       |    8 +-
 src/freeze.c       |   10 ++--
 src/input.c        |  103 +++++++++++++++-------------
 src/m4.c           |  130 ++++++++++++-----------------------
 src/m4.h           |   34 +++++----
 src/macro.c        |   68 ++++++++----------
 src/symtab.c       |   74 +++++++++++---------
 16 files changed, 466 insertions(+), 366 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 7bb18a1..b3ddf60 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,66 @@
 2008-06-02  Eric Blake  <address@hidden>
 
+       Stage 24: Allow embedded NUL in macro names.
+       Replace const char *macro_name with const m4_call_info *call.  Use
+       length rather than NUL-termination when tracking macro names,
+       including in diagnostics.  Quote instances of problematic
+       characters in macro names when presented to user.
+       Memory impact: none.
+       Speed impact: slight penalty, due to more bookkeeping.
+       * m4/gnulib-cache.m4: Import xmemdup0 module.
+       * src/m4.h (define_builtin, push_string_init, push_wrapup_init)
+       (lookup_symbol): Add parameters.
+       (struct call_info): Add name_len member.
+       (struct token_data) [ENABLE_CHANGEWORD]: Add original_len member.
+       (struct symbol): Add len member.
+       (TOKEN_DATA_ORIG_LEN, SYMBOL_NAME_LEN): New accessors.
+       (m4_error_at_line, m4_warn_at_line): Delete.
+       (m4_error, m4_warn, debug_set_output, skip_line, set_word_regexp)
+       (bad_argc, evaluate): Adjust parameter type.
+       * src/m4.c (m4_error_at_line, m4_warn_at_line): Delete.
+       (m4_verror_at_line, m4_error, m4_warn): Adjust parameter type.
+       (main): Adjust caller.
+       * src/symtab.c (profile_strcmp) [DEBUG_SYMTAB]: Rename...
+       (profile_memcmp): ...and accomodate NUL.
+       (hash, lookup_symbol): Add parameter to track length.
+       (symtab_debug, symtab_print_list): Adjust callers.
+       * src/input.c (push_string_init, push_wrapup_init): Take location
+       as parameter rather than using global state.
+       (skip_line, set_word_regexp, next_token): Adjust parameter type.
+       (peek_input, next_char_1, match_input, lex_debug): Adjust
+       callers.
+       * src/macro.c (struct macro_arguments): Delete argv0 and argv0_len
+       members, now covered by info.
+       (expand_input, expand_token, expand_argument, collect_arguments)
+       (expand_macro, arg_text, arg_empty, arg_len, make_argv_ref)
+       (push_arg, wrap_args): Adjust callers.
+       * src/builtin.c (define_builtin): Add parameter.
+       (bad_argc, numeric_arg, mkstemp_helper, substitute): Adjust
+       parameter type.
+       (define_user_macro, builtin_init, define_macro, m4_undefine)
+       (m4_popdef, m4_ifdef, m4_ifelse, dumpdef_cmp, m4_dumpdef)
+       (m4_builtin, m4_indir, m4_defn, m4_syscmd, m4_esyscmd, m4_eval)
+       (m4_incr, m4_decr, m4_divert, m4_divnum, m4_undivert, m4_shift)
+       (m4_changequote, m4_changecom, m4_changeword, include)
+       (m4_maketemp, m4_mkstemp, m4_errprint, m4___file__, m4___line__)
+       (m4___program__, m4_m4exit, m4_m4wrap, m4_traceon, m4_traceoff)
+       (m4_debugmode, m4_debugfile, m4_len, m4_index, m4_substr)
+       (m4_translit, m4_format, m4_regexp, m4_patsubst): Adjust all
+       callers.
+       * src/debug.c (debug_set_file, debug_set_output): Adjust parameter
+       type.
+       (trace_flush, trace_prepre, trace_pre): Adjust all callers.
+       * src/eval.c (logical_or_term, logical_and_term, or_term)
+       (xor_term, and_term, equality_term, cmp_term, shift_term)
+       (add_term, mult_term, exp_term, unary_term, simple_term)
+       (evaluate): Adjust parameter type.
+       * src/format.c (arg_int, arg_long, arg_double): Likewise.
+       (expand_format): Adjust caller.
+       * doc/m4.texinfo (Using frozen files): Test this.
+       * examples/null.m4: Likewise.
+       * examples/null.err: Adjust expected output.
+       * examples/null.out: Likewise.
+
        Allow autobuild usage.
        * m4/gnulib.cache: Import autobuild module.
 
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 67393e2..fe429b4 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -7003,6 +7003,22 @@ $ @kbd{m4 -F /none/such}
 ^D
 @error{}m4: cannot open /none/such: No such file or directory
 @end example
+
address@hidden Another test - can we properly freeze embedded NUL?
+
address@hidden xout: null.out
address@hidden
+ifdef(`__unix__', ,
+      `errprint(` skipping: syscmd does not have unix semantics
+')m4exit(`77')')dnl
+changequote(`[', `]')dnl
+syscmd([printf 'define(-\0-,hi)dnl
+divert(1)undivert(null.out)' | ]__program__[ -F in.m4f \
+     && printf 'errprint(indir(-\0-))' | ]__program__[ -R in.m4f \
+     && rm in.m4f])errprint([ ]sysval[
+])dnl
address@hidden 0
address@hidden example
 @end ignore
 
 When an @code{m4} run is to be frozen, the automatic undiversion
diff --git a/examples/null.err b/examples/null.err
index d825818..05a1ba3 100644
--- a/examples/null.err
+++ b/examples/null.err
@@ -1,3 +1,17 @@
+builtin:
+m4:examples/null.m4:19: Warning: builtin: undefined builtin `-\0-'
+defn:
+m4:examples/null.m4:37: Warning: defn: undefined macro `\0-\0'
+dumpdef:
+m4:examples/null.m4:49: Warning: dumpdef: undefined macro `\0-\0'
+:      `empty'
+-:     `dash'
+--:   `odd name: $1'
+--:   `odd name: $1'
+--:    `dashes'
 errprint: -- --
-traceon: fixme
-m4trace: -1- -(`-
+indir:
+m4:examples/null.m4:79: Warning: indir: undefined macro `\0-\0'
+m4:examples/null.m4:81: Warning: \0\0%%: extra arguments ignored: 1 > 0
+traceon:
+m4trace: -1- --(`--') -> `strange: --'
diff --git a/examples/null.m4 b/examples/null.m4
index 10d15ae..c928360 100644
--- a/examples/null.m4
+++ b/examples/null.m4
@@ -14,8 +14,9 @@ define(`echo', address@hidden')define(`', `empty')dnl
 define(`-', `dash')define(`--', `dashes')dnl
 user: echo(--,`11')
 dnl All macros matching __*__ take no arguments, and never produce NUL.
-dnl First argument of builtin: not tested yet. No builtin includes NUL, so
-dnl   this needs to warn, but warning output needs quoting.
+dnl First argument of builtin:
+errprint(`builtin:
+')builtin(`--')dnl
 dnl Remaining arguments of builtin:
 `builtin:' builtin(`len', --)
 dnl Single-byte delimiter in changecom: not tested yet
@@ -31,7 +32,9 @@ dnl Warning from decr: not tested yet. NUL not a number, 
needs to warn
 dnl Macro name of define:
 define(`--', `odd name: $1')dnl
 dnl Definition of define: not tested yet
-dnl Undefined argument of defn: not tested yet. Should it warn?
+dnl Undefined argument of defn:
+errprint(`defn:
+')defn(`-')dnl
 dnl Defined macro name in defn:
 `defn:' defn(`--')
 dnl Macro contents in defn: not tested yet
@@ -41,9 +44,12 @@ divert(`1')`divert:' --
 divert`'undivert(`1')dnl
 dnl Divnum takes no arguments, and never produces NUL.
 dnl Discarded by dnl: --
-dnl Undefined argument of dumpdef: not tested yet. Needs to quote properly.
-dnl Defined macro names in dumpdef: not tested yet
-dnl Macro contents in dumpdef: not tested yet
+dnl Undefined argument of dumpdef:
+errprint(`dumpdef:
+')dumpdef(`-')dnl
+dnl Defined macro names in dumpdef:
+dumpdef(`--', `-', `', `--', `--')dnl
+dnl Macro contents in dumpdef: not tested yet, needs quoting
 dnl Passed through errprint:
 errprint(`errprint:' --, `--
 ')dnl
@@ -68,8 +74,11 @@ dnl Passed through index:
  index(`-', `-')
 dnl Defined first argument of indir:
 `indir:' indir(`--', 11)dnl
-dnl Undefined first argument of indir: not tested yet. Needs to warn
-dnl Warning issued via indir: not tested yet
+dnl Undefined first argument of indir:
+errprint(`indir:
+')indir(`-')dnl
+dnl Warning issued via indir:
+ define(`%%', defn(`divnum'))indir(`%%', `extra')dnl
 dnl Other arguments of indir:
  indir(`len', `--')
 dnl Passed through len:
@@ -117,7 +126,7 @@ dnl Sysval takes no arguments, and never produces NUL.
 dnl Passed to traceoff:
 traceoff(`--', `')dnl
 dnl Macro name and arguments of traceon: not perfect yet, needs quoting
-`traceon:' errprint(`traceon: fixme
+`traceon:' errprint(`traceon:
 ')traceon(`--')indir(`--', `--')
 dnl Defined text of traceon: not tested yet, needs quoting
 dnl First argument of translit: not tested yet
diff --git a/examples/null.out b/examples/null.out
index cd3764f..66f41b5 100644
--- a/examples/null.out
+++ b/examples/null.out
@@ -7,10 +7,10 @@ builtin: 3
 defn: odd name: $1
 divert: --
 esyscmd: [] 0
-ifdef: yes: -- oops: --
+ifdef: yes: -- no: --
 ifelse: yes: --
 index: 2 -1 -1 8
-indir: odd name: 11 3
+indir: odd name: 11 0 3
 len: 1 3
 patsubst: .. -- abc -!- ---
 popdef: ok
diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4
index 26abbef..b6ba202 100644
--- a/m4/gnulib-cache.m4
+++ b/m4/gnulib-cache.m4
@@ -15,11 +15,11 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --local-dir=local --lib=libm4 
--source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests 
--no-libtool --macro-prefix=M4 announce-gen assert autobuild avltree-oset 
binary-io clean-temp cloexec close-stream closein config-h error fdl fflush 
flexmember fopen-safer fseeko gendocs getopt git-version-gen gnumakefile 
gnupload gpl-3.0 intprops memchr2 memmem mkstemp obstack quote regex stdbool 
stdint stdlib-safer strtod strtol unlocked-io vasnprintf-posix verror 
version-etc version-etc-fsf xalloc xprintf xvasprintf-posix
+#   gnulib-tool --import --dir=. --local-dir=local --lib=libm4 
--source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests 
--no-libtool --macro-prefix=M4 announce-gen assert autobuild avltree-oset 
binary-io clean-temp cloexec close-stream closein config-h error fdl fflush 
flexmember fopen-safer fseeko gendocs getopt git-version-gen gnumakefile 
gnupload gpl-3.0 intprops memchr2 memmem mkstemp obstack quote regex stdbool 
stdint stdlib-safer strtod strtol unlocked-io vasnprintf-posix verror 
version-etc version-etc-fsf xalloc xmemdup0 xprintf xvasprintf-posix
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([local])
-gl_MODULES([announce-gen assert autobuild avltree-oset binary-io clean-temp 
cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer 
fseeko gendocs getopt git-version-gen gnumakefile gnupload gpl-3.0 intprops 
memchr2 memmem mkstemp obstack quote regex stdbool stdint stdlib-safer strtod 
strtol unlocked-io vasnprintf-posix verror version-etc version-etc-fsf xalloc 
xprintf xvasprintf-posix])
+gl_MODULES([announce-gen assert autobuild avltree-oset binary-io clean-temp 
cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer 
fseeko gendocs getopt git-version-gen gnumakefile gnupload gpl-3.0 intprops 
memchr2 memmem mkstemp obstack quote regex stdbool stdint stdlib-safer strtod 
strtol unlocked-io vasnprintf-posix verror version-etc version-etc-fsf xalloc 
xmemdup0 xprintf xvasprintf-posix])
 gl_AVOID([])
 gl_SOURCE_BASE([lib])
 gl_M4_BASE([m4])
diff --git a/src/builtin.c b/src/builtin.c
index 830d4a2..e3ddf32 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -230,18 +230,19 @@ func_print (struct obstack *obs, const builtin *func, 
bool flatten,
     }
 }
 
-/*-------------------------------------------------------------------------.
-| Install a builtin macro with name NAME, bound to the C function given in |
-| BP.  MODE is SYMBOL_INSERT or SYMBOL_PUSHDEF.  TRACED defines whether        
   |
-| NAME is to be traced.                                                        
   |
-`-------------------------------------------------------------------------*/
+/*----------------------------------------------------------------.
+| Install a builtin macro with name NAME and length LEN, bound to |
+| the C function given in BP.  MODE is SYMBOL_INSERT or                  |
+| SYMBOL_PUSHDEF.                                                |
+`----------------------------------------------------------------*/
 
 void
-define_builtin (const char *name, const builtin *bp, symbol_lookup mode)
+define_builtin (const char *name, size_t len, const builtin *bp,
+               symbol_lookup mode)
 {
   symbol *sym;
 
-  sym = lookup_symbol (name, mode);
+  sym = lookup_symbol (name, len, mode);
   SYMBOL_TYPE (sym) = TOKEN_FUNC;
   SYMBOL_MACRO_ARGS (sym) = bp->groks_macro_args;
   SYMBOL_BLIND_NO_ARGS (sym) = bp->blind_if_no_args;
@@ -443,7 +444,7 @@ define_user_macro (const char *name, size_t name_len, const 
char *text,
   symbol *s;
   char *defn = xstrdup (text ? text : "");
 
-  s = lookup_symbol (name, mode);
+  s = lookup_symbol (name, name_len, mode);
   if (SYMBOL_TYPE (s) == TOKEN_TEXT)
     free (SYMBOL_TEXT (s));
 
@@ -468,16 +469,20 @@ define_user_macro (const char *name, size_t name_len, 
const char *text,
          else
            {
              offset = regs->end[0];
-             m4_warn (0, NULL,
-                      _("definition of `%s' contains sequence `%.*s'"),
-                      name, (int) (regs->end[0] - regs->start[0]),
-                      defn + regs->start[0]);
+             /* Safe to use slot 1 since we don't pass a macro name
+                to m4_warn.  */
+             m4_warn (0, NULL, _("definition of %s contains sequence %s"),
+                      quotearg_style_mem (locale_quoting_style, name,
+                                          name_len),
+                      quotearg_n_style_mem (1, locale_quoting_style,
+                                            defn + regs->start[0],
+                                            regs->end[0] - regs->start[0]));
            }
        }
       if (offset == -2)
        m4_warn (0, NULL,
-                _("problem checking --warn-macro-sequence for macro `%s'"),
-                name);
+                _("problem checking --warn-macro-sequence for macro %s"),
+                quotearg_style_mem (locale_quoting_style, name, name_len));
     }
 }
 
@@ -495,14 +500,15 @@ builtin_init (void)
   for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
     if (!no_gnu_extensions || !bp->gnu_extension)
       {
+       size_t len = strlen (bp->name);
        if (prefix_all_builtins)
          {
            string = xasprintf ("m4_%s", bp->name);
-           define_builtin (string, bp, SYMBOL_INSERT);
+           define_builtin (string, len + 3, bp, SYMBOL_INSERT);
            free (string);
          }
        else
-         define_builtin (bp->name, bp, SYMBOL_INSERT);
+         define_builtin (bp->name, len, bp, SYMBOL_INSERT);
       }
 
   for (pp = &predefined_tab[0]; pp->func != NULL; pp++)
@@ -530,7 +536,7 @@ builtin_init (void)
 `------------------------------------------------------------------*/
 
 bool
-bad_argc (const char *name, int argc, unsigned int min, unsigned int max)
+bad_argc (const call_info *name, int argc, unsigned int min, unsigned int max)
 {
   if (argc - 1 < min)
     {
@@ -549,7 +555,7 @@ bad_argc (const char *name, int argc, unsigned int min, 
unsigned int max)
 `-------------------------------------------------------------------*/
 
 static bool
-numeric_arg (const char *name, const char *arg, int *valuep)
+numeric_arg (const call_info *name, const char *arg, int *valuep)
 {
   char *endp;
 
@@ -657,7 +663,7 @@ static void
 define_macro (int argc, macro_arguments *argv, symbol_lookup mode)
 {
   const builtin *bp;
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
 
   if (bad_argc (me, argc, 1, 2))
     return;
@@ -688,7 +694,7 @@ define_macro (int argc, macro_arguments *argv, 
symbol_lookup mode)
       if (bp == NULL)
        return;
       else
-       define_builtin (ARG (1), bp, mode);
+       define_builtin (ARG (1), ARG_LEN (1), bp, mode);
       break;
 
     default:
@@ -706,7 +712,7 @@ m4_define (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_undefine (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int i;
   if (bad_argc (me, argc, 1, -1))
     return;
@@ -714,7 +720,7 @@ m4_undefine (struct obstack *obs, int argc, macro_arguments 
*argv)
     if (arg_type (argv, i) != TOKEN_TEXT)
       m4_warn (0, me, _("invalid macro name ignored"));
     else
-      lookup_symbol (ARG (i), SYMBOL_DELETE);
+      lookup_symbol (ARG (i), ARG_LEN (i), SYMBOL_DELETE);
 }
 
 static void
@@ -726,7 +732,7 @@ m4_pushdef (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_popdef (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int i;
   if (bad_argc (me, argc, 1, -1))
     return;
@@ -734,7 +740,7 @@ m4_popdef (struct obstack *obs, int argc, macro_arguments 
*argv)
     if (arg_type (argv, i) != TOKEN_TEXT)
       m4_warn (0, me, _("invalid macro name ignored"));
     else
-      lookup_symbol (ARG (i), SYMBOL_POPDEF);
+      lookup_symbol (ARG (i), ARG_LEN (i), SYMBOL_POPDEF);
 }
 
 /*---------------------.
@@ -744,7 +750,7 @@ m4_popdef (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_ifdef (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   symbol *s;
 
   if (bad_argc (me, argc, 2, 3))
@@ -755,14 +761,14 @@ m4_ifdef (struct obstack *obs, int argc, macro_arguments 
*argv)
       push_arg (obs, argv, 3);
       return;
     }
-  s = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
+  s = lookup_symbol (ARG (1), ARG_LEN (1), SYMBOL_LOOKUP);
   push_arg (obs, argv, (s && SYMBOL_TYPE (s) != TOKEN_VOID) ? 2 : 3);
 }
 
 static void
 m4_ifelse (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int i;
 
   if (argc == 2 || bad_argc (me, argc, 3, -1))
@@ -832,8 +838,15 @@ dump_symbol (symbol *sym, void *arg)
 static int
 dumpdef_cmp (const void *s1, const void *s2)
 {
-  return strcmp (SYMBOL_NAME (* (symbol *const *) s1),
-                SYMBOL_NAME (* (symbol *const *) s2));
+  const symbol *sym1 = *(const symbol **) s1;
+  const symbol *sym2 = *(const symbol **) s2;
+  size_t len1 = SYMBOL_NAME_LEN (sym1);
+  size_t len2 = SYMBOL_NAME_LEN (sym2);
+  int result = memcmp (SYMBOL_NAME (sym1), SYMBOL_NAME (sym2),
+                      len1 < len2 ? len1 : len2);
+  if (!result)
+    result = len1 < len2 ? -1 : len2 < len1;
+  return result;
 }
 
 /*-------------------------------------------------------------------------.
@@ -844,12 +857,16 @@ dumpdef_cmp (const void *s1, const void *s2)
 static void
 m4_dumpdef (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   symbol *s;
   int i;
   struct dump_symbol_data data;
   const builtin *bp;
 
+  /* If there's no debug stream to dump to, skip all of this work.  */
+  if (!debug)
+    return;
+
   data.obs = obs;
   data.base = (symbol **) obstack_base (obs);
   data.size = 0;
@@ -867,23 +884,27 @@ m4_dumpdef (struct obstack *obs, int argc, 
macro_arguments *argv)
              m4_warn (0, me, _("invalid macro name ignored"));
              continue;
            }
-         s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
+         s = lookup_symbol (ARG (i), ARG_LEN (i), SYMBOL_LOOKUP);
          if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID)
            dump_symbol (s, &data);
          else
-           m4_warn (0, me, _("undefined macro `%s'"), ARG (i));
+           m4_warn (0, me, _("undefined macro %s"),
+                    quotearg_style_mem (locale_quoting_style, ARG (i),
+                                        ARG_LEN (i)));
        }
     }
 
   /* Make table of symbols invisible to expand_macro ().  */
-
   obstack_finish (obs);
 
   qsort (data.base, data.size, sizeof (symbol *), dumpdef_cmp);
-
   for (; data.size > 0; --data.size, data.base++)
     {
-      DEBUG_PRINT1 ("%s:\t", SYMBOL_NAME (data.base[0]));
+      /* TODO - add debugmode(b) option to control quoting style?  */
+      fwrite (SYMBOL_NAME (data.base[0]), 1, SYMBOL_NAME_LEN (data.base[0]),
+             debug);
+      fputc (':', debug);
+      fputc ('\t', debug);
 
       switch (SYMBOL_TYPE (data.base[0]))
        {
@@ -924,7 +945,7 @@ m4_dumpdef (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_builtin (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   const builtin *bp;
   const char *name;
 
@@ -939,7 +960,8 @@ m4_builtin (struct obstack *obs, int argc, macro_arguments 
*argv)
   name = ARG (1);
   bp = find_builtin_by_name (name);
   if (bp->func == m4_placeholder)
-    m4_warn (0, me, _("undefined builtin `%s'"), name);
+    m4_warn (0, me, _("undefined builtin %s"),
+            quotearg_style_mem (locale_quoting_style, name, ARG_LEN (1)));
   else
     {
       macro_arguments *new_argv = make_argv_ref (argv, name, ARG_LEN (1),
@@ -958,9 +980,10 @@ m4_builtin (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_indir (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   symbol *s;
   const char *name;
+  size_t len;
 
   if (bad_argc (me, argc, 1, -1))
     return;
@@ -971,12 +994,14 @@ m4_indir (struct obstack *obs, int argc, macro_arguments 
*argv)
     }
 
   name = ARG (1);
-  s = lookup_symbol (name, SYMBOL_LOOKUP);
+  len = ARG_LEN (1);
+  s = lookup_symbol (name, len, SYMBOL_LOOKUP);
   if (s == NULL || SYMBOL_TYPE (s) == TOKEN_VOID)
-    m4_warn (0, me, _("undefined macro `%s'"), name);
+    m4_warn (0, me, _("undefined macro %s"),
+            quotearg_style_mem (locale_quoting_style, name, len));
   else
     {
-      macro_arguments *new_argv = make_argv_ref (argv, name, ARG_LEN (1),
+      macro_arguments *new_argv = make_argv_ref (argv, name, len,
                                                 !SYMBOL_MACRO_ARGS (s),
                                                 SYMBOL_TRACED (s));
       trace_prepre (arg_info (new_argv));
@@ -993,7 +1018,7 @@ m4_indir (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_defn (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   symbol *s;
   builtin_func *b;
   int i;
@@ -1008,10 +1033,12 @@ m4_defn (struct obstack *obs, int argc, macro_arguments 
*argv)
          m4_warn (0, me, _("invalid macro name ignored"));
          continue;
        }
-      s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
+      s = lookup_symbol (ARG (i), ARG_LEN (i), SYMBOL_LOOKUP);
       if (s == NULL)
        {
-         m4_warn (0, me, _("undefined macro `%s'"), ARG (i));
+         m4_warn (0, me, _("undefined macro %s"),
+                  quotearg_style_mem (locale_quoting_style, ARG (i),
+                                      ARG_LEN (i)));
          continue;
        }
 
@@ -1080,7 +1107,7 @@ static int sysval;
 static void
 m4_syscmd (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  if (bad_argc (ARG (0), argc, 1, 1))
+  if (bad_argc (arg_info (argv), argc, 1, 1))
     {
       /* The empty command is successful.  */
       sysval = 0;
@@ -1104,7 +1131,7 @@ m4_syscmd (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_esyscmd (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   FILE *pin;
   int ch;
 
@@ -1147,7 +1174,7 @@ m4_sysval (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_eval (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int32_t value = 0;
   int radix = 10;
   int min = 1;
@@ -1211,7 +1238,7 @@ m4_eval (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_incr (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int value;
 
   if (bad_argc (me, argc, 1, 1))
@@ -1226,7 +1253,7 @@ m4_incr (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_decr (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int value;
 
   if (bad_argc (me, argc, 1, 1))
@@ -1249,7 +1276,7 @@ m4_decr (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_divert (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int i = 0;
 
   bad_argc (me, argc, 0, 1);
@@ -1266,7 +1293,7 @@ m4_divert (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_divnum (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  bad_argc (ARG (0), argc, 0, 0);
+  bad_argc (arg_info (argv), argc, 0, 0);
   shipout_int (obs, current_diversion);
 }
 
@@ -1280,7 +1307,7 @@ m4_divnum (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_undivert (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int i;
   int file;
   FILE *fp;
@@ -1324,7 +1351,7 @@ m4_undivert (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_dnl (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
 
   bad_argc (me, argc, 0, 0);
   skip_line (me);
@@ -1338,7 +1365,7 @@ m4_dnl (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_shift (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  if (bad_argc (ARG (0), argc, 1, -1))
+  if (bad_argc (arg_info (argv), argc, 1, -1))
     return;
   push_args (obs, argv, true, true);
 }
@@ -1350,7 +1377,7 @@ m4_shift (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_changequote (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  bad_argc (ARG (0), argc, 0, 2);
+  bad_argc (arg_info (argv), argc, 0, 2);
 
   /* Explicit NULL distinguishes between empty and missing argument.  */
   set_quotes ((argc >= 2) ? ARG (1) : NULL,
@@ -1365,7 +1392,7 @@ m4_changequote (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_changecom (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  bad_argc (ARG (0), argc, 0, 2);
+  bad_argc (arg_info (argv), argc, 0, 2);
 
   /* Explicit NULL distinguishes between empty and missing argument.  */
   set_comment ((argc >= 2) ? ARG (1) : NULL,
@@ -1382,7 +1409,7 @@ m4_changecom (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_changeword (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
 
   if (bad_argc (me, argc, 1, 1))
     return;
@@ -1403,7 +1430,7 @@ m4_changeword (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 include (int argc, macro_arguments *argv, bool silent)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   FILE *fp;
   char *name;
 
@@ -1453,7 +1480,7 @@ m4_sinclude (struct obstack *obs, int argc, 
macro_arguments *argv)
    securely create the file, and place the quoted new file name on
    OBS.  Report errors on behalf of ME.  */
 static void
-mkstemp_helper (struct obstack *obs, const char *me, const char *pattern,
+mkstemp_helper (struct obstack *obs, const call_info *me, const char *pattern,
                size_t len)
 {
   int fd;
@@ -1490,7 +1517,7 @@ mkstemp_helper (struct obstack *obs, const char *me, 
const char *pattern,
 static void
 m4_maketemp (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
 
   if (bad_argc (me, argc, 1, 1))
     return;
@@ -1535,7 +1562,7 @@ m4_maketemp (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_mkstemp (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
 
   if (bad_argc (me, argc, 1, 1))
     return;
@@ -1551,7 +1578,7 @@ m4_errprint (struct obstack *obs, int argc, 
macro_arguments *argv)
 {
   size_t len;
 
-  if (bad_argc (ARG (0), argc, 1, -1))
+  if (bad_argc (arg_info (argv), argc, 1, -1))
     return;
   arg_print (obs, argv, 1, NULL, true, NULL, " ", NULL, false);
   debug_flush_files ();
@@ -1565,7 +1592,7 @@ m4_errprint (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4___file__ (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  bad_argc (ARG (0), argc, 0, 0);
+  bad_argc (arg_info (argv), argc, 0, 0);
   obstack_grow (obs, curr_quote.str1, curr_quote.len1);
   obstack_grow (obs, current_file, strlen (current_file));
   obstack_grow (obs, curr_quote.str2, curr_quote.len2);
@@ -1574,14 +1601,14 @@ m4___file__ (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4___line__ (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  bad_argc (ARG (0), argc, 0, 0);
+  bad_argc (arg_info (argv), argc, 0, 0);
   shipout_int (obs, current_line);
 }
 
 static void
 m4___program__ (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  bad_argc (ARG (0), argc, 0, 0);
+  bad_argc (arg_info (argv), argc, 0, 0);
   obstack_grow (obs, curr_quote.str1, curr_quote.len1);
   obstack_grow (obs, program_name, strlen (program_name));
   obstack_grow (obs, curr_quote.str2, curr_quote.len2);
@@ -1599,7 +1626,7 @@ m4___program__ (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_m4exit (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int exit_code = EXIT_SUCCESS;
 
   /* Warn on bad arguments, but still exit.  */
@@ -1632,7 +1659,7 @@ m4_m4exit (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_m4wrap (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  if (bad_argc (ARG (0), argc, 1, -1))
+  if (bad_argc (arg_info (argv), argc, 1, -1))
     return;
   wrap_args (argv);
 }
@@ -1654,13 +1681,13 @@ set_trace (symbol *sym, void *data)
   SYMBOL_TRACED (sym) = data != NULL;
   /* Remove placeholder from table if macro is undefined and untraced.  */
   if (SYMBOL_TYPE (sym) == TOKEN_VOID && data == NULL)
-    lookup_symbol (SYMBOL_NAME (sym), SYMBOL_POPDEF);
+    lookup_symbol (SYMBOL_NAME (sym), SYMBOL_NAME_LEN (sym), SYMBOL_POPDEF);
 }
 
 static void
 m4_traceon (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   symbol *s;
   int i;
 
@@ -1674,9 +1701,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_LOOKUP);
+       s = lookup_symbol (ARG (i), ARG_LEN (i), SYMBOL_LOOKUP);
        if (!s)
-         s = lookup_symbol (ARG (i), SYMBOL_INSERT);
+         s = lookup_symbol (ARG (i), ARG_LEN (i), SYMBOL_INSERT);
        set_trace (s, obs);
       }
 }
@@ -1688,7 +1715,7 @@ m4_traceon (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_traceoff (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   symbol *s;
   int i;
 
@@ -1702,7 +1729,7 @@ m4_traceoff (struct obstack *obs, int argc, 
macro_arguments *argv)
            m4_warn (0, me, _("invalid macro name ignored"));
            continue;
          }
-       s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
+       s = lookup_symbol (ARG (i), ARG_LEN (i), SYMBOL_LOOKUP);
        if (s != NULL)
          set_trace (s, NULL);
       }
@@ -1717,7 +1744,7 @@ m4_traceoff (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_debugmode (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   const char *str = ARG (1);
   int new_debug_level;
   int change_flag;
@@ -1769,7 +1796,7 @@ m4_debugmode (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_debugfile (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
 
   bad_argc (me, argc, 0, 1);
 
@@ -1790,7 +1817,7 @@ m4_debugfile (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_len (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  if (bad_argc (ARG (0), argc, 1, 1))
+  if (bad_argc (arg_info (argv), argc, 1, 1))
     return;
   shipout_int (obs, ARG_LEN (1));
 }
@@ -1808,7 +1835,7 @@ m4_index (struct obstack *obs, int argc, macro_arguments 
*argv)
   const char *result = NULL;
   int retval = -1;
 
-  if (bad_argc (ARG (0), argc, 2, 2))
+  if (bad_argc (arg_info (argv), argc, 2, 2))
     {
       /* builtin(`index') is blank, but index(`abc') is 0.  */
       if (argc == 2)
@@ -1838,7 +1865,7 @@ m4_index (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_substr (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   int start = 0;
   int length;
   int avail;
@@ -1929,7 +1956,7 @@ m4_translit (struct obstack *obs, int argc, 
macro_arguments *argv)
   char found[UCHAR_MAX + 1] = {0};
   unsigned char ch;
 
-  if (bad_argc (ARG (0), argc, 2, 3))
+  if (bad_argc (arg_info (argv), argc, 2, 3))
     {
       /* builtin(`translit') is blank, but translit(`abc') is abc.  */
       if (argc == 2)
@@ -1981,7 +2008,7 @@ m4_translit (struct obstack *obs, int argc, 
macro_arguments *argv)
 static void
 m4_format (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  if (bad_argc (ARG (0), argc, 1, -1))
+  if (bad_argc (arg_info (argv), argc, 1, -1))
     return;
   expand_format (obs, argc, argv);
 }
@@ -1998,7 +2025,7 @@ m4_format (struct obstack *obs, int argc, macro_arguments 
*argv)
 static int substitute_warned = 0;
 
 static void
-substitute (struct obstack *obs, const char *me, const char *victim,
+substitute (struct obstack *obs, const call_info *me, const char *victim,
            const char *repl, struct re_registers *regs)
 {
   int ch;
@@ -2078,7 +2105,7 @@ init_pattern_buffer (struct re_pattern_buffer *buf, 
struct re_registers *regs)
 static void
 m4_regexp (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   const char *victim;          /* first argument */
   const char *regexp;          /* regular expression */
   const char *repl;            /* replacement string */
@@ -2148,7 +2175,7 @@ m4_regexp (struct obstack *obs, int argc, macro_arguments 
*argv)
 static void
 m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);
+  const call_info *me = arg_info (argv);
   const char *victim;          /* first argument */
   const char *regexp;          /* regular expression */
   const char *repl;
diff --git a/src/debug.c b/src/debug.c
index 0298fc8..2b2388f 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -30,7 +30,7 @@ FILE *debug = NULL;
 /* Obstack for trace messages.  */
 static struct obstack trace;
 
-static void debug_set_file (const char *, FILE *);
+static void debug_set_file (const call_info *, FILE *);
 
 /*----------------------------------.
 | Initialize the debugging module.  |
@@ -121,7 +121,7 @@ debug_decode (const char *opts)
 `-----------------------------------------------------------------*/
 
 static void
-debug_set_file (const char *caller, FILE *fp)
+debug_set_file (const call_info *caller, FILE *fp)
 {
   struct stat stdout_stat, debug_stat;
 
@@ -189,7 +189,7 @@ debug_flush_files (void)
 `-------------------------------------------------------------------*/
 
 bool
-debug_set_output (const char *caller, const char *name)
+debug_set_output (const call_info *caller, const char *name)
 {
   FILE *fp;
 
@@ -313,13 +313,16 @@ trace_header (const call_info *info)
 static void
 trace_flush (unsigned int start)
 {
-  char *line;
+  char *base = (char *) obstack_base (&trace);
+  size_t len = obstack_object_size (&trace);
 
-  obstack_1grow (&trace, '\0');
-  line = (char *) obstack_base (&trace);
-  DEBUG_PRINT1 ("%s\n", &line[start]);
-  start -= obstack_object_size (&trace);
-  obstack_blank (&trace, start);
+  if (debug)
+    {
+      /* TODO - quote nonprintable characters if debug is tty?  */
+      fwrite (&base[start], 1, len - start, debug);
+      fputc ('\n', debug);
+    }
+  obstack_blank (&trace, start - len);
 }
 
 /*-------------------------------------------------------------------.
@@ -333,7 +336,8 @@ trace_prepre (const call_info *info)
   if (info->trace && (info->debug_level & DEBUG_TRACE_CALL))
     {
       unsigned int start = trace_header (info);
-      trace_format ("%s ...", info->name);
+      obstack_grow (&trace, info->name, info->name_len);
+      obstack_grow (&trace, " ...", 4);
       trace_flush (start);
     }
 }
@@ -353,7 +357,7 @@ trace_pre (macro_arguments *argv)
   unsigned int start = trace_header (info);
 
   assert (info->trace);
-  trace_format ("%s", info->name);
+  obstack_grow (&trace, ARG (0), ARG_LEN (0));
   if (1 < arg_argc (argv) && (trace_level & DEBUG_TRACE_ARGS))
     {
       size_t len = max_debug_argument_length;
diff --git a/src/eval.c b/src/eval.c
index fdb50b7..e2e600b 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
 
-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006, 2007
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006, 2007, 2008
    Free Software Foundation, Inc.
 
    This file is part of GNU M4.
@@ -62,19 +62,19 @@ typedef enum eval_error
   }
 eval_error;
 
-static eval_error logical_or_term (const char *, eval_token, int32_t *);
-static eval_error logical_and_term (const char *, eval_token, int32_t *);
-static eval_error or_term (const char *, eval_token, int32_t *);
-static eval_error xor_term (const char *, eval_token, int32_t *);
-static eval_error and_term (const char *, eval_token, int32_t *);
-static eval_error equality_term (const char *, eval_token, int32_t *);
-static eval_error cmp_term (const char *, eval_token, int32_t *);
-static eval_error shift_term (const char *, eval_token, int32_t *);
-static eval_error add_term (const char *, eval_token, int32_t *);
-static eval_error mult_term (const char *, eval_token, int32_t *);
-static eval_error exp_term (const char *, eval_token, int32_t *);
-static eval_error unary_term (const char *, eval_token, int32_t *);
-static eval_error simple_term (const char *, eval_token, int32_t *);
+static eval_error logical_or_term (const call_info *, eval_token, int32_t *);
+static eval_error logical_and_term (const call_info *, eval_token, int32_t *);
+static eval_error or_term (const call_info *, eval_token, int32_t *);
+static eval_error xor_term (const call_info *, eval_token, int32_t *);
+static eval_error and_term (const call_info *, eval_token, int32_t *);
+static eval_error equality_term (const call_info *, eval_token, int32_t *);
+static eval_error cmp_term (const call_info *, eval_token, int32_t *);
+static eval_error shift_term (const call_info *, eval_token, int32_t *);
+static eval_error add_term (const call_info *, eval_token, int32_t *);
+static eval_error mult_term (const call_info *, eval_token, int32_t *);
+static eval_error exp_term (const call_info *, eval_token, int32_t *);
+static eval_error unary_term (const call_info *, eval_token, int32_t *);
+static eval_error simple_term (const call_info *, eval_token, int32_t *);
 
 /*--------------------.
 | Lexical functions.  |
@@ -287,7 +287,7 @@ eval_lex (int32_t *val)
 `---------------------------------------*/
 
 bool
-evaluate (const char *me, const char *expr, int32_t *val)
+evaluate (const call_info *me, const char *expr, int32_t *val)
 {
   eval_token et;
   eval_error err;
@@ -355,7 +355,7 @@ evaluate (const char *me, const char *expr, int32_t *val)
 `---------------------------*/
 
 static eval_error
-logical_or_term (const char *me, eval_token et, int32_t *v1)
+logical_or_term (const call_info *me, eval_token et, int32_t *v1)
 {
   int32_t v2;
   eval_error er;
@@ -386,7 +386,7 @@ logical_or_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-logical_and_term (const char *me, eval_token et, int32_t *v1)
+logical_and_term (const call_info *me, eval_token et, int32_t *v1)
 {
   int32_t v2;
   eval_error er;
@@ -417,7 +417,7 @@ logical_and_term (const char *me, eval_token et, int32_t 
*v1)
 }
 
 static eval_error
-or_term (const char *me, eval_token et, int32_t *v1)
+or_term (const call_info *me, eval_token et, int32_t *v1)
 {
   int32_t v2;
   eval_error er;
@@ -444,7 +444,7 @@ or_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-xor_term (const char *me, eval_token et, int32_t *v1)
+xor_term (const call_info *me, eval_token et, int32_t *v1)
 {
   int32_t v2;
   eval_error er;
@@ -471,7 +471,7 @@ xor_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-and_term (const char *me, eval_token et, int32_t *v1)
+and_term (const call_info *me, eval_token et, int32_t *v1)
 {
   int32_t v2;
   eval_error er;
@@ -498,7 +498,7 @@ and_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-equality_term (const char *me, eval_token et, int32_t *v1)
+equality_term (const call_info *me, eval_token et, int32_t *v1)
 {
   eval_token op;
   int32_t v2;
@@ -534,7 +534,7 @@ equality_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-cmp_term (const char *me, eval_token et, int32_t *v1)
+cmp_term (const call_info *me, eval_token et, int32_t *v1)
 {
   eval_token op;
   int32_t v2;
@@ -585,7 +585,7 @@ cmp_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-shift_term (const char *me, eval_token et, int32_t *v1)
+shift_term (const call_info *me, eval_token et, int32_t *v1)
 {
   eval_token op;
   int32_t v2;
@@ -638,7 +638,7 @@ shift_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-add_term (const char *me, eval_token et, int32_t *v1)
+add_term (const call_info *me, eval_token et, int32_t *v1)
 {
   eval_token op;
   int32_t v2;
@@ -673,7 +673,7 @@ add_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-mult_term (const char *me, eval_token et, int32_t *v1)
+mult_term (const call_info *me, eval_token et, int32_t *v1)
 {
   eval_token op;
   int32_t v2;
@@ -734,7 +734,7 @@ mult_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-exp_term (const char *me, eval_token et, int32_t *v1)
+exp_term (const call_info *me, eval_token et, int32_t *v1)
 {
   uint32_t result;
   int32_t v2;
@@ -773,7 +773,7 @@ exp_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-unary_term (const char *me, eval_token et, int32_t *v1)
+unary_term (const call_info *me, eval_token et, int32_t *v1)
 {
   eval_token et2 = et;
   eval_error er;
@@ -805,7 +805,7 @@ unary_term (const char *me, eval_token et, int32_t *v1)
 }
 
 static eval_error
-simple_term (const char *me, eval_token et, int32_t *v1)
+simple_term (const call_info *me, eval_token et, int32_t *v1)
 {
   int32_t v2;
   eval_error er;
diff --git a/src/format.c b/src/format.c
index 717a070..c783d11 100644
--- a/src/format.c
+++ b/src/format.c
@@ -29,7 +29,7 @@
 
 /* Parse STR as an integer, reporting warnings on behalf of ME.  */
 static int
-arg_int (const char *me, const char *str)
+arg_int (const call_info *me, const char *str)
 {
   char *endp;
   long value;
@@ -54,7 +54,7 @@ arg_int (const char *me, const char *str)
 
 /* Parse STR as a long, reporting warnings on behalf of ME.  */
 static long
-arg_long (const char *me, const char *str)
+arg_long (const call_info *me, const char *str)
 {
   char *endp;
   long value;
@@ -79,7 +79,7 @@ arg_long (const char *me, const char *str)
 
 /* Parse STR as a double, reporting warnings on behalf of ME.  */
 static double
-arg_double (const char *me, const char *str)
+arg_double (const call_info *me, const char *str)
 {
   char *endp;
   double value;
@@ -124,7 +124,7 @@ arg_double (const char *me, const char *str)
 void
 expand_format (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = ARG (0);            /* Macro name.  */
+  const call_info *me = arg_info (argv);/* Macro name.  */
   const char *f;                       /* Format control string.  */
   const char *fmt;                     /* Position within f.  */
   char fstart[] = "%'+- 0#*.*hhd";     /* Current format spec.  */
diff --git a/src/freeze.c b/src/freeze.c
index c88a266..e67bcc8 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -98,9 +98,9 @@ produce_frozen_state (const char *name)
            {
            case TOKEN_TEXT:
              xfprintf (file, "T%d,%d\n",
-                       (int) strlen (SYMBOL_NAME (sym)),
+                       (int) SYMBOL_NAME_LEN (sym),
                        (int) strlen (SYMBOL_TEXT (sym)));
-             fputs (SYMBOL_NAME (sym), file);
+             fwrite (SYMBOL_NAME (sym), 1, SYMBOL_NAME_LEN (sym), file);
              fputs (SYMBOL_TEXT (sym), file);
              fputc ('\n', file);
              break;
@@ -113,9 +113,9 @@ produce_frozen_state (const char *name)
                  abort ();
                }
              xfprintf (file, "F%d,%d\n",
-                       (int) strlen (SYMBOL_NAME (sym)),
+                       (int) SYMBOL_NAME_LEN (sym),
                        (int) strlen (bp->name));
-             fputs (SYMBOL_NAME (sym), file);
+             fwrite (SYMBOL_NAME (sym), 1, SYMBOL_NAME_LEN (sym), file);
              fputs (bp->name, file);
              fputc ('\n', file);
              break;
@@ -346,7 +346,7 @@ reload_frozen_state (const char *name)
              /* Enter a macro having a builtin function as a definition.  */
 
              bp = find_builtin_by_name (string[1]);
-             define_builtin (string[0], bp, SYMBOL_PUSHDEF);
+             define_builtin (string[0], number[0], bp, SYMBOL_PUSHDEF);
              break;
 
            case 'T':
diff --git a/src/input.c b/src/input.c
index c1f609b..acbc370 100644
--- a/src/input.c
+++ b/src/input.c
@@ -322,7 +322,7 @@ push_macro (struct obstack *obs, builtin_func *func)
 `--------------------------------------------------------------*/
 
 struct obstack *
-push_string_init (void)
+push_string_init (const char *file, int line)
 {
   /* Free any memory occupied by completely parsed strings.  */
   assert (next == NULL);
@@ -331,8 +331,8 @@ push_string_init (void)
   /* Reserve the next location on the obstack.  */
   next = (input_block *) obstack_alloc (current_input, sizeof *next);
   next->type = INPUT_STRING;
-  next->file = current_file;
-  next->line = current_line;
+  next->file = file;
+  next->line = line;
   next->u.u_s.len = 0;
 
   return current_input;
@@ -544,7 +544,7 @@ push_string_finish (void)
 `--------------------------------------------------------------*/
 
 struct obstack *
-push_wrapup_init (token_chain ***end)
+push_wrapup_init (const call_info *caller, token_chain ***end)
 {
   input_block *i;
   token_chain *chain;
@@ -560,8 +560,8 @@ push_wrapup_init (token_chain ***end)
     {
       i = (input_block *) obstack_alloc (wrapup_stack, sizeof *i);
       i->prev = wsp;
-      i->file = current_file;
-      i->line = current_line;
+      i->file = caller->file;
+      i->line = caller->line;
       i->type = INPUT_CHAIN;
       i->u.u_c.chain = i->u.u_c.end = NULL;
       wsp = i;
@@ -575,8 +575,8 @@ push_wrapup_init (token_chain ***end)
   chain->next = NULL;
   chain->type = CHAIN_LOC;
   chain->quote_age = 0;
-  chain->u.u_l.file = current_file;
-  chain->u.u_l.line = current_line;
+  chain->u.u_l.file = caller->file;
+  chain->u.u_l.line = caller->line;
   *end = &i->u.u_c.end;
   return wrapup_stack;
 }
@@ -859,7 +859,7 @@ peek_input (bool allow_argv)
                  /* Rather than directly parse argv here, we push
                     another input block containing the next unparsed
                     argument from argv.  */
-                 push_string_init ();
+                 push_string_init (block->file, block->line);
                  push_arg_quote (current_input, chain->u.u_a.argv,
                                  chain->u.u_a.index,
                                  quote_cache (NULL, chain->quote_age,
@@ -997,7 +997,7 @@ next_char_1 (bool allow_quote, bool allow_argv)
                  /* Rather than directly parse argv here, we push
                     another input block containing the next unparsed
                     argument from argv.  */
-                 push_string_init ();
+                 push_string_init (isp->file, isp->line);
                  push_arg_quote (current_input, chain->u.u_a.argv,
                                  chain->u.u_a.index,
                                  quote_cache (NULL, chain->quote_age,
@@ -1040,26 +1040,14 @@ next_char_1 (bool allow_quote, bool allow_argv)
 `-------------------------------------------------------------------*/
 
 void
-skip_line (const char *name)
+skip_line (const call_info *name)
 {
   int ch;
-  const char *file = current_file;
-  int line = current_line;
 
   while ((ch = next_char (false, false)) != CHAR_EOF && ch != '\n')
     ;
   if (ch == CHAR_EOF)
-    /* current_file changed to "" if we see CHAR_EOF, use the
-       previous value we stored earlier.  */
-    m4_warn_at_line (0, file, line, name,
-                    _("end of file treated as newline"));
-  /* On the rare occasion that dnl crosses include file boundaries
-     (either the input file did not end in a newline, or changeword
-     was used), calling next_char can update current_file and
-     current_line, and that update will be undone as we return to
-     expand_macro.  This informs next_char to fix things again.  */
-  if (file != current_file || line != current_line)
-    input_change = true;
+    m4_warn (0, name, _("end of file treated as newline"));
 }
 
 /*------------------------------------------------------------------.
@@ -1250,7 +1238,7 @@ match_input (const char *s, bool consume)
     }
 
   /* Failed or shouldn't consume, push back input.  */
-  push_string_init ();
+  push_string_init (current_file, current_line);
   obstack_grow (current_input, t, n);
   push_string_finish ();
   return result;
@@ -1396,7 +1384,7 @@ set_comment (const char *bc, const char *ec)
 `-------------------------------------------------------------------*/
 
 void
-set_word_regexp (const char *caller, const char *regexp)
+set_word_regexp (const call_info *caller, const char *regexp)
 {
   const char *msg;
   struct re_pattern_buffer new_word_regexp;
@@ -1586,7 +1574,7 @@ quote_cache (struct obstack *obs, unsigned int age, const 
string_pair *quotes)
 
 token_type
 next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv,
-           const char *caller)
+           const call_info *caller)
 {
   int ch;
   int quote_level;
@@ -1594,20 +1582,21 @@ next_token (token_data *td, int *line, struct obstack 
*obs, bool allow_argv,
 #ifdef ENABLE_CHANGEWORD
   char *orig_text = NULL;
 #endif /* ENABLE_CHANGEWORD */
-  const char *file;
-  int dummy;
+  const char *file = NULL;
   /* The obstack where token data is stored.  Generally token_stack,
      for tokens where argument collection might not use the literal
      token.  But for comments and strings, we can output directly into
      the argument collection obstack obs, if one was provided.  */
   struct obstack *obs_td = &token_stack;
-
   obstack_free (&token_stack, token_bottom);
-  if (!line)
-    line = &dummy;
 
   TOKEN_DATA_TYPE (td) = TOKEN_VOID;
   ch = next_char (false, allow_argv && current_quote_age);
+  if (line)
+    {
+      *line = current_line;
+      file = current_file;
+    }
   if (ch == CHAR_EOF)
     {
 #ifdef DEBUG_INPUT
@@ -1636,8 +1625,6 @@ next_token (token_data *td, int *line, struct obstack 
*obs, bool allow_argv,
       return TOKEN_ARGV;
     }
 
-  file = current_file;
-  *line = current_line;
   if (MATCH (ch, curr_comm.str1, true))
     {
       if (obs)
@@ -1647,10 +1634,17 @@ next_token (token_data *td, int *line, struct obstack 
*obs, bool allow_argv,
        {
          ch = next_char (false, false);
          if (ch == CHAR_EOF)
-           /* Current_file changed to "" if we see CHAR_EOF, use the
-              previous value we stored earlier.  */
-           m4_error_at_line (EXIT_FAILURE, 0, file, *line, caller,
-                             _("end of file in comment"));
+           {
+             /* Current_file changed to "" if we see CHAR_EOF, use
+                the previous value we stored earlier.  */
+             if (!caller)
+               {
+                 assert (line);
+                 current_line = *line;
+                 current_file = file;
+               }
+             m4_error (EXIT_FAILURE, 0, caller, _("end of file in comment"));
+           }
          if (ch == CHAR_MACRO)
            {
              init_macro_token (obs, obs ? td : NULL);
@@ -1699,8 +1693,10 @@ next_token (token_data *td, int *line, struct obstack 
*obs, bool allow_argv,
          next_char (false, false);
        }
 
+      TOKEN_DATA_ORIG_LEN (td) = obstack_object_size (&token_stack);
       obstack_1grow (&token_stack, '\0');
       orig_text = (char *) obstack_finish (&token_stack);
+      TOKEN_DATA_ORIG_TEXT (td) = orig_text;
 
       if (regs.start[1] != -1)
        obstack_grow (&token_stack, orig_text + regs.start[1],
@@ -1743,11 +1739,17 @@ next_token (token_data *td, int *line, struct obstack 
*obs, bool allow_argv,
        {
          ch = next_char (obs != NULL && current_quote_age, false);
          if (ch == CHAR_EOF)
-           /* Current_file changed to "" if we see CHAR_EOF, use
-              the previous value we stored earlier.  */
-           m4_error_at_line (EXIT_FAILURE, 0, file, *line, caller,
-                             _("end of file in string"));
-
+           {
+             /* Current_file changed to "" if we see CHAR_EOF, use
+                the previous value we stored earlier.  */
+             if (!caller)
+               {
+                 assert (line);
+                 current_line = *line;
+                 current_file = file;
+               }
+             m4_error (EXIT_FAILURE, 0, caller, _("end of file in string"));
+           }
          if (ch == CHAR_MACRO)
            init_macro_token (obs, obs ? td : NULL);
          else if (ch == CHAR_QUOTE)
@@ -1784,12 +1786,10 @@ next_token (token_data *td, int *line, struct obstack 
*obs, bool allow_argv,
        TOKEN_DATA_TEXT (td) = NULL;
       TOKEN_DATA_QUOTE_AGE (td) = current_quote_age;
 #ifdef ENABLE_CHANGEWORD
-      if (orig_text == NULL)
-       TOKEN_DATA_ORIG_TEXT (td) = TOKEN_DATA_TEXT (td);
-      else
+      if (!orig_text)
        {
-         TOKEN_DATA_ORIG_TEXT (td) = orig_text;
-         TOKEN_DATA_LEN (td) = strlen (orig_text);
+         TOKEN_DATA_ORIG_TEXT (td) = TOKEN_DATA_TEXT (td);
+         TOKEN_DATA_ORIG_LEN (td) = TOKEN_DATA_LEN (td);
        }
 #endif /* ENABLE_CHANGEWORD */
 #ifdef DEBUG_INPUT
@@ -1948,6 +1948,10 @@ print_token (const char *s, token_type t, token_data *td)
       xfprintf (stderr, "macro: %p\n", TOKEN_DATA_FUNC (td));
       break;
 
+    case TOKEN_ARGV:
+      xfprintf (stderr, "argv:");
+      break;
+
     case TOKEN_EOF:
       xfprintf (stderr, "eof\n");
       break;
@@ -1960,8 +1964,9 @@ lex_debug (void)
 {
   token_type t;
   token_data td;
+  int line;
 
-  while ((t = next_token (&td, NULL, NULL, false, "<debug>")) != TOKEN_EOF)
+  while ((t = next_token (&td, &line, NULL, false, NULL)) != TOKEN_EOF)
     print_token ("lex", t, &td);
 }
 #endif /* DEBUG_INPUT */
diff --git a/src/m4.c b/src/m4.c
index 84cb8e0..e2ef4bc 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -86,46 +86,53 @@ typedef struct macro_definition macro_definition;
 /*------------------------------------------------------------------.
 | Helper for all the error reporting, as a wrapper around          |
 | error_at_line.  Report error message based on FORMAT and ARGS, on |
-| behalf of MACRO, at the location FILE and LINE (but with no      |
-| location if LINE is 0).  If ERRNUM, decode the errno value that   |
-| caused the error.  If STATUS, exit immediately with that status.  |
-| If WARN, prepend 'Warning: '.                                            |
+| behalf of CALLER (if any), otherwise at the global current       |
+| location.  If ERRNUM, decode the errno value that caused the      |
+| error.  If STATUS, exit immediately with that status.  If WARN,   |
+| prepend 'Warning: '.                                             |
 `------------------------------------------------------------------*/
 
 static void
-m4_verror_at_line (bool warn, int status, int errnum, const char *file,
-                  int line, const char *macro, const char *format,
-                  va_list args)
+m4_verror_at_line (bool warn, int status, int errnum, const call_info *caller,
+                  const char *format, va_list args)
 {
   char *full = NULL;
   char *safe_macro = NULL;
+  const char *macro = caller ? caller->name : NULL;
+  size_t len = caller ? caller->name_len : 0;
+  const char *file = caller ? caller->file : current_file;
+  int line = caller ? caller->line : current_line;
 
   /* Sanitize MACRO, since we are turning around and using it in a
      format string.  The allocation is overly conservative, but
      problematic macro names only occur via indir or changeword.  */
-  if (macro && strchr (macro, '%'))
+  if (macro && memchr (macro, '%', len))
     {
-      char *p = safe_macro = xcharalloc (2 * strlen (macro) + 1);
-      do
+      char *p = safe_macro = xcharalloc (2 * len);
+      const char *end = macro + len;
+      while (macro != end)
        {
          if (*macro == '%')
-           *p++ = '%';
+           {
+             *p++ = '%';
+             len++;
+           }
          *p++ = *macro++;
        }
-      while (*macro);
-      *p = '\0';
     }
+  if (macro)
+    /* Use slot 1, so that the rest of the code can use the simpler
+       quotearg interface in slot 0.  */
+    macro = quotearg_n_mem (1, safe_macro ? safe_macro : macro, len);
   /* Prepend warning and the macro name, as needed.  But if that fails
      for non-memory reasons (unlikely), then still use the original
      format.  */
   if (warn && macro)
-    full = xasprintf (_("Warning: %s: %s"),
-                     quotearg (safe_macro ? safe_macro : macro), format);
+    full = xasprintf (_("Warning: %s: %s"), macro, format);
   else if (warn)
     full = xasprintf (_("Warning: %s"), format);
   else if (macro)
-    full = xasprintf (_("%s: %s"),
-                     quotearg (safe_macro ? safe_macro : macro), format);
+    full = xasprintf (_("%s: %s"), macro, format);
   verror_at_line (status, errnum, line ? file : NULL, line,
                  full ? full : format, args);
   free (full);
@@ -134,82 +141,40 @@ m4_verror_at_line (bool warn, int status, int errnum, 
const char *file,
     retcode = EXIT_FAILURE;
 }
 
-/*----------------------------------------------------------------.
-| Wrapper around error.  Report error message based on FORMAT and |
-| subsequent args, on behalf of MACRO, and the current input line |
-| (if any).  If ERRNUM, decode the errno value that caused the   |
-| error.  If STATUS, exit immediately with that status.                  |
-`----------------------------------------------------------------*/
-
-void
-m4_error (int status, int errnum, const char *macro, const char *format, ...)
-{
-  va_list args;
-  va_start (args, format);
-  if (status == EXIT_SUCCESS && warning_status)
-    status = EXIT_FAILURE;
-  m4_verror_at_line (false, status, errnum, current_file, current_line,
-                    macro, format, args);
-  va_end (args);
-}
-
-/*----------------------------------------------------------------.
-| Wrapper around error_at_line.  Report error message based on   |
-| FORMAT and subsequent args, on behalf of MACRO, at the location |
-| FILE and LINE (but with no location if LINE is 0).  If ERRNUM,  |
-| decode the errno value that caused the error.  If STATUS, exit  |
-| immediately with that status.                                          |
-`----------------------------------------------------------------*/
+/*------------------------------------------------------------------.
+| Wrapper around error.  Report error message based on FORMAT and   |
+| subsequent args, on behalf of CALLER (if any), and the current    |
+| input line (if any).  If ERRNUM, decode the errno value that      |
+| caused the error.  If STATUS, exit immediately with that status.  |
+`------------------------------------------------------------------*/
 
 void
-m4_error_at_line (int status, int errnum, const char *file, int line,
-                 const char *macro, const char *format, ...)
+m4_error (int status, int errnum, const call_info *caller,
+         const char *format, ...)
 {
   va_list args;
   va_start (args, format);
   if (status == EXIT_SUCCESS && warning_status)
     status = EXIT_FAILURE;
-  m4_verror_at_line (false, status, errnum, file, line, macro, format, args);
+  m4_verror_at_line (false, status, errnum, caller, format, args);
   va_end (args);
 }
 
 /*------------------------------------------------------------------.
 | Wrapper around error.  Report warning message based on FORMAT and |
-| subsequent args, on behalf of MACRO, and the current input line   |
-| (if any).  If ERRNUM, decode the errno value that caused the     |
-| warning.                                                         |
+| subsequent args, on behalf of CALLER (if any), and the current    |
+| input line (if any).  If ERRNUM, decode the errno value that      |
+| caused the warning.                                              |
 `------------------------------------------------------------------*/
 
 void
-m4_warn (int errnum, const char *macro, const char *format, ...)
-{
-  va_list args;
-  if (!suppress_warnings)
-    {
-      va_start (args, format);
-      m4_verror_at_line (true, warning_status, errnum, current_file,
-                        current_line, macro, format, args);
-      va_end (args);
-    }
-}
-
-/*----------------------------------------------------------------.
-| Wrapper around error_at_line.  Report warning message based on  |
-| FORMAT and subsequent args, on behalf of MACRO, at the location |
-| FILE and LINE (but with no location if LINE is 0).  If ERRNUM,  |
-| decode the errno value that caused the warning.                |
-`----------------------------------------------------------------*/
-
-void
-m4_warn_at_line (int errnum, const char *file, int line, const char *macro,
-                const char *format, ...)
+m4_warn (int errnum, const call_info *caller, const char *format, ...)
 {
   va_list args;
   if (!suppress_warnings)
     {
       va_start (args, format);
-      m4_verror_at_line (true, warning_status, errnum, file, line, macro,
-                        format, args);
+      m4_verror_at_line (true, warning_status, errnum, caller, format, args);
       va_end (args);
     }
 }
@@ -239,7 +204,7 @@ usage (int status)
 {
   if (status != EXIT_SUCCESS)
     xfprintf (stderr, _("Try `%s --help' for more information.\n"),
-              program_name);
+             program_name);
   else
     {
       xprintf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
@@ -652,23 +617,20 @@ main (int argc, char *const *argv, char *const *envp)
        {
        case 'D':
          {
-           /* defines->arg is read-only, so we need a copy.  */
-           char *macro_name = xstrdup (defines->arg);
-           char *macro_value = strchr (macro_name, '=');
-           if (macro_value)
-             *macro_value++ = '\0';
-           define_user_macro (macro_name, strlen (macro_name),
-                              macro_value, SYMBOL_INSERT);
-           free (macro_name);
+           const char *value = strchr (defines->arg, '=');
+           size_t len = value ? value - defines->arg : strlen (defines->arg);
+           define_user_macro (defines->arg, len, value ? value + 1 : "",
+                              SYMBOL_INSERT);
          }
          break;
 
        case 'U':
-         lookup_symbol (defines->arg, SYMBOL_DELETE);
+         lookup_symbol (defines->arg, strlen (defines->arg), SYMBOL_DELETE);
          break;
 
        case 't':
-         sym = lookup_symbol (defines->arg, SYMBOL_INSERT);
+         sym = lookup_symbol (defines->arg, strlen (defines->arg),
+                              SYMBOL_INSERT);
          SYMBOL_TRACED (sym) = true;
          break;
 
diff --git a/src/m4.h b/src/m4.h
index b2e55e5..d16d87a 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -50,6 +50,7 @@
 #include "vasnprintf.h"
 #include "verror.h"
 #include "xalloc.h"
+#include "xmemdup0.h"
 #include "xprintf.h"
 #include "xvasprintf.h"
 
@@ -150,19 +151,16 @@ struct call_info
   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.  */
 };
 typedef struct call_info call_info;
 
 extern int retcode;
 extern const char *program_name;
 
-void m4_error (int, int, const char *, const char *, ...)
+void m4_error (int, int, const call_info *, const char *, ...)
   M4_GNUC_PRINTF (4, 5);
-void m4_error_at_line (int, int, const char *, int, const char *,
-                      const char *, ...) M4_GNUC_PRINTF (6, 7);
-void m4_warn (int, const char *, const char *, ...) M4_GNUC_PRINTF (3, 4);
-void m4_warn_at_line (int, const char *, int, const char *,
-                     const char *, ...) M4_GNUC_PRINTF (5, 6);
+void m4_warn (int, const call_info *, const char *, ...) M4_GNUC_PRINTF (3, 4);
 
 #ifdef USE_STACKOVF
 void setup_stackovf_trap (char *const *, char *const *,
@@ -256,7 +254,7 @@ extern FILE *debug;
 void debug_init (void);
 int debug_decode (const char *);
 void debug_flush_files (void);
-bool debug_set_output (const char *, const char *);
+bool debug_set_output (const call_info *, const char *);
 void debug_message_prefix (void);
 
 void trace_prepre (const call_info *);
@@ -366,6 +364,7 @@ struct token_data
             portion that matched the () group to form a macro name.
             Otherwise, this field is unused.  */
          const char *original_text;
+         size_t original_len; /* Length of original_text.  */
 #endif
        }
       u_t;
@@ -391,6 +390,7 @@ struct token_data
 #define TOKEN_DATA_QUOTE_AGE(Td)       ((Td)->u.u_t.quote_age)
 #ifdef ENABLE_CHANGEWORD
 # define TOKEN_DATA_ORIG_TEXT(Td)      ((Td)->u.u_t.original_text)
+# define TOKEN_DATA_ORIG_LEN(Td)       ((Td)->u.u_t.original_len)
 #endif
 #define TOKEN_DATA_FUNC(Td)            ((Td)->u.func)
 
@@ -400,8 +400,8 @@ typedef enum token_data_type token_data_type;
 void input_init (void);
 token_type peek_token (void);
 token_type next_token (token_data *, int *, struct obstack *, bool,
-                      const char *);
-void skip_line (const char *);
+                      const call_info *);
+void skip_line (const call_info *);
 
 /* push back input */
 void make_text_link (struct obstack *, token_chain **, token_chain **);
@@ -409,10 +409,10 @@ void push_file (FILE *, const char *, bool);
 void append_macro (struct obstack *, builtin_func *, token_chain **,
                   token_chain **);
 void push_macro (struct obstack *, builtin_func *);
-struct obstack *push_string_init (void);
+struct obstack *push_string_init (const char *, int);
 bool push_token (token_data *, int, bool);
 void push_string_finish (void);
-struct obstack *push_wrapup_init (token_chain ***);
+struct obstack *push_wrapup_init (const call_info *, token_chain ***);
 void push_wrapup_finish (void);
 bool pop_wrapup (void);
 void input_print (struct obstack *);
@@ -433,7 +433,7 @@ extern string_pair curr_quote;
 void set_quotes (const char *, const char *);
 void set_comment (const char *, const char *);
 #ifdef ENABLE_CHANGEWORD
-void set_word_regexp (const char *, const char *);
+void set_word_regexp (const call_info *, const char *);
 #endif
 unsigned int quote_age (void);
 bool safe_quotes (void);
@@ -478,6 +478,7 @@ struct symbol
   int pending_expansions;
 
   char *name;
+  size_t len;
   token_data data;  /* Type should be only TOKEN_TEXT or TOKEN_FUNC.  */
 };
 
@@ -489,6 +490,7 @@ struct symbol
 #define SYMBOL_DELETED(S)      ((S)->deleted)
 #define SYMBOL_PENDING_EXPANSIONS(S) ((S)->pending_expansions)
 #define SYMBOL_NAME(S)         ((S)->name)
+#define SYMBOL_NAME_LEN(S)     ((S)->len)
 #define SYMBOL_TYPE(S)         (TOKEN_DATA_TYPE (&(S)->data))
 #define SYMBOL_TEXT(S)         (TOKEN_DATA_TEXT (&(S)->data))
 #define SYMBOL_FUNC(S)         (TOKEN_DATA_FUNC (&(S)->data))
@@ -503,7 +505,7 @@ extern symbol **symtab;
 
 void free_symbol (symbol *sym);
 void symtab_init (void);
-symbol *lookup_symbol (const char *, symbol_lookup);
+symbol *lookup_symbol (const char *, size_t, symbol_lookup);
 void hack_all_symbols (hack_symbol *, void *);
 
 /* File: macro.c  --- macro expansion.  */
@@ -573,8 +575,8 @@ struct re_registers;
 #define DEFAULT_MACRO_SEQUENCE "\\$\\({[^}]*}\\|[0-9][0-9]+\\)"
 
 void builtin_init (void);
-bool bad_argc (const char *, int, unsigned int, unsigned int);
-void define_builtin (const char *, const builtin *, symbol_lookup);
+bool bad_argc (const call_info *, int, unsigned int, unsigned int);
+void define_builtin (const char *, size_t, const builtin *, symbol_lookup);
 void set_macro_sequence (const char *);
 void free_regex (void);
 void define_user_macro (const char *, size_t, const char *, symbol_lookup);
@@ -598,7 +600,7 @@ FILE *m4_path_search (const char *, char **);
 
 /* File: eval.c  --- expression evaluation.  */
 
-bool evaluate (const char *, const char *, int32_t *);
+bool evaluate (const call_info *, const char *, int32_t *);
 
 /* File: format.c  --- printf like formatting.  */
 
diff --git a/src/macro.c b/src/macro.c
index 2a5b52a..0b57436 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -29,7 +29,7 @@
 #endif /* DEBUG_MACRO */
 
 /* Opaque structure describing all arguments to a macro, including the
-   macro name at index 0.  The lifetime of argv0 is only guaranteed
+   macro name at index 0.  The lifetime of info is only guaranteed
    within a call to expand_macro, whereas the lifetime of the array
    members is guaranteed as long as the input engine can parse text
    with a reference to address@hidden  */
@@ -53,8 +53,6 @@ struct macro_arguments
   bool_bitfield flatten : 1;
   /* True if any token contains builtins.  */
   bool_bitfield has_func : 1;
-  const char *argv0; /* The macro name being expanded.  */
-  size_t argv0_len; /* Length of argv0.  */
   /* The value of quote_age used when parsing all arguments in this
      object, or 0 if quote_age changed during parsing or if any of the
      arguments might contain content that can affect rescan.  */
@@ -221,6 +219,7 @@ expand_input (void)
   TOKEN_DATA_LEN (&empty_token) = 0;
 #ifdef ENABLE_CHANGEWORD
   TOKEN_DATA_ORIG_TEXT (&empty_token) = "";
+  TOKEN_DATA_ORIG_LEN (&empty_token) = 0;
 #endif
 
   while ((t = next_token (&td, &line, NULL, false, NULL)) != TOKEN_EOF)
@@ -301,7 +300,8 @@ expand_token (struct obstack *obs, token_type t, token_data 
*td, int line,
       break;
 
     case TOKEN_WORD:
-      sym = lookup_symbol (TOKEN_DATA_TEXT (td), SYMBOL_LOOKUP);
+      sym = lookup_symbol (TOKEN_DATA_TEXT (td), TOKEN_DATA_LEN (td),
+                          SYMBOL_LOOKUP);
       if (sym == NULL || SYMBOL_TYPE (sym) == TOKEN_VOID
          || (SYMBOL_TYPE (sym) == TOKEN_FUNC
              && SYMBOL_BLIND_NO_ARGS (sym)
@@ -309,7 +309,7 @@ expand_token (struct obstack *obs, token_type t, token_data 
*td, int line,
        {
 #ifdef ENABLE_CHANGEWORD
          divert_text (obs, TOKEN_DATA_ORIG_TEXT (td),
-                      TOKEN_DATA_LEN (td), line);
+                      TOKEN_DATA_ORIG_LEN (td), line);
 #else
          divert_text (obs, TOKEN_DATA_TEXT (td), TOKEN_DATA_LEN (td), line);
 #endif /* !ENABLE_CHANGEWORD */
@@ -344,12 +344,12 @@ expand_token (struct obstack *obs, token_type t, 
token_data *td, int line,
 `-------------------------------------------------------------------*/
 
 static bool
-expand_argument (struct obstack *obs, token_data *argp, const char *caller)
+expand_argument (struct obstack *obs, token_data *argp,
+                const call_info *caller)
 {
   token_type t;
   token_data td;
   int paren_level;
-  const char *file = current_file;
   int line = current_line;
   unsigned int age = quote_age ();
   bool first = true;
@@ -414,10 +414,8 @@ expand_argument (struct obstack *obs, token_data *argp, 
const char *caller)
          break;
 
        case TOKEN_EOF:
-         /* Current_file changed to "" if we see TOKEN_EOF, use the
-            previous value we stored earlier.  */
-         m4_error_at_line (EXIT_FAILURE, 0, file, line, caller,
-                           _("end of file in argument list"));
+         m4_error (EXIT_FAILURE, 0, caller,
+                   _("end of file in argument list"));
          break;
 
        case TOKEN_WORD:
@@ -495,8 +493,6 @@ collect_arguments (symbol *sym, call_info *info, struct 
obstack *arguments,
   args.has_ref = false;
   args.flatten = !groks_macro_args;
   args.has_func = false;
-  args.argv0 = SYMBOL_NAME (sym);
-  args.argv0_len = strlen (args.argv0);
   args.quote_age = quote_age ();
   args.info = info;
   args.level = expansion_level - 1;
@@ -506,11 +502,11 @@ collect_arguments (symbol *sym, call_info *info, struct 
obstack *arguments,
   if (peek_token () == TOKEN_OPEN)
     {
       /* gobble parenthesis */
-      next_token (&td, NULL, NULL, false, SYMBOL_NAME (sym));
+      next_token (&td, NULL, NULL, false, info);
       do
        {
          tdp = (token_data *) obstack_alloc (arguments, sizeof *tdp);
-         more_args = expand_argument (arguments, tdp, SYMBOL_NAME (sym));
+         more_args = expand_argument (arguments, tdp, info);
 
          if ((TOKEN_DATA_TYPE (tdp) == TOKEN_TEXT && !TOKEN_DATA_LEN (tdp))
              || (!groks_macro_args && TOKEN_DATA_TYPE (tdp) == TOKEN_FUNC))
@@ -622,10 +618,6 @@ expand_macro (symbol *sym)
   int level = expansion_level; /* Expansion level of this macro.  */
   call_info my_call_info;      /* Context of this macro.  */
 
-  /* TODO - make m4_warn use optional call_info, so we don't need these.  */
-  const char *loc_close_file;
-  int loc_close_line;
-
   /* Obstack preparation.  */
   if (level >= stacks_count)
     {
@@ -670,6 +662,7 @@ expand_macro (symbol *sym)
   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);
+  my_call_info.name_len = SYMBOL_NAME_LEN (sym);
   trace_prepre (&my_call_info);
 
   /* Collect the arguments.  */
@@ -677,23 +670,13 @@ expand_macro (symbol *sym)
                            stacks[level].argv);
   args_scratch = obstack_finish (stacks[level].args);
 
-  /*  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 = my_call_info.file;
-  current_line = my_call_info.line;
-
   /* The actual macro call.  */
-  expansion = push_string_init ();
+  expansion = push_string_init (my_call_info.file, my_call_info.line);
   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);
 
@@ -714,7 +697,7 @@ expand_macro (symbol *sym)
          if (debug_macro_level & PRINT_ARGCOUNT_CHANGES)
            xfprintf (debug, "m4debug: -%d- `%s' in use, level=%d, "
                      "refcount=%zu, argcount=%zu\n", my_call_info.call_id,
-                     argv->argv0, level, stacks[level].refcount,
+                     my_call_info.name, level, stacks[level].refcount,
                      stacks[level].argcount);
        }
       else
@@ -916,7 +899,10 @@ arg_text (macro_arguments *argv, unsigned int arg, bool 
flatten)
   struct obstack *obs; /* Scratch space; cleaned at end of macro_expand.  */
 
   if (arg == 0)
-    return argv->argv0;
+    {
+      assert (argv->info);
+      return argv->info->name;
+    }
   if (arg >= argv->argc)
     return "";
   token = arg_token (argv, arg, NULL, flatten);
@@ -1119,7 +1105,10 @@ bool
 arg_empty (macro_arguments *argv, unsigned int arg)
 {
   if (arg == 0)
-    return argv->argv0_len == 0;
+    {
+      assert (argv->info);
+      return argv->info->name_len == 0;
+    }
   if (arg >= argv->argc)
     return true;
   return arg_token (argv, arg, NULL, false) == &empty_token;
@@ -1135,7 +1124,10 @@ arg_len (macro_arguments *argv, unsigned int arg)
   size_t len;
 
   if (arg == 0)
-    return argv->argv0_len;
+    {
+      assert (argv->info);
+      return argv->info->name_len;
+    }
   if (arg >= argv->argc)
     return 0;
   token = arg_token (argv, arg, NULL, false);
@@ -1450,12 +1442,11 @@ make_argv_ref (macro_arguments *argv, const char 
*argv0, size_t argv0_len,
     }
   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;
+  info->name_len = argv0_len;
   new_argv->level = argv->level;
   return new_argv;
 }
@@ -1469,7 +1460,8 @@ push_arg (struct obstack *obs, macro_arguments *argv, 
unsigned int arg)
     {
       /* Always push copy of arg 0, since its lifetime is not
         guaranteed beyond expand_macro.  */
-      obstack_grow (obs, argv->argv0, argv->argv0_len);
+      assert (argv->info);
+      obstack_grow (obs, argv->info->name, argv->info->name_len);
       return;
     }
   if (arg >= argv->argc)
@@ -1538,7 +1530,7 @@ wrap_args (macro_arguments *argv)
   if ((argv->argc == 2 || no_gnu_extensions) && arg_empty (argv, 1))
     return;
 
-  obs = push_wrapup_init (&end);
+  obs = push_wrapup_init (argv->info, &end);
   for (i = 1; i < (no_gnu_extensions ? 2 : argv->argc); i++)
     {
       if (i != 1)
diff --git a/src/symtab.c b/src/symtab.c
index dac49d7..d4da200 100644
--- a/src/symtab.c
+++ b/src/symtab.c
@@ -62,19 +62,20 @@ show_profile (void)
     }
 }
 
-/* Like strcmp (S1, S2), but also track profiling statistics.  */
+/* Like memcmp (S1, S2, L), but also track profiling statistics.  */
 static int
-profile_strcmp (const char *s1, const char *s2)
+profile_memcmp (const char *s1, const char *s2, size_t l)
 {
-  int i = 1;
+  int i = 0;
   int result;
-  while (*s1 && *s1 == *s2)
+  while (l && *s1 == *s2)
     {
       s1++;
       s2++;
       i++;
+      l--;
     }
-  result = (unsigned char) *s1 - (unsigned char) *s2;
+  result = l ? (unsigned char) *s1 - (unsigned char) *s2 : 0;
   profiles[current_mode].comparisons++;
   if (result != 0)
     profiles[current_mode].misses++;
@@ -82,7 +83,7 @@ profile_strcmp (const char *s1, const char *s2)
   return result;
 }
 
-# define strcmp profile_strcmp
+# define memcmp profile_memcmp
 #endif /* DEBUG_SYM */
 
 
@@ -111,19 +112,18 @@ symtab_init (void)
 }
 
 /*--------------------------------------------------.
-| Return a hashvalue for a string, from GNU-emacs.  |
+| Return a hashvalue for a string S of length LEN.  |
 `--------------------------------------------------*/
 
 static size_t
-hash (const char *s)
+hash (const char *s, size_t len)
 {
-  register size_t val = 0;
+  size_t val = len;
 
-  register const char *ptr = s;
-  register char ch;
-
-  while ((ch = *ptr++) != '\0')
-    val = (val << 7) + (val >> (sizeof (val) * CHAR_BIT - 7)) + ch;
+  /* This algorithm was originally borrowed from GNU Emacs, but has
+     been modified to allow embedded NUL.  */
+  while (len--)
+    val = (val << 7) + (val >> (sizeof val * CHAR_BIT - 7)) + to_uchar (*s++);
   return val;
 }
 
@@ -146,20 +146,20 @@ free_symbol (symbol *sym)
 }
 
 /*-------------------------------------------------------------------.
-| Search in, and manipulation of the symbol table, are all done by   |
-| lookup_symbol ().  It basically hashes NAME to a list in the      |
-| symbol table, and searches this list for the first occurrence of a |
-| symbol with the name.                                                     |
-|                                                                   |
+| Searches and manipulation of the symbol table are all done by      |
+| lookup_symbol ().  It basically hashes NAME, of length LEN, to a   |
+| list in the symbol table, and searches this list for the first     |
+| occurrence of a symbol with the name.                              |
+|                                                                    |
 | The MODE parameter determines what lookup_symbol () will do.  It   |
-| can either just do a lookup, do a lookup and insert if not        |
+| can either just do a lookup, do a lookup and insert if not         |
 | present, do an insertion even if the name is already in the list,  |
 | delete the first occurrence of the name on the list, or delete all |
-| occurrences of the name on the list.                              |
+| occurrences of the name on the list.                               |
 `-------------------------------------------------------------------*/
 
 symbol *
-lookup_symbol (const char *name, symbol_lookup mode)
+lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 {
   size_t h;
   int cmp = 1;
@@ -171,12 +171,13 @@ lookup_symbol (const char *name, symbol_lookup mode)
   profiles[mode].entry++;
 #endif /* DEBUG_SYM */
 
-  h = hash (name);
+  h = hash (name, len);
   sym = symtab[h % hash_table_size];
 
   for (prev = NULL; sym != NULL; prev = sym, sym = sym->next)
     {
-      cmp = strcmp (SYMBOL_NAME (sym), name);
+      cmp = (len < SYMBOL_NAME_LEN (sym) ? -1 : len > SYMBOL_NAME_LEN (sym) ? 1
+            : memcmp (SYMBOL_NAME (sym), name, len));
       if (cmp >= 0)
        break;
     }
@@ -210,7 +211,8 @@ lookup_symbol (const char *name, symbol_lookup mode)
              sym = (symbol *) xmalloc (sizeof (symbol));
              SYMBOL_TYPE (sym) = TOKEN_VOID;
              SYMBOL_TRACED (sym) = SYMBOL_TRACED (old);
-             SYMBOL_NAME (sym) = xstrdup (name);
+             SYMBOL_NAME (sym) = xmemdup0 (name, len);
+             SYMBOL_NAME_LEN (sym) = len;
              SYMBOL_SHADOWED (sym) = false;
              SYMBOL_MACRO_ARGS (sym) = false;
              SYMBOL_BLIND_NO_ARGS (sym) = false;
@@ -234,7 +236,8 @@ lookup_symbol (const char *name, symbol_lookup mode)
       sym = (symbol *) xmalloc (sizeof (symbol));
       SYMBOL_TYPE (sym) = TOKEN_VOID;
       SYMBOL_TRACED (sym) = false;
-      SYMBOL_NAME (sym) = xstrdup (name);
+      SYMBOL_NAME (sym) = xmemdup0 (name, len);
+      SYMBOL_NAME_LEN (sym) = len;
       SYMBOL_SHADOWED (sym) = false;
       SYMBOL_MACRO_ARGS (sym) = false;
       SYMBOL_BLIND_NO_ARGS (sym) = false;
@@ -287,7 +290,8 @@ lookup_symbol (const char *name, symbol_lookup mode)
            sym = (symbol *) xmalloc (sizeof (symbol));
            SYMBOL_TYPE (sym) = TOKEN_VOID;
            SYMBOL_TRACED (sym) = true;
-           SYMBOL_NAME (sym) = xstrdup (name);
+           SYMBOL_NAME (sym) = xmemdup0 (name, len);
+           SYMBOL_NAME_LEN (sym) = len;
            SYMBOL_SHADOWED (sym) = false;
            SYMBOL_MACRO_ARGS (sym) = false;
            SYMBOL_BLIND_NO_ARGS (sym) = false;
@@ -348,28 +352,32 @@ symtab_debug (void)
   const char *text;
   symbol *s;
   int delete;
+  size_t len;
+  int line;
   static int i;
 
-  while (next_token (&td, NULL, NULL, false, "<debug>") == TOKEN_WORD)
+  while (next_token (&td, &line, NULL, false, NULL) == TOKEN_WORD)
     {
       text = TOKEN_DATA_TEXT (&td);
+      len = TOKEN_DATA_LEN (&td);
       if (*text == '_')
        {
          delete = 1;
          text++;
+         len--;
        }
       else
        delete = 0;
 
-      s = lookup_symbol (text, SYMBOL_LOOKUP);
+      s = lookup_symbol (text, len, SYMBOL_LOOKUP);
 
       if (s == NULL)
        xprintf ("Name `%s' is unknown\n", text);
 
       if (delete)
-       (void) lookup_symbol (text, SYMBOL_DELETE);
+       lookup_symbol (text, len, SYMBOL_DELETE);
       else
-       (void) lookup_symbol (text, SYMBOL_INSERT);
+       lookup_symbol (text, len, SYMBOL_INSERT);
     }
   symtab_print_list (i++);
 }
@@ -383,9 +391,9 @@ symtab_print_list (int i)
   xprintf ("Symbol dump #%d:\n", i);
   for (h = 0; h < hash_table_size; h++)
     for (sym = symtab[h]; sym != NULL; sym = sym->next)
-      xprintf ("\tname %s, bucket %lu, addr %p, next %p, "
+      xprintf ("\tname %s, len %zu, bucket %lu, addr %p, next %p, "
               "flags%s%s%s, pending %d\n",
-              SYMBOL_NAME (sym),
+              SYMBOL_NAME (sym), SYMBOL_NAME_LEN (sym),
               (unsigned long int) h, sym, SYMBOL_NEXT (sym),
               SYMBOL_TRACED (sym) ? " traced" : "",
               SYMBOL_SHADOWED (sym) ? " shadowed" : "",
-- 
1.5.5.1


reply via email to

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