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


From: Eric Blake
Subject: [SCM] GNU M4 source repository branch, master, updated. cvs-readonly-96-g0fe33c9
Date: Tue, 15 Apr 2008 04:27:18 +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=0fe33c9c7dbdbbf98255a8779bf179854753df61

The branch, master has been updated
       via  0fe33c9c7dbdbbf98255a8779bf179854753df61 (commit)
       via  b6964b5f0e6b81833bfbf1aee8bb49c35b09c3f3 (commit)
      from  03c4e545afedafff51cce3a73aec5172482c0120 (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 0fe33c9c7dbdbbf98255a8779bf179854753df61
Author: Eric Blake <address@hidden>
Date:   Mon Apr 14 17:02:36 2008 -0600

    Stage 21b: $@ concatenates builtins, m4wrap takes builtins.
    
    * m4/m4module.h (m4_push_builtin): Add parameter.
    (m4_builtin_print, m4_push_wrapup_init, m4_push_wrapup_finish)
    (m4_arg_print, m4_symbol_value_print): Rename and reduce scope...
    * m4/m4private.h (m4__builtin_print, m4__push_wrapup_init)
    (m4__push_wrapup_finish, m4__arg_print, m4__symbol_value_print):
    ...to these, in some cases adding a parameter.
    (m4__append_builtin): New prototype.
    * m4/builtin.c (m4_builtin_print): Alter signature to print
    builtin to a growing symbol chain.
    * m4/symtab.c (m4__symbol_value_print): Alter signature.
    (m4_symbol_print, dump_symbol_CB): Adjust callers.
    * m4/input.c (builtin_peek, builtin_read, builtin_unget)
    (builtin_print, builtin_funcs): Delete, handled via composite
    blocks now.
    (struct m4_input_block): Delete u.builtin member.
    (init_builtin_token): Only use composite block.
    (m4__append_builtin): New function.
    (m4_push_builtin, m4__push_wrapup_init): Alter signature.
    (m4__push_symbol): Allow builtin tokens.
    (m4__push_wrapup_finish): Rename.
    (composite_print, m4_print_token): Adjust callers.
    * m4/macro.c (m4_wrap_args, collect_arguments): Allow builtin
    tokens.
    (m4__arg_print): Alter signature.
    (trace_prepre, trace_pre, m4_arg_text, m4_arg_equal): Adjust
    callers.
    * modules/m4.c (m4wrap): Allow builtin tokens.
    (defn, errprint): Adjust callers.
    * modules/gnu.c (builtin): Likewise.
    * doc/m4.texinfo (M4wrap): New test.
    (Debuglen): Adjust expected output.
    
    Signed-off-by: Eric Blake <address@hidden>

commit b6964b5f0e6b81833bfbf1aee8bb49c35b09c3f3
Author: Eric Blake <address@hidden>
Date:   Mon Apr 14 08:16:32 2008 -0600

    Stage 21a: Optimize checks for end of input.
    
    * m4/input.c (eof_funcs, input_eof): New objects.
    (eof_peek, eof_read, eof_unget): New functions.
    (file_clean, m4_push_string_init, pop_input, m4_push_wrapup_init)
    (m4_pop_wrapup, next_char, peek_char, unget_input, m4_input_init)
    (m4_input_exit): Use placeholder to guarantee non-NULL isp and
    wsp.
    (next_char): Rename retry to allow_unget, and change sense for
    easier manipulation.  All callers changed.
    
    Signed-off-by: Eric Blake <address@hidden>

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

Summary of changes:
 ChangeLog      |   53 +++++++++
 doc/m4.texinfo |   22 ++++-
 m4/builtin.c   |   16 ++-
 m4/input.c     |  349 +++++++++++++++++++++++++++-----------------------------
 m4/m4module.h  |   12 +--
 m4/m4private.h |   19 +++-
 m4/macro.c     |  130 +++++++++++----------
 m4/symtab.c    |   56 +++++-----
 modules/gnu.c  |    2 +-
 modules/m4.c   |   18 ++--
 10 files changed, 376 insertions(+), 301 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index af55ae5..0869fc5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,58 @@
 2008-04-14  Eric Blake  <address@hidden>
 
+       Stage 21b: $@ concatenates builtins, m4wrap takes builtins.
+       Improve arg_print to handle builtin tokens when printing to a
+       known chain, rather than always flattening builtins.  This allows
+       m4wrap and $@ back-references to handle embedded builtin tokens.
+       Memory impact: none.
+       Speed impact: slight penalty, from more bookkeeping.
+       * m4/m4module.h (m4_push_builtin): Add parameter.
+       (m4_builtin_print, m4_push_wrapup_init, m4_push_wrapup_finish)
+       (m4_arg_print, m4_symbol_value_print): Rename and reduce scope...
+       * m4/m4private.h (m4__builtin_print, m4__push_wrapup_init)
+       (m4__push_wrapup_finish, m4__arg_print, m4__symbol_value_print):
+       ...to these, in some cases adding a parameter.
+       (m4__append_builtin): New prototype.
+       * m4/builtin.c (m4_builtin_print): Alter signature to print
+       builtin to a growing symbol chain.
+       * m4/symtab.c (m4__symbol_value_print): Alter signature.
+       (m4_symbol_print, dump_symbol_CB): Adjust callers.
+       * m4/input.c (builtin_peek, builtin_read, builtin_unget)
+       (builtin_print, builtin_funcs): Delete, handled via composite
+       blocks now.
+       (struct m4_input_block): Delete u.builtin member.
+       (init_builtin_token): Only use composite block.
+       (m4__append_builtin): New function.
+       (m4_push_builtin, m4__push_wrapup_init): Alter signature.
+       (m4__push_symbol): Allow builtin tokens.
+       (m4__push_wrapup_finish): Rename.
+       (composite_print, m4_print_token): Adjust callers.
+       * m4/macro.c (m4_wrap_args, collect_arguments): Allow builtin
+       tokens.
+       (m4__arg_print): Alter signature.
+       (trace_prepre, trace_pre, m4_arg_text, m4_arg_equal): Adjust
+       callers.
+       * modules/m4.c (m4wrap): Allow builtin tokens.
+       (defn, errprint): Adjust callers.
+       * modules/gnu.c (builtin): Likewise.
+       * doc/m4.texinfo (M4wrap): New test.
+       (Debuglen): Adjust expected output.
+
+       Stage 21a: Optimize checks for end of input.
+       Create a new polymorphic input block type, which always fails with
+       CHAR_EOF, so that remaining input routines no longer have to check
+       for NULL input block.
+       Memory impact: none.
+       Speed impact: noticeable improvement, from fewer conditionals.
+       * m4/input.c (eof_funcs, input_eof): New objects.
+       (eof_peek, eof_read, eof_unget): New functions.
+       (file_clean, m4_push_string_init, pop_input, m4_push_wrapup_init)
+       (m4_pop_wrapup, next_char, peek_char, unget_input, m4_input_init)
+       (m4_input_exit): Use placeholder to guarantee non-NULL isp and
+       wsp.
+       (next_char): Rename retry to allow_unget, and change sense for
+       easier manipulation.  All callers changed.
+
        Improve OS/2 detection.
        * modules/gnu.c (m4_macro_table): Ensure all possible identifiers
        are defined, not just the first.  The testsuite ensures that
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 9198a09..d0bbfa4 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -4103,7 +4103,7 @@ echo(`1', `long string')
 @error{}m4trace: -1- echo(`1', `long s...') -> ``1',`l...'
 @result{}1,long string
 echo(defn(`changequote'))
address@hidden: -2- defn(`change...') -> <changequote>
address@hidden: -2- defn(`change...') -> `<changequote>'
 @error{}m4trace: -1- echo(<changequote>) -> ``<changequote>''
 @result{}
 debuglen
@@ -5352,6 +5352,24 @@ m4wrap(`m4wrap(`)')len(abc')
 @error{}m4:stdin:1: len: end of file in argument list
 @end example
 
+As of M4 1.6, @code{m4wrap} transparently handles builtin tokens
+generated by @code{defn} (@pxref{Defn}).  However, for portability, it
+is better to defer the evaluation of @code{defn} along with the rest of
+the wrapped text, as is done for @code{foo} in the example below, rather
+than computing the builtin token up front, as is done for @code{bar}.
+
address@hidden
+m4wrap(`define(`foo', defn(`divnum'))foo
+')
address@hidden
+m4wrap(`define(`bar', ')m4wrap(defn(`divnum'))m4wrap(`)bar
+')
address@hidden
+^D
address@hidden
address@hidden
address@hidden example
+
 @node File Inclusion
 @chapter File inclusion
 
@@ -6193,7 +6211,7 @@ names (consider the beta release @samp{1.9b}, or the use 
of
 be used via @code{defn} rather than directly invoked.
 
 @comment This test is excluded from the testsuite since it depends on a
address@hidden texinfo macro; but builtin.at covers the same thing.
address@hidden texinfo macro; but builtins.at covers the same thing.
 @comment ignore
 @example
 defn(`__m4_version__')
diff --git a/m4/builtin.c b/m4/builtin.c
index 5228abc..04f5ee3 100644
--- a/m4/builtin.c
+++ b/m4/builtin.c
@@ -88,10 +88,12 @@ m4_builtin_find_by_func (m4_module *module, m4_builtin_func 
*func)
 
 /* Print a representation of FUNC to OBS, optionally including the
    MODULE it came from.  If FLATTEN, output QUOTES around an empty
-   string instead.  */
+   string; if CHAIN, append the builtin to the chain; otherwise print
+   the name of FUNC.  */
 void
-m4_builtin_print (m4_obstack *obs, const m4_builtin *func, bool flatten,
-                 const m4_string_pair *quotes, m4_module *module)
+m4__builtin_print (m4_obstack *obs, const m4__builtin *func, bool flatten,
+                  m4__symbol_chain **chain, const m4_string_pair *quotes,
+                  bool module)
 {
   assert (func);
   if (flatten)
@@ -101,17 +103,19 @@ m4_builtin_print (m4_obstack *obs, const m4_builtin 
*func, bool flatten,
          obstack_grow (obs, quotes->str1, quotes->len1);
          obstack_grow (obs, quotes->str2, quotes->len2);
        }
-      module = NULL;
+      module = false;
     }
+  else if (chain)
+    m4__append_builtin (obs, func, NULL, chain);
   else
     {
       obstack_1grow (obs, '<');
-      obstack_grow (obs, func->name, strlen (func->name));
+      obstack_grow (obs, func->builtin.name, strlen (func->builtin.name));
       obstack_1grow (obs, '>');
     }
   if (module)
     {
-      const char *text = m4_get_module_name (module);
+      const char *text = m4_get_module_name (func->module);
       obstack_1grow (obs, '{');
       obstack_grow (obs, text, strlen (text));
       obstack_1grow (obs, '}');
diff --git a/m4/input.c b/m4/input.c
index a7f1da9..0f48768 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -60,7 +60,7 @@
    "wrapup_stack" to "current_input" can continue indefinitely, even
    generating infinite loops (e.g. "define(`f',`m4wrap(`f')')f"),
    without memory leaks.  Adding wrapped data is done through
-   m4_push_wrapup_init/m4_push_wrapup_finish().
+   m4__push_wrapup_init/m4__push_wrapup_finish().
 
    Pushing new input on the input stack is done by m4_push_file(), the
    conceptual m4_push_string(), and m4_push_builtin() (for builtin
@@ -98,11 +98,6 @@ static       int     file_read               (m4_input_block 
*, m4 *, bool, bool,
 static void    file_unget              (m4_input_block *, int);
 static bool    file_clean              (m4_input_block *, m4 *, bool);
 static void    file_print              (m4_input_block *, m4 *, m4_obstack *);
-static int     builtin_peek            (m4_input_block *, m4 *, bool);
-static int     builtin_read            (m4_input_block *, m4 *, bool, bool,
-                                        bool);
-static void    builtin_unget           (m4_input_block *, int);
-static void    builtin_print           (m4_input_block *, m4 *, m4_obstack *);
 static int     string_peek             (m4_input_block *, m4 *, bool);
 static int     string_read             (m4_input_block *, m4 *, bool, bool,
                                         bool);
@@ -114,6 +109,10 @@ static     int     composite_read          (m4_input_block 
*, m4 *, bool, bool,
 static void    composite_unget         (m4_input_block *, int);
 static bool    composite_clean         (m4_input_block *, m4 *, bool);
 static void    composite_print         (m4_input_block *, m4 *, m4_obstack *);
+static int     eof_peek                (m4_input_block *, m4 *, bool);
+static int     eof_read                (m4_input_block *, m4 *, bool, bool,
+                                        bool);
+static void    eof_unget               (m4_input_block *, int);
 
 static void    init_builtin_token      (m4 *, m4_symbol_value *);
 static void    append_quote_token      (m4 *, m4_obstack *,
@@ -143,10 +142,10 @@ struct input_funcs
   /* Read input, return an unsigned char, CHAR_BUILTIN if it is a
      builtin, or CHAR_RETRY if none available.  If ALLOW_QUOTE, then
      CHAR_QUOTE may be returned.  If ALLOW_ARGV, then CHAR_ARGV may be
-     returned.  If SAFE, then do not alter the current file or
-     line.  */
+     returned.  If ALLOW_UNGET, then ensure that the next unget_func
+     will work with the returned character.  */
   int  (*read_func)    (m4_input_block *, m4 *, bool allow_quote,
-                        bool allow_argv, bool safe);
+                        bool allow_argv, bool allow_unget);
 
   /* Unread a single unsigned character or CHAR_BUILTIN, must be the
      same character previously read by read_func.  */
@@ -186,7 +185,6 @@ struct m4_input_block
          bool_bitfield line_start : 1; /* Saved start_of_input_line state.  */
        }
       u_f;     /* See file_funcs.  */
-      const m4__builtin *builtin;      /* A builtin, see builtin_funcs.  */
       struct
        {
          m4__symbol_chain *chain;      /* Current link in chain.  */
@@ -213,13 +211,14 @@ static m4_obstack *current_input;
 /* Bottom of token_stack, for obstack_free.  */
 static void *token_bottom;
 
-/* Pointer to top of current_input.  */
+/* Pointer to top of current_input, never NULL.  */
 static m4_input_block *isp;
 
-/* Pointer to top of wrapup_stack.  */
+/* Pointer to top of wrapup_stack, never NULL.  */
 static m4_input_block *wsp;
 
-/* Aux. for handling split m4_push_string ().  */
+/* Auxiliary for handling split m4_push_string (), NULL when not
+   pushing text for rescanning.  */
 static m4_input_block *next;
 
 /* Flag for next_char () to increment current_line.  */
@@ -233,11 +232,6 @@ static struct input_funcs file_funcs = {
   file_peek, file_read, file_unget, file_clean, file_print
 };
 
-/* Vtable for handling input from builtin functions.  */
-static struct input_funcs builtin_funcs = {
-  builtin_peek, builtin_read, builtin_unget, NULL, builtin_print
-};
-
 /* Vtable for handling input from strings.  */
 static struct input_funcs string_funcs = {
   string_peek, string_read, string_unget, NULL, string_print
@@ -249,6 +243,14 @@ static struct input_funcs composite_funcs = {
   composite_print
 };
 
+/* Vtable for recognizing end of input.  */
+static struct input_funcs eof_funcs = {
+  eof_peek, eof_read, eof_unget, NULL, NULL
+};
+
+/* Marker at end of an input stack.  */
+static m4_input_block input_eof = { NULL, &eof_funcs, "", 0 };
+
 
 /* Input files, from command line or [s]include.  */
 static int
@@ -270,7 +272,7 @@ file_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
 
 static int
 file_read (m4_input_block *me, m4 *context, bool allow_quote M4_GNUC_UNUSED,
-          bool allow_argv M4_GNUC_UNUSED, bool safe M4_GNUC_UNUSED)
+          bool allow_argv M4_GNUC_UNUSED, bool allow_unget M4_GNUC_UNUSED)
 {
   int ch;
 
@@ -313,7 +315,7 @@ file_clean (m4_input_block *me, m4 *context, bool cleanup)
 {
   if (!cleanup)
     return false;
-  if (me->prev)
+  if (me->prev != &input_eof)
     m4_debug_message (context, M4_DEBUG_TRACE_INPUT,
                      _("input reverted to %s, line %d"),
                      me->prev->file, me->prev->line);
@@ -386,66 +388,6 @@ m4_push_file (m4 *context, FILE *fp, const char *title, 
bool close_file)
 }
 
 
-/* Handle a builtin macro token.  */
-static int
-builtin_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
-             bool allow_argv M4_GNUC_UNUSED)
-{
-  return me->u.builtin ? CHAR_BUILTIN : CHAR_RETRY;
-}
-
-static int
-builtin_read (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
-             bool allow_quote M4_GNUC_UNUSED, bool allow_argv M4_GNUC_UNUSED,
-             bool safe M4_GNUC_UNUSED)
-{
-  /* Not consumed here - wait until init_builtin_token.  */
-  return me->u.builtin ? CHAR_BUILTIN : CHAR_RETRY;
-}
-
-static void
-builtin_unget (m4_input_block *me, int ch)
-{
-  assert (ch == CHAR_BUILTIN && me->u.builtin);
-}
-
-static void
-builtin_print (m4_input_block *me, m4 *context, m4_obstack *obs)
-{
-  bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE);
-  m4_builtin_print (obs, &me->u.builtin->builtin, false, NULL,
-                   module ? me->u.builtin->module : NULL);
-}
-
-/* m4_push_builtin () pushes TOKEN, which contains a builtin's
-   definition, on the input stack.  If next is non-NULL, this push
-   invalidates a call to m4_push_string_init (), whose storage is
-   consequently released.  */
-void
-m4_push_builtin (m4 *context, m4_symbol_value *token)
-{
-  m4_input_block *i;
-
-  /* Make sure we were passed a builtin function type token.  */
-  assert (m4_is_symbol_value_func (token));
-
-  if (next != NULL)
-    {
-      obstack_free (current_input, next);
-      next = NULL;
-    }
-
-  i = (m4_input_block *) obstack_alloc (current_input, sizeof *i);
-  i->funcs = &builtin_funcs;
-  i->file = m4_get_current_file (context);
-  i->line = m4_get_current_line (context);
-  i->u.builtin = token->u.builtin;
-  i->prev = isp;
-  isp = i;
-  input_change = true;
-}
-
-
 /* Handle string expansion text.  */
 static int
 string_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
@@ -457,7 +399,7 @@ string_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
 static int
 string_read (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
             bool allow_quote M4_GNUC_UNUSED, bool allow_argv M4_GNUC_UNUSED,
-            bool safe M4_GNUC_UNUSED)
+            bool allow_unget M4_GNUC_UNUSED)
 {
   if (!me->u.u_s.len)
     return CHAR_RETRY;
@@ -492,7 +434,7 @@ m4_push_string_init (m4 *context)
 {
   /* Free any memory occupied by completely parsed input.  */
   assert (!next);
-  while (isp && pop_input (context, false));
+  while (pop_input (context, false));
 
   /* Reserve the next location on the obstack.  */
   next = (m4_input_block *) obstack_alloc (current_input, sizeof *next);
@@ -547,7 +489,18 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, 
size_t level, bool inuse)
          return false;
        }
     }
-  else if (!m4_is_symbol_value_func (value))
+  else if (m4_is_symbol_value_func (value))
+    {
+      if (next->funcs == &string_funcs)
+       {
+         next->funcs = &composite_funcs;
+         next->u.u_c.chain = next->u.u_c.end = NULL;
+       }
+      m4__append_builtin (current_input, value->u.builtin, &next->u.u_c.chain,
+                         &next->u.u_c.end);
+      return false;
+    }
+  else
     {
       /* For composite values, if argv is already in use, creating
         additional references for long text segments is more
@@ -594,24 +547,15 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, 
size_t level, bool inuse)
       m4__adjust_refcount (context, level, true);
       inuse = true;
     }
-  else if (m4_is_symbol_value_func (value))
-    {
-      chain = (m4__symbol_chain *) obstack_alloc (current_input,
-                                                 sizeof *chain);
-      if (next->u.u_c.end)
-       next->u.u_c.end->next = chain;
-      else
-       next->u.u_c.chain = chain;
-      next->u.u_c.end = chain;
-      chain->next = NULL;
-      chain->type = M4__CHAIN_FUNC;
-      chain->quote_age = 0;
-      chain->u.builtin = value->u.builtin;
-    }
   while (src_chain)
     {
-      /* TODO - support func concatenation.  */
-      assert (src_chain->type != M4__CHAIN_FUNC);
+      if (src_chain->type == M4__CHAIN_FUNC)
+       {
+         m4__append_builtin (current_input, src_chain->u.builtin,
+                             &next->u.u_c.chain, &next->u.u_c.end);
+         src_chain = src_chain->next;
+         continue;
+       }
       if (level == SIZE_MAX)
        {
          /* Nothing to copy, since link already lives on obstack.  */
@@ -764,7 +708,7 @@ composite_peek (m4_input_block *me, m4 *context, bool 
allow_argv)
 
 static int
 composite_read (m4_input_block *me, m4 *context, bool allow_quote,
-               bool allow_argv, bool safe)
+               bool allow_argv, bool allow_unget)
 {
   m4__symbol_chain *chain = me->u.u_c.chain;
   size_t argc;
@@ -818,13 +762,13 @@ composite_read (m4_input_block *me, m4 *context, bool 
allow_quote,
          chain->u.u_a.index++;
          chain->u.u_a.comma = true;
          m4_push_string_finish ();
-         return next_char (context, allow_quote, allow_argv, !safe);
+         return next_char (context, allow_quote, allow_argv, allow_unget);
        case M4__CHAIN_LOC:
          me->file = chain->u.u_l.file;
          me->line = chain->u.u_l.line;
          input_change = true;
          me->u.u_c.chain = chain->next;
-         return next_char (context, allow_quote, allow_argv, !safe);
+         return next_char (context, allow_quote, allow_argv, allow_unget);
        default:
          assert (!"composite_read");
          abort ();
@@ -922,17 +866,16 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
            done = true;
          break;
        case M4__CHAIN_FUNC:
-         m4_builtin_print (obs, &chain->u.builtin->builtin, false, NULL,
-                           module ? chain->u.builtin->module : NULL);
+         m4__builtin_print (obs, chain->u.builtin, false, NULL, NULL, module);
          break;
        case M4__CHAIN_ARGV:
          assert (!chain->u.u_a.comma);
-         if (m4_arg_print (context, obs, chain->u.u_a.argv,
-                           chain->u.u_a.index,
-                           m4__quote_cache (M4SYNTAX, NULL, chain->quote_age,
-                                            chain->u.u_a.quotes),
-                           chain->u.u_a.flatten, NULL, &maxlen, false,
-                           module))
+         if (m4__arg_print (context, obs, chain->u.u_a.argv,
+                            chain->u.u_a.index,
+                            m4__quote_cache (M4SYNTAX, NULL, chain->quote_age,
+                                             chain->u.u_a.quotes),
+                            chain->u.u_a.flatten, NULL, NULL, &maxlen, false,
+                            module))
            done = true;
          break;
        default:
@@ -974,6 +917,73 @@ m4__make_text_link (m4_obstack *obs, m4__symbol_chain 
**start,
     }
 }
 
+/* Given an obstack OBS, capture any unfinished text as a link, then
+   append the builtin FUNC as the next link in the chain that starts
+   at *START and ends at *END.  START may be NULL if *END is
+   non-NULL.  */
+void
+m4__append_builtin (m4_obstack *obs, const m4__builtin *func,
+                   m4__symbol_chain **start, m4__symbol_chain **end)
+{
+  m4__symbol_chain *chain;
+
+  assert (func);
+  m4__make_text_link (obs, start, end);
+  chain = (m4__symbol_chain *) obstack_alloc (obs, sizeof *chain);
+  if (*end)
+    (*end)->next = chain;
+  else
+    *start = chain;
+  *end = chain;
+  chain->next = NULL;
+  chain->type = M4__CHAIN_FUNC;
+  chain->quote_age = 0;
+  chain->u.builtin = func;
+}
+
+/* Push TOKEN, which contains a builtin's definition, onto the obstack
+   OBS, which is either input stack or the wrapup stack.  */
+void
+m4_push_builtin (m4 *context, m4_obstack *obs, m4_symbol_value *token)
+{
+  m4_input_block *i = (obs == current_input ? next : wsp);
+  assert (i);
+  if (i->funcs == &string_funcs)
+    {
+      i->funcs = &composite_funcs;
+      i->u.u_c.chain = i->u.u_c.end = NULL;
+    }
+  else
+    assert (i->funcs == &composite_funcs);
+  m4__append_builtin (obs, token->u.builtin, &i->u.u_c.chain, &i->u.u_c.end);
+}
+
+
+/* End of input optimization.  By providing these dummy callback
+   functions, we guarantee that the input stack is never NULL, and
+   thus make fewer execution branches.  */
+static int
+eof_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
+          bool allow_argv M4_GNUC_UNUSED)
+{
+  assert (me == &input_eof);
+  return CHAR_EOF;
+}
+
+static int
+eof_read (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
+         bool allow_quote M4_GNUC_UNUSED, bool allow_argv M4_GNUC_UNUSED,
+         bool allow_unget M4_GNUC_UNUSED)
+{
+  assert (me == &input_eof);
+  return CHAR_EOF;
+}
+
+static void
+eof_unget (m4_input_block *me M4_GNUC_UNUSED, int ch)
+{
+  assert (ch == CHAR_EOF);
+}
 
 
 /* When tracing, print a summary of the contents of the input block
@@ -998,19 +1008,18 @@ m4_input_print (m4 *context, m4_obstack *obs, 
m4_input_block *input)
     }
 }
 
-/* The function m4_push_wrapup_init () returns an obstack ready for
-   direct expansion of wrapup text, and should be followed by
-   m4_push_wrapup_finish ().
-
-   FIXME - we should allow pushing builtins as well as text.  */
+/* 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 ().  */
 m4_obstack *
-m4_push_wrapup_init (m4 *context)
+m4__push_wrapup_init (m4 *context, m4__symbol_chain ***end)
 {
   m4_input_block *i;
   m4__symbol_chain *chain;
 
   assert (obstack_object_size (wrapup_stack) == 0);
-  if (wsp)
+  if (wsp != &input_eof)
     {
       i = wsp;
       assert (i->funcs == &composite_funcs && i->u.u_c.end
@@ -1037,12 +1046,13 @@ m4_push_wrapup_init (m4 *context)
   chain->quote_age = 0;
   chain->u.u_l.file = m4_get_current_file (context);
   chain->u.u_l.line = m4_get_current_line (context);
+  *end = &i->u.u_c.end;
   return wrapup_stack;
 }
 
 /* After pushing wrapup text, this completes the bookkeeping.  */
 void
-m4_push_wrapup_finish (void)
+m4__push_wrapup_finish (void)
 {
   m4__make_text_link (wrapup_stack, &wsp->u.u_c.chain, &wsp->u.u_c.end);
   assert (wsp->u.u_c.end->type != M4__CHAIN_LOC);
@@ -1064,12 +1074,9 @@ pop_input (m4 *context, bool cleanup)
       : (isp->funcs->peek_func (isp, context, true) != CHAR_RETRY))
     return false;
 
-  if (tmp != NULL)
-    {
-      obstack_free (current_input, isp);
-      m4__quote_uncache (M4SYNTAX);
-      next = NULL;     /* might be set in m4_push_string_init () */
-    }
+  obstack_free (current_input, isp);
+  m4__quote_uncache (M4SYNTAX);
+  next = NULL; /* might be set in m4_push_string_init () */
 
   isp = tmp;
   input_change = true;
@@ -1088,7 +1095,7 @@ m4_pop_wrapup (m4 *context)
   obstack_free (current_input, NULL);
   free (current_input);
 
-  if (wsp == NULL)
+  if (wsp == &input_eof)
     {
       obstack_free (wrapup_stack, NULL);
       m4_set_current_file (context, NULL);
@@ -1108,7 +1115,7 @@ m4_pop_wrapup (m4 *context)
   obstack_init (wrapup_stack);
 
   isp = wsp;
-  wsp = NULL;
+  wsp = &input_eof;
   input_change = true;
 
   return true;
@@ -1120,24 +1127,14 @@ m4_pop_wrapup (m4 *context)
 static void
 init_builtin_token (m4 *context, m4_symbol_value *token)
 {
-  if (isp->funcs == &builtin_funcs)
-    {
-      assert (isp->u.builtin);
-      if (token)
-       m4__set_symbol_value_builtin (token, isp->u.builtin);
-      isp->u.builtin = NULL;
-    }
-  else
-    {
-      m4__symbol_chain *chain;
-      assert (isp->funcs == &composite_funcs);
-      chain = isp->u.u_c.chain;
-      assert (!chain->quote_age && chain->type == M4__CHAIN_FUNC
-             && chain->u.builtin);
-      if (token)
-       m4__set_symbol_value_builtin (token, chain->u.builtin);
-      chain->u.builtin = NULL;
-    }
+  m4__symbol_chain *chain;
+  assert (isp->funcs == &composite_funcs);
+  chain = isp->u.u_c.chain;
+  assert (!chain->quote_age && chain->type == M4__CHAIN_FUNC
+         && chain->u.builtin);
+  if (token)
+    m4__set_symbol_value_builtin (token, chain->u.builtin);
+  chain->u.builtin = NULL;
 }
 
 /* When a QUOTE token is seen, convert VALUE to a composite (if it is
@@ -1251,21 +1248,15 @@ init_argv_symbol (m4 *context, m4_obstack *obs, 
m4_symbol_value *value)
    for append_quote_token; otherwise, if ALLOW_ARGV, and the current
    input matches an argv reference with the correct quoting, return
    CHAR_ARGV and leave consumption of data for init_argv_symbol.  If
-   RETRY, then avoid returning CHAR_RETRY by popping input.  */
+   ALLOW_UNGET, then pop input to avoid returning CHAR_RETRY, and
+   ensure that unget_input can safely be called next.  */
 static int
-next_char (m4 *context, bool allow_quote, bool allow_argv, bool retry)
+next_char (m4 *context, bool allow_quote, bool allow_argv, bool allow_unget)
 {
   int ch;
 
   while (1)
     {
-      if (isp == NULL)
-       {
-         m4_set_current_file (context, NULL);
-         m4_set_current_line (context, 0);
-         return CHAR_EOF;
-       }
-
       if (input_change)
        {
          m4_set_current_file (context, isp->file);
@@ -1274,9 +1265,9 @@ next_char (m4 *context, bool allow_quote, bool 
allow_argv, bool retry)
 
       assert (isp->funcs->read_func);
       while (((ch = isp->funcs->read_func (isp, context, allow_quote,
-                                          allow_argv, !retry))
+                                          allow_argv, allow_unget))
              != CHAR_RETRY)
-            || !retry)
+            || allow_unget)
        {
          /* if (!IS_IGNORE (ch)) */
          return ch;
@@ -1299,15 +1290,12 @@ peek_char (m4 *context, bool allow_argv)
 
   while (1)
     {
-      if (block == NULL)
-       return CHAR_EOF;
-
       assert (block->funcs->peek_func);
       ch = block->funcs->peek_func (block, context, allow_argv);
       if (ch != CHAR_RETRY)
        {
 /*       if (IS_IGNORE (ch)) */
-/*         return next_char (context, false, true, true); */
+/*         return next_char (context, false, true, false); */
          return ch;
        }
 
@@ -1317,11 +1305,11 @@ peek_char (m4 *context, bool allow_argv)
 
 /* The function unget_input () puts back a character on the input
    stack, using an existing input_block if possible.  This is not safe
-   to call except immediately after next_char(context, aq, aa, false).  */
+   to call except immediately after next_char(context, aq, aa, true).  */
 static void
 unget_input (int ch)
 {
-  assert (isp != NULL && isp->funcs->unget_func != NULL);
+  assert (isp->funcs->unget_func != NULL);
   isp->funcs->unget_func (isp, ch);
 }
 
@@ -1335,7 +1323,7 @@ m4_skip_line (m4 *context, const char *name)
   const char *file = m4_get_current_file (context);
   int line = m4_get_current_line (context);
 
-  while ((ch = next_char (context, false, false, true)) != CHAR_EOF
+  while ((ch = next_char (context, false, false, false)) != CHAR_EOF
         && ch != '\n')
     ;
   if (ch == CHAR_EOF)
@@ -1381,14 +1369,14 @@ match_input (m4 *context, const char *s, bool consume)
   if (s[1] == '\0')
     {
       if (consume)
-       next_char (context, false, false, true);
+       next_char (context, false, false, false);
       return true;                     /* short match */
     }
 
-  next_char (context, false, false, true);
+  next_char (context, false, false, false);
   for (n = 1, t = s++; (ch = peek_char (context, false)) == to_uchar (*s++); )
     {
-      next_char (context, false, false, true);
+      next_char (context, false, false, false);
       n++;
       if (*s == '\0')          /* long match */
        {
@@ -1435,7 +1423,7 @@ consume_syntax (m4 *context, m4_obstack *obs, unsigned 
int syntax)
         by CHAR_RETRY.  We exploit the fact that CHAR_EOF,
         CHAR_BUILTIN, CHAR_QUOTE, and CHAR_ARGV do not satisfy any
         syntax categories.  */
-      while ((ch = next_char (context, allow, allow, false)) != CHAR_RETRY
+      while ((ch = next_char (context, allow, allow, true)) != CHAR_RETRY
             && m4_has_syntax (M4SYNTAX, ch, syntax))
        {
          assert (ch < CHAR_EOF);
@@ -1448,7 +1436,7 @@ consume_syntax (m4 *context, m4_obstack *obs, unsigned 
int syntax)
            {
              assert (ch < CHAR_EOF);
              obstack_1grow (obs, ch);
-             next_char (context, false, false, true);
+             next_char (context, false, false, false);
              continue;
            }
          return ch == CHAR_EOF;
@@ -1478,8 +1466,8 @@ m4_input_init (m4 *context)
   obstack_init (&token_stack);
   token_bottom = obstack_finish (&token_stack);
 
-  isp = NULL;
-  wsp = NULL;
+  isp = &input_eof;
+  wsp = &input_eof;
   next = NULL;
 
   start_of_input_line = false;
@@ -1489,8 +1477,8 @@ m4_input_init (m4 *context)
 void
 m4_input_exit (void)
 {
-  assert (current_input == NULL);
-  assert (wrapup_stack == NULL);
+  assert (!current_input && isp == &input_eof);
+  assert (!wrapup_stack && wsp == &input_eof);
   obstack_free (&file_names, NULL);
   obstack_free (&token_stack, NULL);
 }
@@ -1536,7 +1524,7 @@ 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),
-                   true);
+                   false);
     if (ch == CHAR_EOF)                        /* EOF */
       {
 #ifdef DEBUG_INPUT
@@ -1568,7 +1556,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
     if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ESCAPE))
       {                                        /* ESCAPED WORD */
        obstack_1grow (&token_stack, ch);
-       if ((ch = next_char (context, false, false, true)) < CHAR_EOF)
+       if ((ch = next_char (context, false, false, false)) < CHAR_EOF)
          {
            obstack_1grow (&token_stack, ch);
            if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ALPHA))
@@ -1597,7 +1585,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
        while (1)
          {
            ch = next_char (context, obs && m4__quote_age (M4SYNTAX), false,
-                           true);
+                           false);
            if (ch == CHAR_EOF)
              m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
                                _("end of file in string"));
@@ -1614,7 +1602,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
                    ch = peek_char (context, false);
                    if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_RQUOTE))
                      {
-                       ch = next_char (context, false, false, true);
+                       ch = next_char (context, false, false, false);
 #ifdef DEBUG_INPUT
                        m4_print_token (context, "next_token", M4_TOKEN_MACDEF,
                                        token);
@@ -1656,7 +1644,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
        assert (!m4__quote_age (M4SYNTAX));
        while (1)
          {
-           ch = next_char (context, false, false, true);
+           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"));
@@ -1674,7 +1662,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
                    if (MATCH (context, ch, context->syntax->quote.str2,
                               false))
                      {
-                       ch = next_char (context, false, false, true);
+                       ch = next_char (context, false, false, false);
                        MATCH (context, ch, context->syntax->quote.str2, true);
 #ifdef DEBUG_INPUT
                        m4_print_token (context, "next_token", M4_TOKEN_MACDEF,
@@ -1714,7 +1702,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
        obstack_1grow (obs_safe, ch);
        while (1)
          {
-           ch = next_char (context, false, false, true);
+           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"));
@@ -1746,7 +1734,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
                      context->syntax->comm.len1);
        while (1)
          {
-           ch = next_char (context, false, false, true);
+           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"));
@@ -1956,7 +1944,8 @@ m4_print_token (m4 *context, const char *s, 
m4__token_type type,
   if (token)
     {
       obstack_init (&obs);
-      m4_symbol_value_print (context, token, &obs, NULL, false, NULL, true);
+      m4__symbol_value_print (context, token, &obs, NULL, false, NULL, NULL,
+                             true);
       len = obstack_object_size (&obs);
       xfprintf (stderr, "%s\n", quotearg_style_mem (c_maybe_quoting_style,
                                                    obstack_finish (&obs),
diff --git a/m4/m4module.h b/m4/m4module.h
index 5c1f4e8..ced18d5 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -284,9 +284,6 @@ 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);
-extern bool    m4_symbol_value_print   (m4 *, m4_symbol_value *, m4_obstack *,
-                                        const m4_string_pair *, bool,
-                                        size_t *, bool);
 extern void    m4_symbol_print         (m4 *, m4_symbol *, m4_obstack *,
                                         const m4_string_pair *, bool, size_t,
                                         bool);
@@ -343,8 +340,6 @@ extern void         m4_set_symbol_value_placeholder 
(m4_symbol_value *,
 extern m4_symbol_value *m4_builtin_find_by_name (m4_module *, const char *);
 extern m4_symbol_value *m4_builtin_find_by_func (m4_module *,
                                                  m4_builtin_func *);
-extern void m4_builtin_print (m4_obstack *, const m4_builtin *, bool,
-                             const m4_string_pair *, m4_module *);
 
 
 
@@ -364,9 +359,6 @@ extern bool m4_arg_empty            (m4_macro_args *, 
size_t);
 extern size_t  m4_arg_len              (m4 *, m4_macro_args *, size_t);
 extern m4_builtin_func *m4_arg_func    (m4_macro_args *, size_t);
 extern m4_obstack *m4_arg_scratch      (m4 *);
-extern bool    m4_arg_print            (m4 *, m4_obstack *, m4_macro_args *,
-                                        size_t, const m4_string_pair *, bool,
-                                        const char *, size_t *, bool, bool);
 extern m4_macro_args *m4_make_argv_ref (m4 *, m4_macro_args *, const char *,
                                         size_t, bool, bool);
 extern void    m4_push_arg             (m4 *, m4_obstack *, m4_macro_args *,
@@ -501,11 +493,9 @@ extern     void    m4_skip_line    (m4 *context, const 
char *);
 /* push back input */
 
 extern void    m4_push_file    (m4 *, FILE *, const char *, bool);
-extern void    m4_push_builtin (m4 *, m4_symbol_value *);
+extern void    m4_push_builtin (m4 *, m4_obstack *, m4_symbol_value *);
 extern m4_obstack      *m4_push_string_init    (m4 *);
 extern m4_input_block  *m4_push_string_finish  (void);
-extern m4_obstack      *m4_push_wrapup_init    (m4 *);
-extern void    m4_push_wrapup_finish           (void);
 extern bool    m4_pop_wrapup   (m4 *);
 extern void    m4_input_print  (m4 *, m4_obstack *, m4_input_block *);
 
diff --git a/m4/m4private.h b/m4/m4private.h
index 86f18e8..48a0075 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -28,6 +28,7 @@
 
 typedef struct m4__search_path_info m4__search_path_info;
 typedef struct m4__macro_arg_stacks m4__macro_arg_stacks;
+typedef struct m4__symbol_chain m4__symbol_chain;
 
 typedef enum {
   M4_SYMBOL_VOID,              /* Traced but undefined, u is invalid.  */
@@ -163,6 +164,9 @@ typedef struct m4__builtin m4__builtin;
 
 extern void m4__set_symbol_value_builtin (m4_symbol_value *,
                                          const m4__builtin *);
+extern void m4__builtin_print (m4_obstack *, const m4__builtin *, bool,
+                              m4__symbol_chain **, const m4_string_pair *,
+                              bool);
 
 
 /* --- MODULE MANAGEMENT --- */
@@ -198,8 +202,6 @@ extern m4_module *  m4__module_find (const char *name);
 
 /* --- SYMBOL TABLE MANAGEMENT --- */
 
-typedef struct m4__symbol_chain m4__symbol_chain;
-
 struct m4_symbol
 {
   bool traced;                 /* True if this symbol is traced.  */
@@ -334,6 +336,10 @@ extern size_t      m4__adjust_refcount     (m4 *, size_t, 
bool);
 extern bool    m4__arg_adjust_refcount (m4 *, m4_macro_args *, bool);
 extern void    m4__push_arg_quote      (m4 *, m4_obstack *, m4_macro_args *,
                                         size_t, const m4_string_pair *);
+extern bool    m4__arg_print           (m4 *, m4_obstack *, m4_macro_args *,
+                                        size_t, const m4_string_pair *, bool,
+                                        m4__symbol_chain **, const char *,
+                                        size_t *, bool, bool);
 
 #define VALUE_NEXT(T)          ((T)->next)
 #define VALUE_MODULE(T)                ((T)->module)
@@ -418,7 +424,9 @@ struct m4_symbol_arg {
 
 extern void m4__symtab_remove_module_references (m4_symbol_table*,
                                                 m4_module *);
-
+extern bool m4__symbol_value_print (m4 *, m4_symbol_value *, m4_obstack *,
+                                   const m4_string_pair *, bool,
+                                   m4__symbol_chain **, size_t *, bool);
 
 
 
@@ -530,8 +538,13 @@ typedef enum {
 
 extern void            m4__make_text_link (m4_obstack *, m4__symbol_chain **,
                                            m4__symbol_chain **);
+extern void            m4__append_builtin (m4_obstack *, const m4__builtin *,
+                                           m4__symbol_chain **,
+                                           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 void            m4__push_wrapup_finish (void);
 extern m4__token_type  m4__next_token (m4 *, m4_symbol_value *, int *,
                                        m4_obstack *, bool, const char *);
 extern bool            m4__next_token_is_open (m4 *);
diff --git a/m4/macro.c b/m4/macro.c
index 6d1976d..3072444 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -663,8 +663,7 @@ collect_arguments (m4 *context, const char *name, size_t 
len,
   argv->wrapper = args.wrapper;
   argv->has_ref = args.has_ref;
   argv->has_func = args.has_func;
-  /* TODO allow funcs without crippling quote age.  */
-  if (args.quote_age != m4__quote_age (M4SYNTAX) || args.has_func)
+  if (args.quote_age != m4__quote_age (M4SYNTAX))
     argv->quote_age = 0;
   argv->arraylen = args.arraylen;
   return argv;
@@ -928,8 +927,8 @@ trace_prepre (m4 *context, const char *name, size_t id, 
m4_symbol_value *value)
     quotes = m4_get_syntax_quotes (M4SYNTAX);
   trace_header (context, id);
   trace_format (context, "%s ... = ", name);
-  m4_symbol_value_print (context, value, &context->trace_messages, quotes,
-                        false, &arg_length, module);
+  m4__symbol_value_print (context, value, &context->trace_messages, quotes,
+                         false, NULL, &arg_length, module);
   trace_flush (context);
 }
 
@@ -950,8 +949,8 @@ trace_pre (m4 *context, size_t id, m4_macro_args *argv)
       if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
        quotes = m4_get_syntax_quotes (M4SYNTAX);
       trace_format (context, "(");
-      m4_arg_print (context, &context->trace_messages, argv, 1, quotes, false,
-                   ", ", &arg_length, true, module);
+      m4__arg_print (context, &context->trace_messages, argv, 1, quotes, false,
+                    NULL, ", ", &arg_length, true, module);
       trace_format (context, ")");
     }
 }
@@ -1250,7 +1249,6 @@ m4_arg_text (m4 *context, m4_macro_args *argv, size_t 
index)
   value = m4_arg_symbol (argv, index);
   if (m4_is_symbol_value_text (value))
     return m4_get_symbol_value_text (value);
-  /* TODO - concatenate functions.  */
   assert (value->type == M4_SYMBOL_COMP);
   chain = value->u.u_c.chain;
   obs = m4_arg_scratch (context);
@@ -1262,11 +1260,11 @@ m4_arg_text (m4 *context, m4_macro_args *argv, size_t 
index)
          obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
          break;
        case M4__CHAIN_ARGV:
-         m4_arg_print (context, obs, chain->u.u_a.argv, chain->u.u_a.index,
-                       m4__quote_cache (M4SYNTAX, NULL, chain->quote_age,
-                                        chain->u.u_a.quotes),
-                       argv->flatten || chain->u.u_a.flatten, NULL, NULL,
-                       false, false);
+         m4__arg_print (context, obs, chain->u.u_a.argv, chain->u.u_a.index,
+                        m4__quote_cache (M4SYNTAX, NULL, chain->quote_age,
+                                         chain->u.u_a.quotes),
+                        argv->flatten || chain->u.u_a.flatten, NULL, NULL,
+                        NULL, false, false);
          break;
        default:
          assert (!"m4_arg_text");
@@ -1292,6 +1290,7 @@ m4_arg_equal (m4 *context, m4_macro_args *argv, size_t 
indexa, size_t indexb)
   m4__symbol_chain tmpb;
   m4__symbol_chain *ca = &tmpa;
   m4__symbol_chain *cb = &tmpb;
+  m4__symbol_chain *chain;
   m4_obstack *obs = m4_arg_scratch (context);
 
   /* Quick tests.  */
@@ -1350,34 +1349,33 @@ m4_arg_equal (m4 *context, m4_macro_args *argv, size_t 
indexa, size_t indexb)
     {
       if (ca->type == M4__CHAIN_ARGV)
        {
-         tmpa.next = ca->next;
+         tmpa.next = NULL;
          tmpa.type = M4__CHAIN_STR;
-         /* TODO support funcs in address@hidden  */
-         assert (!ca->u.u_a.has_func || argv->flatten || ca->u.u_a.flatten);
-         m4_arg_print (context, obs, ca->u.u_a.argv, ca->u.u_a.index,
-                       m4__quote_cache (M4SYNTAX, NULL, ca->quote_age,
-                                        ca->u.u_a.quotes),
-                       argv->flatten || ca->u.u_a.flatten, NULL, NULL, false,
-                       false);
-         tmpa.u.u_s.len = obstack_object_size (obs);
-         tmpa.u.u_s.str = (char *) obstack_finish (obs);
-         ca = &tmpa;
+         tmpa.u.u_s.str = NULL;
+         tmpa.u.u_s.len = 0;
+         chain = &tmpa;
+         m4__arg_print (context, obs, ca->u.u_a.argv, ca->u.u_a.index,
+                        m4__quote_cache (M4SYNTAX, NULL, ca->quote_age,
+                                         ca->u.u_a.quotes),
+                        argv->flatten || ca->u.u_a.flatten, &chain, NULL,
+                        NULL, false, false);
+         assert (obstack_object_size (obs) == 0 && chain != &tmpa);
+         chain->next = ca->next;
+         ca = tmpa.next;
          continue;
        }
       if (cb->type == M4__CHAIN_ARGV)
        {
-         tmpb.next = cb->next;
+         tmpb.next = NULL;
          tmpb.type = M4__CHAIN_STR;
-         /* TODO support funcs in address@hidden  */
-         assert (!cb->u.u_a.has_func || argv->flatten || cb->u.u_a.flatten);
-         m4_arg_print (context, obs, cb->u.u_a.argv, cb->u.u_a.index,
-                       m4__quote_cache (M4SYNTAX, NULL, cb->quote_age,
-                                        cb->u.u_a.quotes),
-                       argv->flatten || cb->u.u_a.flatten, NULL, NULL, false,
-                       false);
-         tmpb.u.u_s.len = obstack_object_size (obs);
-         tmpb.u.u_s.str = (char *) obstack_finish (obs);
-         cb = &tmpb;
+         m4__arg_print (context, obs, cb->u.u_a.argv, cb->u.u_a.index,
+                        m4__quote_cache (M4SYNTAX, NULL, cb->quote_age,
+                                         cb->u.u_a.quotes),
+                        argv->flatten || cb->u.u_a.flatten, &chain, NULL,
+                        NULL, false, false);
+         assert (obstack_object_size (obs) == 0 && chain != &tmpb);
+         chain->next = cb->next;
+         cb = tmpb.next;
          continue;
        }
       if (ca->type == M4__CHAIN_FUNC)
@@ -1504,20 +1502,23 @@ m4_arg_func (m4_macro_args *argv, size_t index)
 
 /* Dump a representation of ARGV to the obstack OBS, starting with
    argument INDEX.  If QUOTES is non-NULL, each argument is displayed
-   with those quotes.  If FLATTEN, builtins are ignored.  Separate
-   arguments with SEP, which defaults to a comma.  If MAX_LEN is
-   non-NULL, truncate the output after *MAX_LEN bytes are output and
-   return true; otherwise, return false, and reduce *MAX_LEN by the
-   number of bytes output.  If QUOTE_EACH, the truncation length is
-   reset for each argument, quotes do not count against length, and
-   all arguments are printed; otherwise, quotes count against the
-   length and trailing arguments may be discarded.  If MODULE, print
-   any details about originating modules; modules do not count against
-   truncation length.  */
+   with those quotes.  If FLATTEN, builtins are converted to empty
+   quotes; if CHAINP, *CHAINP is updated with macro tokens; otherwise,
+   builtins are represented by their name.  Separate arguments with
+   SEP, which defaults to a comma.  If MAX_LEN is non-NULL, truncate
+   the output after *MAX_LEN bytes are output and return true;
+   otherwise, return false, and reduce *MAX_LEN by the number of bytes
+   output.  If QUOTE_EACH, the truncation length is reset for each
+   argument, quotes do not count against length, and all arguments are
+   printed; otherwise, quotes count against the length and trailing
+   arguments may be discarded.  If MODULE, print any details about
+   originating modules; modules do not count against truncation
+   length.  MAX_LEN and CHAINP may not both be specified.  */
 bool
-m4_arg_print (m4 *context, m4_obstack *obs, m4_macro_args *argv, size_t index,
-             const m4_string_pair *quotes, bool flatten, const char *sep,
-             size_t *max_len, bool quote_each, bool module)
+m4__arg_print (m4 *context, m4_obstack *obs, m4_macro_args *argv, size_t index,
+              const m4_string_pair *quotes, bool flatten,
+              m4__symbol_chain **chainp, const char *sep, size_t *max_len,
+              bool quote_each, bool module)
 {
   size_t len = max_len ? *max_len : SIZE_MAX;
   size_t i;
@@ -1525,6 +1526,8 @@ m4_arg_print (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, size_t index,
   size_t sep_len;
   size_t *plen = quote_each ? NULL : &len;
 
+  if (chainp)
+    assert (!max_len && *chainp);
   if (!sep)
     sep = ",";
   sep_len = strlen (sep);
@@ -1539,9 +1542,9 @@ m4_arg_print (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, size_t index,
          && m4_shipout_string_trunc (obs, quotes->str1, quotes->len1, NULL,
                                      plen))
        return true;
-      if (m4_symbol_value_print (context, arg_symbol (argv, i, NULL, flatten),
-                                obs, quote_each ? quotes : NULL, flatten,
-                                &len, module))
+      if (m4__symbol_value_print (context, arg_symbol (argv, i, NULL, flatten),
+                                 obs, quote_each ? quotes : NULL, flatten,
+                                 chainp, &len, module))
        return true;
       if (quotes && !quote_each
          && m4_shipout_string_trunc (obs, quotes->str2, quotes->len2, NULL,
@@ -1550,6 +1553,8 @@ m4_arg_print (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, size_t index,
     }
   if (max_len)
     *max_len = len;
+  else if (chainp)
+    m4__make_text_link (obs, NULL, chainp);
   return false;
 }
 
@@ -1680,12 +1685,13 @@ m4_wrap_args (m4 *context, m4_macro_args *argv)
   m4_obstack *obs;
   m4_symbol_value *value;
   m4__symbol_chain *chain;
+  m4__symbol_chain **end;
   size_t limit = m4_get_posixly_correct_opt (context) ? 2 : argv->argc;
 
   if (limit == 2 && m4_arg_empty (argv, 1))
     return;
 
-  obs = m4_push_wrapup_init (context);
+  obs = m4__push_wrapup_init (context, &end);
   for (i = 1; i < limit; i++)
     {
       if (i != 1)
@@ -1698,8 +1704,7 @@ m4_wrap_args (m4 *context, m4_macro_args *argv)
                        m4_get_symbol_value_len (value));
          break;
        case M4_SYMBOL_FUNC:
-         /* TODO allow builtins.  */
-         assert (false);
+         m4__append_builtin (obs, value->u.builtin, NULL, end);
          break;
        case M4_SYMBOL_COMP:
          chain = value->u.u_c.chain;
@@ -1711,17 +1716,16 @@ m4_wrap_args (m4 *context, m4_macro_args *argv)
                  obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
                  break;
                case M4__CHAIN_FUNC:
-                 /* TODO allow builtins.  */
-                 assert (false);
+                 m4__append_builtin (obs, chain->u.builtin, NULL, end);
                  break;
                case M4__CHAIN_ARGV:
-                 m4_arg_print (context, obs, chain->u.u_a.argv,
-                               chain->u.u_a.index,
-                               m4__quote_cache (M4SYNTAX, NULL,
-                                                chain->quote_age,
-                                                chain->u.u_a.quotes),
-                               chain->u.u_a.flatten, NULL, NULL, false,
-                               false);
+                 m4__arg_print (context, obs, chain->u.u_a.argv,
+                                chain->u.u_a.index,
+                                m4__quote_cache (M4SYNTAX, NULL,
+                                                 chain->quote_age,
+                                                 chain->u.u_a.quotes),
+                                chain->u.u_a.flatten, end, NULL, NULL, false,
+                                false);
                  break;
                default:
                  assert (!"m4_wrap_args");
@@ -1735,7 +1739,7 @@ m4_wrap_args (m4 *context, m4_macro_args *argv)
          abort ();
        }
     }
-  m4_push_wrapup_finish ();
+  m4__push_wrapup_finish ();
 }
 
 
diff --git a/m4/symtab.c b/m4/symtab.c
index 7fa7ccc..f4bb855 100644
--- a/m4/symtab.c
+++ b/m4/symtab.c
@@ -543,16 +543,17 @@ m4_set_symbol_name_traced (m4_symbol_table *symtab, const 
char *name,
 }
 
 /* Grow OBS with a text representation of VALUE.  If QUOTES, then use
-   it to surround a text definition.  If FLATTEN, then flatten builtin
-   macros to the empty string.  If MAXLEN, then truncate text
-   definitions to *MAXLEN, and adjust by how many characters are
-   printed.  If MODULE, then include which module defined a builtin.
-   Return true if the output was truncated.  QUOTES and MODULE do not
-   count against the truncation length.  */
+   it to surround a text definition.  If FLATTEN, builtins are
+   converted to empty quotes; if CHAINP, *CHAINP is updated with macro
+   tokens; otherwise, builtins are represented by their name.  If
+   MAXLEN, then truncate text definitions to *MAXLEN, and adjust by
+   how many characters are printed.  If MODULE, then include which
+   module defined a builtin.  Return true if the output was truncated.
+   QUOTES and MODULE do not count against the truncation length.  */
 bool
-m4_symbol_value_print (m4 *context, m4_symbol_value *value, m4_obstack *obs,
-                      const m4_string_pair *quotes, bool flatten,
-                      size_t *maxlen, bool module)
+m4__symbol_value_print (m4 *context, m4_symbol_value *value, m4_obstack *obs,
+                       const m4_string_pair *quotes, bool flatten,
+                       m4__symbol_chain **chainp, size_t *maxlen, bool module)
 {
   const char *text;
   m4__symbol_chain *chain;
@@ -568,8 +569,8 @@ m4_symbol_value_print (m4 *context, m4_symbol_value *value, 
m4_obstack *obs,
        result = true;
       break;
     case M4_SYMBOL_FUNC:
-      m4_builtin_print (obs, m4_get_symbol_value_builtin (value), flatten,
-                       quotes, module ? VALUE_MODULE (value) : NULL);
+      m4__builtin_print (obs, value->u.builtin, flatten, chainp, quotes,
+                        module);
       module = false;
       break;
     case M4_SYMBOL_PLACEHOLDER:
@@ -607,22 +608,21 @@ m4_symbol_value_print (m4 *context, m4_symbol_value 
*value, m4_obstack *obs,
                result = true;
              break;
            case M4__CHAIN_FUNC:
-             m4_builtin_print (obs, &chain->u.builtin->builtin, flatten,
-                               quotes,
-                               module ? chain->u.builtin->module : NULL);
+             m4__builtin_print (obs, chain->u.builtin, flatten, chainp,
+                                quotes, module);
              break;
            case M4__CHAIN_ARGV:
-             if (m4_arg_print (context, obs, chain->u.u_a.argv,
-                               chain->u.u_a.index,
-                               m4__quote_cache (M4SYNTAX, NULL,
-                                                chain->quote_age,
-                                                chain->u.u_a.quotes),
-                               chain->u.u_a.flatten, NULL, &len, false,
-                               module))
+             if (m4__arg_print (context, obs, chain->u.u_a.argv,
+                                chain->u.u_a.index,
+                                m4__quote_cache (M4SYNTAX, NULL,
+                                                 chain->quote_age,
+                                                 chain->u.u_a.quotes),
+                                chain->u.u_a.flatten, chainp, NULL, &len,
+                                false, module))
                result = true;
              break;
            default:
-             assert (!"m4_symbol_value_print");
+             assert (!"m4__symbol_value_print");
              abort ();
            }
            chain = chain->next;
@@ -631,7 +631,7 @@ m4_symbol_value_print (m4 *context, m4_symbol_value *value, 
m4_obstack *obs,
        obstack_grow (obs, quotes->str2, quotes->len2);
       break;
     default:
-      assert (!"m4_symbol_value_print");
+      assert (!"m4__symbol_value_print");
       abort ();
     }
 
@@ -665,7 +665,8 @@ m4_symbol_print (m4 *context, m4_symbol *symbol, m4_obstack 
*obs,
   assert (obs);
 
   value = m4_get_symbol_value (symbol);
-  m4_symbol_value_print (context, value, obs, quotes, false, &len, module);
+  m4__symbol_value_print (context, value, obs, quotes, false, NULL, &len,
+                         module);
   if (stack)
     {
       value = VALUE_NEXT (value);
@@ -674,8 +675,8 @@ m4_symbol_print (m4 *context, m4_symbol *symbol, m4_obstack 
*obs,
          obstack_1grow (obs, ',');
          obstack_1grow (obs, ' ');
          len = arg_length;
-         m4_symbol_value_print (context, value, obs, quotes, false, &len,
-                                module);
+         m4__symbol_value_print (context, value, obs, quotes, false, NULL,
+                                 &len, module);
          value = VALUE_NEXT (value);
        }
     }
@@ -873,7 +874,8 @@ dump_symbol_CB (m4_symbol_table *symtab, const char *name,
     {
       m4_obstack obs;
       obstack_init (&obs);
-      m4_symbol_value_print (context, value, &obs, NULL, false, NULL, true);
+      m4__symbol_value_print (context, value, &obs, NULL, false, NULL, NULL,
+                             true);
       xfprintf (stderr, "%s", (char *) obstack_finish (&obs));
       obstack_free (&obs, NULL);
     }
diff --git a/modules/gnu.c b/modules/gnu.c
index b481581..f0d3a44 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -424,7 +424,7 @@ M4BUILTIN_HANDLER (builtin)
            m4_warn (context, 0, me, _("undefined builtin `%s'"), name);
          else
            {
-             m4_push_builtin (context, value);
+             m4_push_builtin (context, obs, value);
              free (value);
            }
        }
diff --git a/modules/m4.c b/modules/m4.c
index d484f4d..f4013ef 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -78,7 +78,7 @@ extern void m4_make_temp     (m4 *context, m4_obstack *obs, 
const char *macro,
   BUILTIN (index,      false,  true,   true,   2,      2  )    \
   BUILTIN (len,                false,  true,   true,   1,      1  )    \
   BUILTIN (m4exit,     false,  false,  false,  0,      1  )    \
-  BUILTIN (m4wrap,     false,  true,   false,  1,      -1 )    \
+  BUILTIN (m4wrap,     true,   true,   false,  1,      -1 )    \
   BUILTIN (maketemp,   false,  true,   false,  1,      1  )    \
   BUILTIN (mkstemp,    false,  true,   false,  1,      1  )    \
   BUILTIN (popdef,     true,   true,   false,  1,      -1 )    \
@@ -364,7 +364,7 @@ M4BUILTIN_HANDLER (defn)
        m4_shipout_string (context, obs, m4_get_symbol_text (symbol),
                           m4_get_symbol_len (symbol), true);
       else if (m4_is_symbol_func (symbol))
-       m4_push_builtin (context, m4_get_symbol_value (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"),
@@ -767,15 +767,17 @@ M4BUILTIN_HANDLER (mkstemp)
 /* Print all arguments on standard error.  */
 M4BUILTIN_HANDLER (errprint)
 {
-  size_t len;
+  size_t i;
 
-  assert (obstack_object_size (obs) == 0);
-  m4_arg_print (context, obs, argv, 1, NULL, true, " ", NULL, false, false);
   m4_sysval_flush (context, false);
-  len = obstack_object_size (obs);
   /* The close_stdin module makes it safe to skip checking the return
-     value here.  */
-  fwrite (obstack_finish (obs), 1, len, stderr);
+     values here.  */
+  fwrite (M4ARG (1), 1, M4ARGLEN (1), stderr);
+  for (i = 2; i < m4_arg_argc (argv); i++)
+    {
+      fputc (' ', stderr);
+      fwrite (M4ARG (i), 1, M4ARGLEN (i), stderr);
+    }
   fflush (stderr);
 }
 


hooks/post-receive
--
GNU M4 source repository




reply via email to

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