[SCM] GNU M4 source repository branch, branch-1.6, updated. v1.5.89a-26-gf3fbfb9

"Eric Blake" <[email protected]>
Newsgroups gmane.comp.gnu.m4.cvs
Message-ID <[email protected]>
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=f3fbfb92cc3f44af244f888b8be6b465493dc590

The branch, branch-1.6 has been updated
       via  f3fbfb92cc3f44af244f888b8be6b465493dc590 (commit)
      from  b9a0d949a0485213215c59b6df9283d4886d5fff (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 f3fbfb92cc3f44af244f888b8be6b465493dc590
Author: Eric Blake <[email protected]>
Date:   Tue Dec 18 16:13:16 2007 -0700

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

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

Summary of changes:
 ChangeLog          |   61 ++++++++++++++++
 doc/m4.texinfo     |   16 ++++
 examples/null.err  |  Bin 51 -> 505 bytes
 examples/null.m4   |  Bin 5876 -> 5891 bytes
 examples/null.out  |  Bin 404 -> 404 bytes
 m4/gnulib-cache.m4 |    4 +-
 src/builtin.c      |  193 +++++++++++++++++++++++++++++----------------------
 src/debug.c        |   26 ++++---
 src/eval.c         |   56 ++++++++--------
 src/format.c       |    8 +-
 src/freeze.c       |   10 ++--
 src/input.c        |  103 +++++++++++++++-------------
 src/m4.c           |  130 ++++++++++++-----------------------
 src/m4.h           |   34 +++++----
 src/macro.c        |   68 ++++++++----------
 src/symtab.c       |   74 +++++++++++---------
 16 files changed, 430 insertions(+), 353 deletions(-)

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


hooks/post-receive
--
GNU M4 source repository
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.