m4-commit
[Top][All Lists]
Advanced

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

[SCM] GNU M4 source repository branch, master, updated. cvs-readonly-130


From: Eric Blake
Subject: [SCM] GNU M4 source repository branch, master, updated. cvs-readonly-130-g50c5eb0
Date: Tue, 03 Jun 2008 03:51:20 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU M4 source repository".

http://git.sv.gnu.org/gitweb/?p=m4.git;a=commitdiff;h=50c5eb094704ebfe272b695a89ad35280a40a716

The branch, master has been updated
       via  50c5eb094704ebfe272b695a89ad35280a40a716 (commit)
       via  a3aa897fc496179ff7bdd0fe6cd42a9e298e8473 (commit)
       via  3e33a6b3b9b2529939136c34b1e88bd3ee87a172 (commit)
      from  c41ad82fe554e33048e1ef60c4600e602c7207e9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 50c5eb094704ebfe272b695a89ad35280a40a716
Author: Eric Blake <address@hidden>
Date:   Thu May 29 07:24:16 2008 -0600

    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>

commit a3aa897fc496179ff7bdd0fe6cd42a9e298e8473
Author: Eric Blake <address@hidden>
Date:   Wed May 28 06:41:53 2008 -0600

    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>

commit 3e33a6b3b9b2529939136c34b1e88bd3ee87a172
Author: Eric Blake <address@hidden>
Date:   Wed May 21 06:59:01 2008 -0600

    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>

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog               |  100 +++++++++++++++++++++++++
 ltdl/m4/gnulib-cache.m4 |    4 +-
 m4/debug.c              |   26 +++---
 m4/hash.c               |   30 +++++---
 m4/input.c              |  117 ++++++++++++++++++------------
 m4/m4module.h           |   66 +++++++++--------
 m4/m4private.h          |    8 +-
 m4/macro.c              |   84 +++++++++++-----------
 m4/module.c             |    4 +-
 m4/symtab.c             |  186 ++++++++++++++++++++++++++++++++---------------
 m4/utility.c            |  134 ++++++++++++----------------------
 modules/evalparse.c     |    2 +-
 modules/format.c        |    8 +-
 modules/gnu.c           |  100 ++++++++++++++-----------
 modules/m4.c            |  161 +++++++++++++++++++++++-----------------
 modules/m4.h            |    4 +-
 modules/stdlib.c        |    6 +-
 modules/time.c          |   10 +-
 src/freeze.c            |   26 +++---
 src/main.c              |   42 +++++------
 tests/freeze.at         |    7 +-
 tests/null.err          |  Bin 51 -> 460 bytes
 tests/null.m4           |  Bin 6605 -> 6634 bytes
 tests/null.out          |  Bin 404 -> 419 bytes
 tests/options.at        |   24 ++++++
 tests/others.at         |    6 +-
 26 files changed, 686 insertions(+), 469 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 176c711..f06aee7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,105 @@
 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.
+       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
+       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/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/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/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/input.c b/m4/input.c
index a021e70..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;
 }
@@ -1334,30 +1337,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"));
 }
 
 
@@ -1408,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;
@@ -1521,13 +1513,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 +1527,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 +1534,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 +1564,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 +1598,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 +1639,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 +1677,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 +1714,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..346463d 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
 {
@@ -159,24 +167,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 *);
@@ -256,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);
@@ -264,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);
@@ -409,7 +414,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,13 +497,13 @@ 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 */
 
 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 526a0ef..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,10 +559,12 @@ 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, 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..1436ea9 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 *);
 
@@ -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
@@ -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:
@@ -444,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)
@@ -507,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;
@@ -539,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
        {
@@ -572,24 +562,22 @@ 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))
     {
       /* 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 +649,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 +660,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 +773,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;
@@ -892,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.
@@ -919,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);
@@ -937,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))
     {
@@ -1260,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);
@@ -1455,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
@@ -1469,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);
@@ -1631,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;
@@ -1651,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);
@@ -1723,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/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 2cd4d18..c104779 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,70 +116,81 @@ 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))
     {
       const char *name = M4ARG (i);
-      result = m4_symbol_lookup (M4SYMTAB, name);
+      size_t len = M4ARGLEN (i);
+      result = m4_symbol_lookup (M4SYMTAB, name, len);
       if (must_exist && !result)
-       m4_warn (context, 0, caller, _("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, 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;
+  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);
 
+  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.  */
-  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);
@@ -192,43 +204,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 +224,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 +239,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..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
@@ -130,7 +131,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 +232,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 +286,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,9 +403,10 @@ 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;
+  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,33 +439,36 @@ 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);
-         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, 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))
-           {
-             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 +484,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 +502,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 +545,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 +567,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 +593,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 +610,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,21 +676,23 @@ 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
     {
       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);
+       m4_warn (context, 0, me, _("undefined macro %s"),
+                quotearg_style_mem (locale_quoting_style, name, len));
       else
        {
          m4_macro_args *new_argv;
          m4_symbol_value *value = m4_get_symbol_value (symbol);
-         new_argv = m4_make_argv_ref (context, argv, name, M4ARGLEN (1),
+         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);
@@ -701,7 +712,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 +731,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 +777,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 +786,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 +860,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 +870,6 @@ M4BUILTIN_HANDLER (renamesyms)
 
       int resyntax;
 
-      me      = M4ARG (0);
       regexp  = M4ARG (1);
       replace = M4ARG (2);
 
@@ -881,14 +890,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);
            }
        }
     }
@@ -916,7 +925,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, ',');
        }
@@ -937,6 +947,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..d9e789e 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.
@@ -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,
@@ -153,48 +153,50 @@ 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_symbol_define (M4SYMTAB, M4ARG (1), value);
+       m4_warn (context, 0, me, _("cannot concatenate builtins"));
+      m4_symbol_define (M4SYMTAB, M4ARG (1), M4ARGLEN (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))
-      m4_symbol_delete (M4SYMTAB, M4ARG (i));
+    if (m4_symbol_value_lookup (context, argv, i, true))
+      m4_symbol_delete (M4SYMTAB, M4ARG (i), M4ARGLEN (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_symbol_pushdef (M4SYMTAB, M4ARG (1), value);
+       m4_warn (context, 0, me, _("cannot concatenate builtins"));
+      m4_symbol_pushdef (M4SYMTAB, M4ARG (1), M4ARGLEN (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))
-      m4_symbol_popdef (M4SYMTAB, M4ARG (i));
+    if (m4_symbol_value_lookup (context, argv, i, true))
+      m4_symbol_popdef (M4SYMTAB, M4ARG (i), M4ARGLEN (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
@@ -254,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);
@@ -271,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;
 }
 
@@ -290,9 +304,9 @@ 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 *);
+  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);
@@ -303,15 +317,17 @@ 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);
+           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
@@ -353,12 +373,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 +388,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 +500,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 +530,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 +540,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 +556,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 +578,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 +617,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 +662,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 +688,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 +701,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 +725,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 +744,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 +779,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 +815,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 +859,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)
@@ -845,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"));
 }
@@ -853,7 +876,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)
@@ -862,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"));
 }
@@ -902,7 +925,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..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;
 
@@ -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/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 104c256..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,22 +732,27 @@ 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:
          {
            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;
 
        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 44de59d..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.
@@ -506,7 +507,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/null.err b/tests/null.err
index d825818..8bf1f4f 100644
Binary files a/tests/null.err and b/tests/null.err differ
diff --git a/tests/null.m4 b/tests/null.m4
index 851d665..55bd3bd 100644
Binary files a/tests/null.m4 and b/tests/null.m4 differ
diff --git a/tests/null.out b/tests/null.out
index cd3764f..3a96faa 100644
Binary files a/tests/null.out and b/tests/null.out differ
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
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],


hooks/post-receive
--
GNU M4 source repository




reply via email to

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