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


From: Eric Blake
Subject: [SCM] GNU M4 source repository branch, master, updated. cvs-readonly-55-g9123df1
Date: Sat, 02 Feb 2008 23:20:16 +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=9123df1a730177cdfcd9cbbe17c5275c9473bccf

The branch, master has been updated
       via  9123df1a730177cdfcd9cbbe17c5275c9473bccf (commit)
       via  7fe816278fe35846cf4f02e8ca38e050fd10506c (commit)
       via  89ceca3d1d57ea666822040018b5036d84c087cc (commit)
      from  410176a44990a565e22770e7d66bbc739cf0dcb4 (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 9123df1a730177cdfcd9cbbe17c5275c9473bccf
Author: Eric Blake <address@hidden>
Date:   Sat Feb 2 14:55:08 2008 -0700

    Consistently use size_t for number of arguments.
    
    * m4/m4module.h (m4_builtin_func): Alter prototype.
    (struct m4_builtin): Adjust type of min_args, max_args.
    (M4BUILTIN, M4BUILTIN_HANDLER): Adjust all builtins.
    (m4_bad_argc, m4_dump_args, m4_macro_call, m4_arg_argc)
    (m4_arg_symbol, m4_is_arg_text, m4_is_arg_func, m4_arg_text)
    (m4_arg_equal, m4_arg_empty, m4_arg_len, m4_arg_func)
    (m4_arg_print, m4_push_arg): Adjust all clients.
    * m4/m4private.h (struct m4__symbol_chain, m4_symbol_value)
    (m4_macro_args): Adjust type of various fields.
    (m4__push_arg_quote): Adjust all clients.
    * m4/input.c (m4_pop_wrapup): Likewise.
    * m4/macro.c (m4_macro_call, trace_pre, make_argv_ref)
    (arg_symbol, m4_arg_symbol, m4_is_arg_text, m4_is_arg_func)
    (m4_arg_text, m4_arg_equal, m4_arg_empty, m4_arg_len)
    (m4_arg_func, m4_arg_print, m4_make_argv_ref, m4_push_arg)
    (m4__push_arg_quote, m4_push_args, m4_arg_argc): Likewise.
    * m4/utility.c (m4_bad_argc, m4_dump_args): Likewise.
    * modules/evalparse.c (m4_evaluate): Likewise.
    * modules/gnu.c (changesyntax): Likewise.
    * modules/m4.c (m4_dump_symbols, undefine, popdef, ifelse, defn)
    (undivert, traceon, traceoff): Likewise.
    * modules/m4.h (m4_dump_symbols_func): Likewise.
    * modules/perl.c (perleval): Likewise.
    
    Signed-off-by: Eric Blake <address@hidden>

commit 7fe816278fe35846cf4f02e8ca38e050fd10506c
Author: Eric Blake <address@hidden>
Date:   Sat Feb 2 07:34:08 2008 -0700

    Stage 14b: allow pushing argv references.
    
    * m4/m4private.h (struct m4__symbol_chain): Add comma and quotes
    fields.
    (struct m4_macro_args): Add level field.
    (m4__arg_adjust_refcount, m4__push_arg_quote): New prototypes.
    * m4/input.c (m4__push_symbol, composite_peek, composite_read)
    (composite_unget, composite_clean, composite_print): Support $@
    refs.
    * m4/macro.c (collect_arguments): Populate new field.
    (expand_macro): Move argv cleanup...
    (m4__arg_adjust_refcount): ...to this new function.
    (m4_arg_symbol, m4_make_argv_ref, m4_push_arg): Factor...
    (arg_symbol, make_argv_ref, m4__push_arg_quote): ...to these new
    helper functions, to add parameters.
    (m4_push_args): Adjust caller.
    * m4/symtab.c (m4_symbol_value_print): Likewise.
    
    Signed-off-by: Eric Blake <address@hidden>

commit 89ceca3d1d57ea666822040018b5036d84c087cc
Author: Eric Blake <address@hidden>
Date:   Sat Feb 2 07:33:34 2008 -0700

    Stage 14a: allow printing argv references.
    
    * m4/m4module.h (m4_arg_print): New prototype.
    (m4_symbol_value_print): Alter prototype.
    * m4/input.c (struct input_funcs): Add parameter to peek_func.
    (file_peek, builtin_peek, string_peek): Ignore new parameter.
    (composite_peek): Ignore new parameter, for now.
    (composite_clean, pop_input): Rework to minimize indirection, and
    to avoid infinite recursion in next patch.
    * m4/macro.c (trace_prepre, trace_pre): Adjust callers.
    (m4_arg_print): New function.
    * m4/symtab.c (m4_symbol_value_print): Update signature.
    (m4_symbol_print): Update caller.
    * m4/output.c (m4_shipout_string_trunc): Update comments.
    * m4/syntax.c (set_quote_age): Require comma as argument separator
    when dealing with $@ as a unit.
    * tests/builtins.at (ifelse): Augment test.
    * doc/m4.texinfo (Changesyntax): Document changesyntax deficiency.
    
    Signed-off-by: Eric Blake <address@hidden>

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

Summary of changes:
 ChangeLog           |   80 ++++++++++++-
 doc/m4.texinfo      |   20 +++-
 ltdl/.cvsignore     |    1 +
 ltdl/.gitignore     |    1 +
 m4/input.c          |  112 ++++++++++++++-----
 m4/m4module.h       |   51 ++++----
 m4/m4private.h      |   20 ++-
 m4/macro.c          |  319 +++++++++++++++++++++++++++++++++------------------
 m4/output.c         |    2 +-
 m4/symtab.c         |  108 +++++++++--------
 m4/syntax.c         |   11 ++-
 m4/utility.c        |   12 +-
 modules/evalparse.c |    7 +-
 modules/gnu.c       |    2 +-
 modules/m4.c        |   21 ++--
 modules/m4.h        |    6 +-
 modules/perl.c      |    5 +-
 tests/builtins.at   |   20 +++
 18 files changed, 544 insertions(+), 254 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2ad8299..8ee5d0f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,76 @@
+2008-02-02  Eric Blake  <address@hidden>
+
+       Consistently use size_t for number of arguments.
+       * m4/m4module.h (m4_builtin_func): Alter prototype.
+       (struct m4_builtin): Adjust type of min_args, max_args.
+       (M4BUILTIN, M4BUILTIN_HANDLER): Adjust all builtins.
+       (m4_bad_argc, m4_dump_args, m4_macro_call, m4_arg_argc)
+       (m4_arg_symbol, m4_is_arg_text, m4_is_arg_func, m4_arg_text)
+       (m4_arg_equal, m4_arg_empty, m4_arg_len, m4_arg_func)
+       (m4_arg_print, m4_push_arg): Adjust all clients.
+       * m4/m4private.h (struct m4__symbol_chain, m4_symbol_value)
+       (m4_macro_args): Adjust type of various fields.
+       (m4__push_arg_quote): Adjust all clients.
+       * m4/input.c (m4_pop_wrapup): Likewise.
+       * m4/macro.c (m4_macro_call, trace_pre, make_argv_ref)
+       (arg_symbol, m4_arg_symbol, m4_is_arg_text, m4_is_arg_func)
+       (m4_arg_text, m4_arg_equal, m4_arg_empty, m4_arg_len)
+       (m4_arg_func, m4_arg_print, m4_make_argv_ref, m4_push_arg)
+       (m4__push_arg_quote, m4_push_args, m4_arg_argc): Likewise.
+       * m4/utility.c (m4_bad_argc, m4_dump_args): Likewise.
+       * modules/evalparse.c (m4_evaluate): Likewise.
+       * modules/gnu.c (changesyntax): Likewise.
+       * modules/m4.c (m4_dump_symbols, undefine, popdef, ifelse, defn)
+       (undivert, traceon, traceoff): Likewise.
+       * modules/m4.h (m4_dump_symbols_func): Likewise.
+       * modules/perl.c (perleval): Likewise.
+
+       Stage 14b: allow pushing argv references.
+       Push a $@ reference to the input engine in one go, rather than
+       pushing each element.  For now, argument collection still gets one
+       argument of a $@ at a time; but the penalties of this patch make
+       it easier to manage $@ efficiently in future patches.
+       Memory impact: noticeable penalty, due to larger struct and O(n)
+       to O(n^2) on unboxed recursion
+       Speed impact: noticeable penalty, due to more bookkeeping.
+       * m4/m4private.h (struct m4__symbol_chain): Add comma and quotes
+       fields.
+       (struct m4_macro_args): Add level field.
+       (m4__arg_adjust_refcount, m4__push_arg_quote): New prototypes.
+       * m4/input.c (m4__push_symbol, composite_peek, composite_read)
+       (composite_unget, composite_clean, composite_print): Support $@
+       refs.
+       * m4/macro.c (collect_arguments): Populate new field.
+       (expand_macro): Move argv cleanup...
+       (m4__arg_adjust_refcount): ...to this new function.
+       (m4_arg_symbol, m4_make_argv_ref, m4_push_arg): Factor...
+       (arg_symbol, make_argv_ref, m4__push_arg_quote): ...to these new
+       helper functions, to add parameters.
+       (m4_push_args): Adjust caller.
+       * m4/symtab.c (m4_symbol_value_print): Likewise.
+
+       Stage 14a: allow printing argv references.
+       Refactor symbol-value printing code for better sharing, and to
+       allow printing a contiguous text representation of a $@ ref.
+       Memory impact: none.
+       Speed impact: none.
+       * m4/m4module.h (m4_arg_print): New prototype.
+       (m4_symbol_value_print): Alter prototype.
+       * m4/input.c (struct input_funcs): Add parameter to peek_func.
+       (file_peek, builtin_peek, string_peek): Ignore new parameter.
+       (composite_peek): Ignore new parameter, for now.
+       (composite_clean, pop_input): Rework to minimize indirection, and
+       to avoid infinite recursion in next patch.
+       * m4/macro.c (trace_prepre, trace_pre): Adjust callers.
+       (m4_arg_print): New function.
+       * m4/symtab.c (m4_symbol_value_print): Update signature.
+       (m4_symbol_print): Update caller.
+       * m4/output.c (m4_shipout_string_trunc): Update comments.
+       * m4/syntax.c (set_quote_age): Require comma as argument separator
+       when dealing with $@ as a unit.
+       * tests/builtins.at (ifelse): Augment test.
+       * doc/m4.texinfo (Changesyntax): Document changesyntax deficiency.
+
 2008-01-31  Eric Blake  <address@hidden>
 
        Kill hack for M4 1.4.4.
@@ -47,7 +120,7 @@
        reused through multiple macro expansions.  Add hueristic that
        avoids creating new reference when pushing existing references.
        Memory impact: noticeable improvement due to better reference
-       reuse, except for boxed recursion doing more copying.
+       reuse, except for O(n) to O(n^2) copying in boxed recursion.
        Speed impact: slight penalty, due to more bookkeeping.
        * m4/m4private.h (m4__push_symbol): Adjust prototype.
        * m4/input.c (m4__push_symbol): Add parameter, and support
@@ -154,7 +227,7 @@
        action, so that an argument can be reused throughout macro
        recursion if it remains unchanged.
        Memory impact: noticeable improvement, due to more reuse in
-       argument collection stacks.
+       argument collection stacks; O(n^2) to O(n) on boxed recursion.
        Speed impact: noticeable improvement, due to less copying.
        * m4/m4module.h (m4_arg_text): Add parameter.
        (M4ARG): Adjust.
@@ -233,7 +306,8 @@
        creating a FIFO link.  Also start testing embedded NUL behavior.
        Until the argument collection engine also shares references, the
        memory usage increases.
-       Memory impact: noticeable penalty, due to longer life of argv.
+       Memory impact: noticeable penalty, due to longer life of argv
+       changing O(n) to O(n^2) on boxed recursion.
        Speed impact: slight improvement, due less data copying.
        * ltdl/m4/gnulib-cache.m4: Import memmem and quote modules.
        * m4/m4module.h (m4_arg_scratch): New prototype.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 5d87489..9e9dd46 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -4937,8 +4937,24 @@ Note how it is possible to have both long and short 
quotes, if
 The syntax table is initialized to be backwards compatible, so if you
 never call @code{changesyntax}, nothing will have changed.
 
-Debugging output continue to use @kbd{(}, @kbd{,} and @kbd{)} to show
-macro calls.
+For now, debugging output continues to use @kbd{(}, @kbd{,} and @kbd{)}
+to show macro calls; and macro expansions that result in a list of
+arguments (such as @samp{$@@} or @code{shift}) use @samp{,}, regardless
+of the current syntax settings.  However, this is likely to change in a
+future release, so it should not be relied on, particularly since it is
+next to impossible to write recursive macros if the argument separator
+doesn't match between expansion and rescanning.
+
address@hidden FIXME - changing syntax of , should not break iterative macros.
address@hidden
+$ @kbd{m4 -d}
+changesyntax(`,=|')traceon(`foo')define(`foo'|`$#:$@')
address@hidden
+foo(foo(1|2|3))
address@hidden: -2- foo(`1', `2', `3') -> `3:`1',`2',`3''
address@hidden: -1- foo(`3:1,2,3') -> `1:`3:1,2,3''
address@hidden:3:1,2,3
address@hidden example
 
 @node M4wrap
 @section Saving text until end of input
diff --git a/ltdl/.cvsignore b/ltdl/.cvsignore
index c2d1277..766a9f7 100644
--- a/ltdl/.cvsignore
+++ b/ltdl/.cvsignore
@@ -2,6 +2,7 @@
 *.la
 *.lo
 .deps
+.dirstamp
 .libs
 aclocal.m4
 argz.c
diff --git a/ltdl/.gitignore b/ltdl/.gitignore
index c2d1277..766a9f7 100644
--- a/ltdl/.gitignore
+++ b/ltdl/.gitignore
@@ -2,6 +2,7 @@
 *.la
 *.lo
 .deps
+.dirstamp
 .libs
 aclocal.m4
 argz.c
diff --git a/m4/input.c b/m4/input.c
index 9616d37..025ae0d 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -92,20 +92,20 @@
    maintains its own notion of the current file and line, so swapping
    between input blocks must update the context accordingly.  */
 
-static int     file_peek               (m4_input_block *);
+static int     file_peek               (m4_input_block *, m4 *);
 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 *);
+static int     builtin_peek            (m4_input_block *, m4 *);
 static int     builtin_read            (m4_input_block *, m4 *, 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 *);
+static int     string_peek             (m4_input_block *, m4 *);
 static int     string_read             (m4_input_block *, m4 *, bool, bool);
 static void    string_unget            (m4_input_block *, int);
 static void    string_print            (m4_input_block *, m4 *, m4_obstack *);
-static int     composite_peek          (m4_input_block *);
+static int     composite_peek          (m4_input_block *, m4 *);
 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);
@@ -130,7 +130,7 @@ struct input_funcs
 {
   /* Peek at input, return an unsigned char, CHAR_BUILTIN if it is a
      builtin, or CHAR_RETRY if none available.  */
-  int  (*peek_func)    (m4_input_block *);
+  int  (*peek_func)    (m4_input_block *, m4 *);
 
   /* Read input, return an unsigned char, CHAR_BUILTIN if it is a
      builtin, or CHAR_RETRY if none available.  If ALLOW_QUOTE, then
@@ -254,7 +254,7 @@ static struct input_funcs composite_funcs = {
 
 /* Input files, from command line or [s]include.  */
 static int
-file_peek (m4_input_block *me)
+file_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED)
 {
   int ch;
 
@@ -389,7 +389,7 @@ 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)
+builtin_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED)
 {
   if (me->u.u_b.read)
     return CHAR_RETRY;
@@ -474,7 +474,7 @@ m4_push_builtin (m4 *context, m4_symbol_value *token)
 
 /* Handle string expansion text.  */
 static int
-string_peek (m4_input_block *me)
+string_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED)
 {
   return me->u.u_s.len ? to_uchar (*me->u.u_s.str) : CHAR_RETRY;
 }
@@ -537,8 +537,8 @@ m4_push_string_init (m4 *context)
    level, or SIZE_MAX if VALUE is composite, its contents reside
    entirely on the current_input stack, and VALUE lives in temporary
    storage.  If VALUE is a simple string, then it belongs to the
-   current macro expansion.  If VALUE is composit, then each text link
-   has a level of SIZE_MAX if it belongs to the current macro
+   current macro expansion.  If VALUE is composite, then each text
+   link has a level of SIZE_MAX if it belongs to the current macro
    expansion, otherwise it is a back-reference where level tracks
    which stack it came from.  The resulting input block chain contains
    links with a level of SIZE_MAX if the text belongs to the input
@@ -556,7 +556,6 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, 
size_t level, bool inuse)
   m4__symbol_chain *chain;
 
   assert (next);
-  /* TODO - also accept composite chains with $@ refs.  */
 
   /* Speed consideration - for short enough symbols, the speed and
      memory overhead of parsing another INPUT_CHAIN link outweighs the
@@ -661,8 +660,12 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, 
size_t level, bool inuse)
       else
        next->u.u_c.chain = chain;
       next->u.u_c.end = chain;
-      assert (chain->type == M4__CHAIN_STR);
-      if (chain->u.u_s.level < SIZE_MAX)
+      if (chain->type == M4__CHAIN_ARGV)
+       {
+         assert (!chain->u.u_a.comma);
+         inuse |= m4__arg_adjust_refcount (context, chain->u.u_a.argv, true);
+       }
+      else if (chain->type == M4__CHAIN_STR && chain->u.u_s.level < SIZE_MAX)
        m4__adjust_refcount (context, chain->u.u_s.level, true);
       src_chain = src_chain->next;
     }
@@ -715,7 +718,7 @@ m4_push_string_finish (void)
    in FIFO order, even though the obstack allocates memory in LIFO
    order.  */
 static int
-composite_peek (m4_input_block *me)
+composite_peek (m4_input_block *me, m4 *context)
 {
   m4__symbol_chain *chain = me->u.u_c.chain;
   while (chain)
@@ -727,7 +730,22 @@ composite_peek (m4_input_block *me)
            return to_uchar (chain->u.u_s.str[0]);
          break;
        case M4__CHAIN_ARGV:
-         /* TODO - peek into argv.  */
+         /* TODO - figure out how to pass multiple arguments to
+            macro.c at once.  */
+         if (chain->u.u_a.index == m4_arg_argc (chain->u.u_a.argv))
+           break;
+         if (chain->u.u_a.comma)
+           return ','; /* FIXME - support M4_SYNTAX_COMMA.  */
+         /* Rather than directly parse argv here, we push another
+            input block containing the next unparsed argument from
+            argv.  */
+         m4_push_string_init (context);
+         m4__push_arg_quote (context, current_input, chain->u.u_a.argv,
+                             chain->u.u_a.index, chain->u.u_a.quotes);
+         chain->u.u_a.index++;
+         chain->u.u_a.comma = true;
+         m4_push_string_finish ();
+         return peek_char (context);
        default:
          assert (!"composite_peek");
          abort ();
@@ -743,7 +761,9 @@ composite_read (m4_input_block *me, m4 *context, bool 
allow_quote, bool safe)
   m4__symbol_chain *chain = me->u.u_c.chain;
   while (chain)
     {
-      if (allow_quote && chain->quote_age == m4__quote_age (M4SYNTAX))
+      /* TODO also support returning $@ as CHAR_QUOTE.  */
+      if (allow_quote && chain->quote_age == m4__quote_age (M4SYNTAX)
+         && chain->type == M4__CHAIN_STR)
        return CHAR_QUOTE;
       switch (chain->type)
        {
@@ -759,7 +779,28 @@ composite_read (m4_input_block *me, m4 *context, bool 
allow_quote, bool safe)
            m4__adjust_refcount (context, chain->u.u_s.level, false);
          break;
        case M4__CHAIN_ARGV:
-         /* TODO - peek into argv.  */
+         /* TODO - figure out how to pass multiple arguments to
+            macro.c at once.  */
+         if (chain->u.u_a.index == m4_arg_argc (chain->u.u_a.argv))
+           {
+             m4__arg_adjust_refcount (context, chain->u.u_a.argv, false);
+             break;
+           }
+         if (chain->u.u_a.comma)
+           {
+             chain->u.u_a.comma = false;
+             return ','; /* FIXME - support M4_SYNTAX_COMMA.  */
+           }
+         /* Rather than directly parse argv here, we push another
+            input block containing the next unparsed argument from
+            argv.  */
+         m4_push_string_init (context);
+         m4__push_arg_quote (context, current_input, chain->u.u_a.argv,
+                             chain->u.u_a.index, chain->u.u_a.quotes);
+         chain->u.u_a.index++;
+         chain->u.u_a.comma = true;
+         m4_push_string_finish ();
+         return next_char (context, allow_quote, !safe);
        default:
          assert (!"composite_read");
          abort ();
@@ -781,7 +822,10 @@ composite_unget (m4_input_block *me, int ch)
       chain->u.u_s.len++;
       break;
     case M4__CHAIN_ARGV:
-      /* TODO support argv ref.  */
+      /* FIXME - support M4_SYNTAX_COMMA.  */
+      assert (ch == ',' && !chain->u.u_a.comma);
+      chain->u.u_a.comma = true;
+      break;
     default:
       assert (!"composite_unget");
       abort ();
@@ -798,12 +842,22 @@ composite_clean (m4_input_block *me, m4 *context, bool 
cleanup)
       switch (chain->type)
        {
        case M4__CHAIN_STR:
-         assert (!chain->u.u_s.len);
+         if (chain->u.u_s.len)
+           {
+             assert (!cleanup);
+             return false;
+           }
          if (chain->u.u_s.level < SIZE_MAX)
            m4__adjust_refcount (context, chain->u.u_s.level, false);
          break;
        case M4__CHAIN_ARGV:
-         /* TODO - peek into argv.  */
+         if (chain->u.u_a.index < m4_arg_argc (chain->u.u_a.argv))
+           {
+             assert (!cleanup);
+             return false;
+           }
+         m4__arg_adjust_refcount (context, chain->u.u_a.argv, false);
+         break;
        default:
          assert (!"composite_clean");
          abort ();
@@ -820,6 +874,7 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
   size_t maxlen = m4_get_max_debug_arg_length_opt (context);
   m4__symbol_chain *chain = me->u.u_c.chain;
   const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX);
+  bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE);
   bool done = false;
 
   if (quote)
@@ -834,7 +889,11 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
            done = true;
          break;
        case M4__CHAIN_ARGV:
-         /* TODO support argv refs as well.  */
+         assert (!chain->u.u_a.comma);
+         if (m4_arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
+                           chain->u.u_a.quotes, &maxlen, module))
+           done = true;
+         break;
        default:
          assert (!"composite_print");
          abort ();
@@ -935,9 +994,9 @@ pop_input (m4 *context, bool cleanup)
   m4_input_block *tmp = isp->prev;
 
   assert (isp);
-  if (isp->funcs->peek_func (isp) != CHAR_RETRY
-      || (isp->funcs->clean_func
-         && !isp->funcs->clean_func (isp, context, cleanup)))
+  if (isp->funcs->clean_func
+      ? !isp->funcs->clean_func (isp, context, cleanup)
+      : (isp->funcs->peek_func (isp, context) != CHAR_RETRY))
     return false;
 
   if (tmp != NULL)
@@ -976,8 +1035,7 @@ m4_pop_wrapup (m4 *context)
     }
 
   m4_debug_message (context, M4_DEBUG_TRACE_INPUT,
-                   _("input from m4wrap recursion level %lu"),
-                   (unsigned long int) ++level);
+                   _("input from m4wrap recursion level %zu"), ++level);
 
   current_input = wrapup_stack;
   wrapup_stack = (m4_obstack *) xmalloc (sizeof *wrapup_stack);
@@ -1101,7 +1159,7 @@ peek_char (m4 *context)
        return CHAR_EOF;
 
       assert (block->funcs->peek_func);
-      if ((ch = block->funcs->peek_func (block)) != CHAR_RETRY)
+      if ((ch = block->funcs->peek_func (block, context)) != CHAR_RETRY)
        {
 /*       if (IS_IGNORE (ch)) */
 /*         return next_char (context, false, true); */
diff --git a/m4/m4module.h b/m4/m4module.h
index 24d6a45..bdb31a3 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -42,8 +42,7 @@ typedef struct m4_string_pair m4_string_pair;
 
 typedef struct obstack         m4_obstack;
 
-typedef void   m4_builtin_func  (m4 *, m4_obstack *, unsigned int,
-                                m4_macro_args *);
+typedef void   m4_builtin_func  (m4 *, m4_obstack *, size_t, m4_macro_args *);
 
 /* The value of m4_builtin flags is built from these:  */
 enum {
@@ -67,9 +66,9 @@ struct m4_builtin
   m4_builtin_func * func;      /* implementation of the builtin */
   const char *     name;       /* name found by builtin, printed by dumpdef */
   int              flags;      /* bitwise OR of M4_BUILTIN_* bits */
-  unsigned int     min_args;   /* 0-based minimum number of arguments */
+  size_t           min_args;   /* 0-based minimum number of arguments */
   /* max arguments, UINT_MAX if unlimited; must be >= min_args */
-  unsigned int     max_args;
+  size_t           max_args;
 };
 
 struct m4_macro
@@ -89,11 +88,11 @@ struct m4_string_pair
 
 #define M4BUILTIN(name)                                                        
\
   static void CONC (builtin_, name)                                    \
-   (m4 *context, m4_obstack *obs, unsigned int argc, m4_macro_args *argv);
+   (m4 *context, m4_obstack *obs, size_t argc, m4_macro_args *argv);
 
 #define M4BUILTIN_HANDLER(name)                                                
\
   static void CONC (builtin_, name)                                    \
-   (m4 *context, m4_obstack *obs, unsigned int argc, m4_macro_args *argv)
+   (m4 *context, m4_obstack *obs, size_t argc, m4_macro_args *argv)
 
 #define M4INIT_HANDLER(name)                                           \
   void CONC (name, CONC (_LTX_, m4_init_module))                       \
@@ -121,10 +120,10 @@ struct m4_string_pair
    scope.  */
 #define M4ARGLEN(i) m4_arg_len (argv, i)
 
-extern bool    m4_bad_argc        (m4 *, int, const char *,
-                                   unsigned int, unsigned int, bool);
+extern bool    m4_bad_argc        (m4 *, int, const char *, size_t, size_t,
+                                   bool);
 extern bool    m4_numeric_arg     (m4 *, const char *, const char *, int *);
-extern void    m4_dump_args       (m4 *, m4_obstack *, unsigned int,
+extern void    m4_dump_args       (m4 *, m4_obstack *, size_t,
                                    m4_macro_args *, const char *, bool);
 extern bool    m4_parse_truth_arg (m4 *, const char *, const char *, bool);
 
@@ -246,8 +245,9 @@ 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 void    m4_symbol_value_print   (m4_symbol_value *, m4_obstack *,
-                                        const m4_string_pair *, size_t, bool);
+extern bool    m4_symbol_value_print   (m4_symbol_value *, m4_obstack *,
+                                        const m4_string_pair *, size_t *,
+                                        bool);
 extern void    m4_symbol_print         (m4_symbol *, m4_obstack *,
                                         const m4_string_pair *, bool, size_t,
                                         bool);
@@ -312,24 +312,25 @@ extern const m4_builtin   *m4_builtin_find_by_func 
(m4_module *,
 /* --- MACRO MANAGEMENT --- */
 
 extern void    m4_macro_expand_input   (m4 *);
-extern void    m4_macro_call           (m4 *, m4_symbol_value *,
-                                        m4_obstack *, unsigned int,
-                                        m4_macro_args *);
-extern unsigned int m4_arg_argc                (m4_macro_args *);
-extern m4_symbol_value *m4_arg_symbol  (m4_macro_args *, unsigned int);
-extern bool    m4_is_arg_text          (m4_macro_args *, unsigned int);
-extern bool    m4_is_arg_func          (m4_macro_args *, unsigned int);
-extern const char *m4_arg_text         (m4 *, m4_macro_args *, unsigned int);
-extern bool    m4_arg_equal            (m4_macro_args *, unsigned int,
-                                        unsigned int);
-extern bool    m4_arg_empty            (m4_macro_args *, unsigned int);
-extern size_t  m4_arg_len              (m4_macro_args *, unsigned int);
-extern m4_builtin_func *m4_arg_func    (m4_macro_args *, unsigned int);
+extern void    m4_macro_call           (m4 *, m4_symbol_value *, m4_obstack *,
+                                        size_t, m4_macro_args *);
+extern size_t  m4_arg_argc             (m4_macro_args *);
+extern m4_symbol_value *m4_arg_symbol  (m4_macro_args *, size_t);
+extern bool    m4_is_arg_text          (m4_macro_args *, size_t);
+extern bool    m4_is_arg_func          (m4_macro_args *, size_t);
+extern const char *m4_arg_text         (m4 *, m4_macro_args *, size_t);
+extern bool    m4_arg_equal            (m4_macro_args *, size_t, size_t);
+extern bool    m4_arg_empty            (m4_macro_args *, size_t);
+extern size_t  m4_arg_len              (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_obstack *, m4_macro_args *,
+                                        size_t, const m4_string_pair *,
+                                        size_t *, 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 *,
-                                        unsigned int);
+                                        size_t);
 extern void    m4_push_args            (m4 *, m4_obstack *, m4_macro_args *,
                                         bool, bool);
 
diff --git a/m4/m4private.h b/m4/m4private.h
index 5304682..28ac867 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -217,9 +217,11 @@ struct m4__symbol_chain
     } u_s;                     /* M4__CHAIN_STR.  */
     struct
     {
-      m4_macro_args *argv;     /* Reference to earlier address@hidden  */
-      unsigned int index;      /* Argument index within argv.  */
-      bool flatten;            /* True to treat builtins as text.  */
+      m4_macro_args *argv;             /* Reference to earlier address@hidden  
*/
+      size_t index;                    /* Argument index within argv.  */
+      bool_bitfield flatten : 1;       /* True to treat builtins as text.  */
+      bool_bitfield comma : 1;         /* True when `,' is next input.  */
+      const m4_string_pair *quotes;    /* NULL for $*, quotes for 
address@hidden  */
     } u_a;                     /* M4__CHAIN_ARGV.  */
   } u;
 };
@@ -233,8 +235,8 @@ struct m4_symbol_value
   unsigned int         flags;
 
   m4_hash *            arg_signature;
-  unsigned int         min_args;
-  unsigned int         max_args;
+  size_t               min_args;
+  size_t               max_args;
   size_t               pending_expansions;
 
   m4__symbol_type      type;
@@ -264,7 +266,7 @@ struct m4_macro_args
   /* One more than the highest actual argument.  May be larger than
      arraylen since the array can refer to multiple arguments via a
      single $@ reference.  */
-  unsigned int argc;
+  size_t argc;
   /* False unless the macro expansion refers to $@; determines whether
      this object can be freed at end of macro expansion or must wait
      until all references have been rescanned.  */
@@ -281,6 +283,7 @@ struct m4_macro_args
      during parsing or any token is potentially unsafe and requires a
      rescan.  */
   unsigned int quote_age;
+  size_t level; /* Which obstack owns this argv.  */
   size_t arraylen; /* True length of allocated elements in array.  */
   /* Used as a variable-length array, storing information about each
      argument.  */
@@ -299,7 +302,10 @@ struct m4__macro_arg_stacks
   void *argv_base;     /* Location for clearing the argv obstack.  */
 };
 
-extern size_t m4__adjust_refcount (m4 *, size_t, bool);
+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 *);
 
 #define VALUE_NEXT(T)          ((T)->next)
 #define VALUE_MODULE(T)                ((T)->module)
diff --git a/m4/macro.c b/m4/macro.c
index f91923c..708be58 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -522,31 +522,13 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
   if (BIT_TEST (VALUE_FLAGS (value), VALUE_DELETED_BIT))
     m4_symbol_value_delete (value);
 
-  /* If argv contains references, those refcounts must be reduced now.  */
-  if (argv->has_ref)
-    {
-      m4__symbol_chain *chain;
-      size_t i;
-      for (i = 0; i < argv->arraylen; i++)
-       if (argv->array[i]->type == M4_SYMBOL_COMP)
-         {
-           chain = argv->array[i]->u.u_c.chain;
-           while (chain)
-             {
-               assert (chain->type == M4__CHAIN_STR);
-               if (chain->u.u_s.level < SIZE_MAX)
-                 m4__adjust_refcount (context, chain->u.u_s.level, false);
-               chain = chain->next;
-             }
-         }
-    }
-
   /* We no longer need argv, so reduce the refcount.  Additionally, if
      no other references to argv were created, we can free our portion
      of the obstack, although we must leave earlier content alone.  A
      refcount of 0 implies that adjust_refcount already freed the
      entire stack.  */
-  if (m4__adjust_refcount (context, level, false))
+  m4__arg_adjust_refcount (context, argv, false);
+  if (stack->refcount)
     {
       if (argv->inuse)
        {
@@ -593,6 +575,7 @@ collect_arguments (m4 *context, const char *name, size_t 
len,
   args.argv0 = (char *) obstack_copy0 (arguments, name, len);
   args.argv0_len = len;
   args.quote_age = m4__quote_age (M4SYNTAX);
+  args.level = context->expansion_level - 1;
   args.arraylen = 0;
   obstack_grow (argv_stack, &args, offsetof (m4_macro_args, array));
   name = args.argv0;
@@ -647,7 +630,7 @@ collect_arguments (m4 *context, const char *name, size_t 
len,
    the obstack EXPANSION.  Macro tracing is also handled here.  */
 void
 m4_macro_call (m4 *context, m4_symbol_value *value, m4_obstack *expansion,
-              unsigned int argc, m4_macro_args *argv)
+              size_t argc, m4_macro_args *argv)
 {
   if (m4_bad_argc (context, argc, argv->argv0,
                   VALUE_MIN_ARGS (value), VALUE_MAX_ARGS (value),
@@ -896,7 +879,7 @@ 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 (value, &context->trace_messages, quotes, arg_length,
+  m4_symbol_value_print (value, &context->trace_messages, quotes, &arg_length,
                         module);
   trace_flush (context);
 }
@@ -906,8 +889,8 @@ trace_prepre (m4 *context, const char *name, size_t id, 
m4_symbol_value *value)
 static void
 trace_pre (m4 *context, size_t id, m4_macro_args *argv)
 {
-  unsigned int i;
-  unsigned int argc = m4_arg_argc (argv);
+  size_t i;
+  size_t argc = m4_arg_argc (argv);
 
   trace_header (context, id);
   trace_format (context, "%s", M4ARG (0));
@@ -923,11 +906,12 @@ trace_pre (m4 *context, size_t id, m4_macro_args *argv)
       trace_format (context, "(");
       for (i = 1; i < argc; i++)
        {
+         size_t len = arg_length;
          if (i != 1)
            trace_format (context, ", ");
 
          m4_symbol_value_print (m4_arg_symbol (argv, i),
-                                &context->trace_messages, quotes, arg_length,
+                                &context->trace_messages, quotes, &len,
                                 module);
        }
       trace_format (context, ")");
@@ -980,6 +964,33 @@ m4__adjust_refcount (m4 *context, size_t level, bool 
increase)
   return stack->refcount;
 }
 
+/* Given ARGV, adjust the refcount of every reference it contains in
+   the direction decided by INCREASE.  Return true if increasing
+   references to ARGV implies the first use of ARGV.  */
+bool
+m4__arg_adjust_refcount (m4 *context, m4_macro_args *argv, bool increase)
+{
+  size_t i;
+  m4__symbol_chain *chain;
+  bool result = !argv->inuse;
+
+  if (argv->has_ref)
+    for (i = 0; i < argv->arraylen; i++)
+      if (argv->array[i]->type == M4_SYMBOL_COMP)
+       {
+         chain = argv->array[i]->u.u_c.chain;
+         while (chain)
+           {
+             assert (chain->type == M4__CHAIN_STR);
+             if (chain->u.u_s.level < SIZE_MAX)
+               m4__adjust_refcount (context, chain->u.u_s.level, increase);
+             chain = chain->next;
+           }
+       }
+  m4__adjust_refcount (context, argv->level, increase);
+  return result;
+}
+
 /* Mark ARGV as being in use, along with any $@ references that it
    wraps.  */
 static void
@@ -997,20 +1008,74 @@ arg_mark (m4_macro_args *argv)
     }
 }
 
+/* Populate the newly-allocated VALUE as a wrapper around ARGV,
+   starting with argument INDEX.  Allocate any data on OBS, owned by a
+   given expansion LEVEL.  FLATTEN determines whether to allow
+   builtins, and QUOTES determines whether all arguments are quoted.
+   Return TOKEN when successful, NULL when wrapping ARGV is trivially
+   empty.  */
+static m4_symbol_value *
+make_argv_ref (m4_symbol_value *value, m4_obstack *obs, size_t level,
+              m4_macro_args *argv, size_t index, bool flatten,
+              const m4_string_pair *quotes)
+{
+  m4__symbol_chain *chain;
+
+  assert (obstack_object_size (obs) == 0);
+  if (argv->wrapper)
+    {
+      /* TODO support concatenation with $@ refs.  */
+      assert (argv->arraylen == 1 && argv->array[0]->type == M4_SYMBOL_COMP);
+      chain= argv->array[0]->u.u_c.chain;
+      assert (!chain->next && chain->type == M4__CHAIN_ARGV);
+      argv = chain->u.u_a.argv;
+      index += chain->u.u_a.index - 1;
+    }
+  if (argv->argc <= index)
+    return NULL;
+
+  chain = (m4__symbol_chain *) obstack_alloc (obs, sizeof *chain);
+  value->type = M4_SYMBOL_COMP;
+  value->u.u_c.chain = value->u.u_c.end = chain;
+  chain->next = NULL;
+  chain->type = M4__CHAIN_ARGV;
+  chain->quote_age = argv->quote_age;
+  chain->u.u_a.argv = argv;
+  chain->u.u_a.index = index;
+  chain->u.u_a.flatten = flatten;
+  chain->u.u_a.comma = false;
+  if (quotes)
+    {
+      /* Clone the quotes into the obstack, since changequote can
+        occur before this $@ is rescanned.  */
+      /* TODO - optimize when quote_age is nonzero?  */
+      m4_string_pair *tmp = (m4_string_pair *) obstack_copy (obs, quotes,
+                                                            sizeof *quotes);
+      tmp->str1 = (char *) obstack_copy0 (obs, quotes->str1, quotes->len1);
+      tmp->str2 = (char *) obstack_copy0 (obs, quotes->str2, quotes->len2);
+      chain->u.u_a.quotes = tmp;
+    }
+  else
+    chain->u.u_a.quotes = NULL;
+  return value;
+}
+
 /* Given ARGV, return the symbol value at the specified INDEX, which
-   must be non-zero.  */
-m4_symbol_value *
-m4_arg_symbol (m4_macro_args *argv, unsigned int index)
+   must be non-zero.  *LEVEL is set to the obstack level that contains
+   the symbol (which is not necessarily the level of ARGV).  */
+static m4_symbol_value *
+arg_symbol (m4_macro_args *argv, size_t index, size_t *level)
 {
-  unsigned int i;
+  size_t i;
   m4_symbol_value *value;
 
   assert (index);
+  *level = argv->level;
   if (argv->argc <= index)
     return &empty_symbol;
-
   if (!argv->wrapper)
     return argv->array[index - 1];
+
   /* Must cycle through all array slots until we find index, since
      wrappers can contain multiple arguments.  */
   for (i = 0; i < argv->arraylen; i++)
@@ -1023,8 +1088,8 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index)
          assert (!chain->next && chain->type == M4__CHAIN_ARGV);
          if (index < chain->u.u_a.argv->argc - (chain->u.u_a.index - 1))
            {
-             value = m4_arg_symbol (chain->u.u_a.argv,
-                                    chain->u.u_a.index - 1 + index);
+             value = arg_symbol (chain->u.u_a.argv,
+                                 chain->u.u_a.index - 1 + index, level);
              if (chain->u.u_a.flatten && m4_is_symbol_value_func (value))
                value = &empty_symbol;
              break;
@@ -1037,10 +1102,19 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index)
   return value;
 }
 
+/* Given ARGV, return the symbol value at the specified INDEX, which
+   must be non-zero.  */
+m4_symbol_value *
+m4_arg_symbol (m4_macro_args *argv, size_t index)
+{
+  size_t dummy;
+  return arg_symbol (argv, index, &dummy);
+}
+
 /* Given ARGV, return true if argument INDEX is text.  Index 0 is
    always text, as are indices beyond argc.  */
 bool
-m4_is_arg_text (m4_macro_args *argv, unsigned int index)
+m4_is_arg_text (m4_macro_args *argv, size_t index)
 {
   m4_symbol_value *value;
   if (index == 0 || argv->argc <= index)
@@ -1055,7 +1129,7 @@ m4_is_arg_text (m4_macro_args *argv, unsigned int index)
 /* Given ARGV, return true if argument INDEX is a builtin function.
    Only non-zero indices less than argc can return true.  */
 bool
-m4_is_arg_func (m4_macro_args *argv, unsigned int index)
+m4_is_arg_func (m4_macro_args *argv, size_t index)
 {
   if (index == 0 || argv->argc <= index)
     return false;
@@ -1067,7 +1141,7 @@ m4_is_arg_func (m4_macro_args *argv, unsigned int index)
    argc return the empty string.  The result is always NUL-terminated,
    even if it includes embedded NUL characters.  */
 const char *
-m4_arg_text (m4 *context, m4_macro_args *argv, unsigned int index)
+m4_arg_text (m4 *context, m4_macro_args *argv, size_t index)
 {
   m4_symbol_value *value;
   m4__symbol_chain *chain;
@@ -1101,7 +1175,7 @@ m4_arg_text (m4 *context, m4_macro_args *argv, unsigned 
int index)
    !strcmp (m4_arg_text (context, argv, indexa),
            m4_arg_text (context, argv, indexb)).  */
 bool
-m4_arg_equal (m4_macro_args *argv, unsigned int indexa, unsigned int indexb)
+m4_arg_equal (m4_macro_args *argv, size_t indexa, size_t indexb)
 {
   m4_symbol_value *sa = m4_arg_symbol (argv, indexa);
   m4_symbol_value *sb = m4_arg_symbol (argv, indexb);
@@ -1191,7 +1265,7 @@ m4_arg_equal (m4_macro_args *argv, unsigned int indexa, 
unsigned int indexb)
    This gives the same result as comparing m4_arg_len against 0, but
    is often faster.  */
 bool
-m4_arg_empty (m4_macro_args *argv, unsigned int index)
+m4_arg_empty (m4_macro_args *argv, size_t index)
 {
   return (index ? m4_arg_symbol (argv, index) == &empty_symbol
          : !argv->argv0_len);
@@ -1200,7 +1274,7 @@ m4_arg_empty (m4_macro_args *argv, unsigned int index)
 /* Given ARGV, return the length of argument INDEX.  Abort if the
    argument is not text.  Indices beyond argc return 0.  */
 size_t
-m4_arg_len (m4_macro_args *argv, unsigned int index)
+m4_arg_len (m4_macro_args *argv, size_t index)
 {
   m4_symbol_value *value;
   m4__symbol_chain *chain;
@@ -1230,11 +1304,46 @@ m4_arg_len (m4_macro_args *argv, unsigned int index)
 /* Given ARGV, return the builtin function referenced by argument
    INDEX.  Abort if it is not a single builtin.  */
 m4_builtin_func *
-m4_arg_func (m4_macro_args *argv, unsigned int index)
+m4_arg_func (m4_macro_args *argv, size_t index)
 {
   return m4_get_symbol_value_func (m4_arg_symbol (argv, 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 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
+   MODULE, print any details about originating modules.  QUOTES count
+   against the truncation length, but not module names.  */
+bool
+m4_arg_print (m4_obstack *obs, m4_macro_args *argv, size_t index,
+             const m4_string_pair *quotes, size_t *max_len, bool module)
+{
+  size_t len = max_len ? *max_len : SIZE_MAX;
+  size_t i;
+  bool comma = false;
+
+  for (i = index; i < argv->argc; i++)
+    {
+      if (comma && m4_shipout_string_trunc (obs, ",", 1, NULL, &len))
+       return true;
+      comma = true;
+      if (quotes && m4_shipout_string_trunc (obs, quotes->str1, quotes->len1,
+                                            NULL, &len))
+       return true;
+      if (m4_symbol_value_print (m4_arg_symbol (argv, i), obs, NULL, &len,
+                                module))
+       return true;
+      if (quotes && m4_shipout_string_trunc (obs, quotes->str2, quotes->len2,
+                                            NULL, &len))
+       return true;
+    }
+  if (max_len)
+    *max_len = len;
+  return false;
+}
+
 /* Create a new argument object using the same obstack as ARGV; thus,
    the new object will automatically be freed when the original is
    freed.  Explicitly set the macro name (argv[0]) from ARGV0 with
@@ -1248,23 +1357,16 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, 
const char *argv0,
 {
   m4_macro_args *new_argv;
   m4_symbol_value *value;
-  m4__symbol_chain *chain;
-  unsigned int index = skip ? 2 : 1;
+  m4_symbol_value *new_value;
+  size_t index = skip ? 2 : 1;
   m4_obstack *obs = m4_arg_scratch (context);
 
-  /* When making a reference through a reference, point to the
-     original if possible.  */
-  if (argv->wrapper)
-    {
-      /* TODO for now we support only a single-length $@ chain.  */
-      assert (argv->arraylen == 1 && argv->array[0]->type == M4_SYMBOL_COMP);
-      chain = argv->array[0]->u.u_c.chain;
-      assert (!chain->next && chain->type == M4__CHAIN_ARGV);
-      argv = chain->u.u_a.argv;
-      index += chain->u.u_a.index - 1;
-    }
-  if (argv->argc <= index)
+  new_value = (m4_symbol_value *) obstack_alloc (obs, sizeof *value);
+  value = make_argv_ref (new_value, obs, context->expansion_level - 1, argv,
+                        index, flatten, NULL);
+  if (!value)
     {
+      obstack_free (obs, new_value);
       new_argv = (m4_macro_args *) obstack_alloc (obs, offsetof (m4_macro_args,
                                                                 array));
       new_argv->arraylen = 0;
@@ -1275,53 +1377,57 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, 
const char *argv0,
       new_argv = (m4_macro_args *) obstack_alloc (obs, (offsetof 
(m4_macro_args,
                                                                  array)
                                                        + sizeof value));
-      value = (m4_symbol_value *) obstack_alloc (obs, sizeof *value);
-      chain = (m4__symbol_chain *) obstack_alloc (obs, sizeof *chain);
       new_argv->arraylen = 1;
       new_argv->array[0] = value;
       new_argv->wrapper = true;
-      new_argv->has_ref = true;
-      value->type = M4_SYMBOL_COMP;
-      value->u.u_c.chain = value->u.u_c.end = chain;
-      chain->next = NULL;
-      chain->type = M4__CHAIN_ARGV;
-      chain->quote_age = argv->quote_age;
-      chain->u.u_a.argv = argv;
-      chain->u.u_a.index = index;
-      chain->u.u_a.flatten = flatten;
+      new_argv->has_ref = argv->has_ref;
     }
   new_argv->argc = argv->argc - (index - 1);
   new_argv->inuse = false;
   new_argv->argv0 = argv0;
   new_argv->argv0_len = argv0_len;
   new_argv->quote_age = argv->quote_age;
+  new_argv->level = argv->level;
   return new_argv;
 }
 
 /* Push argument INDEX from ARGV, which must be a text token, onto the
    expansion stack OBS for rescanning.  */
 void
-m4_push_arg (m4 *context, m4_obstack *obs, m4_macro_args *argv,
-            unsigned int index)
+m4_push_arg (m4 *context, m4_obstack *obs, m4_macro_args *argv, size_t index)
 {
-  m4_symbol_value *value;
-  m4_symbol_value temp;
+  m4_symbol_value value;
 
   if (index == 0)
     {
-      value = &temp;
-      m4_set_symbol_value_text (value, argv->argv0, argv->argv0_len, 0);
+      m4_set_symbol_value_text (&value, argv->argv0, argv->argv0_len, 0);
+      if (m4__push_symbol (context, &value, context->expansion_level - 1,
+                          argv->inuse))
+       arg_mark (argv);
     }
   else
-    {
-      value = m4_arg_symbol (argv, index);
-      if (value == &empty_symbol)
-       return;
-    }
+    m4__push_arg_quote (context, obs, argv, index, NULL);
+}
+
+/* Push argument INDEX from ARGV, which must be a text token, onto the
+   expansion stack OBS for rescanning.  INDEX must be non-zero.
+   QUOTES determines any quote delimiters that were in effect when the
+   reference was created.  */
+void
+m4__push_arg_quote (m4 *context, m4_obstack *obs, m4_macro_args *argv,
+                   size_t index, const m4_string_pair *quotes)
+{
+  size_t level;
+  m4_symbol_value *value = arg_symbol (argv, index, &level);
+
   /* TODO handle builtin tokens?  */
-  if (m4__push_symbol (context, value, context->expansion_level - 1,
-                      argv->inuse))
+  if (quotes)
+    obstack_grow (obs, quotes->str1, quotes->len1);
+  if (value != &empty_symbol
+      && m4__push_symbol (context, value, level, argv->inuse))
     arg_mark (argv);
+  if (quotes)
+    obstack_grow (obs, quotes->str2, quotes->len2);
 }
 
 /* Push series of comma-separated arguments from ARGV, which should
@@ -1332,54 +1438,47 @@ void
 m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args *argv, bool skip,
              bool quote)
 {
+  m4_symbol_value tmp;
   m4_symbol_value *value;
-  unsigned int i = skip ? 2 : 1;
-  const char *sep = ",";
-  size_t sep_len = 1;
-  bool use_sep = false;
-  bool inuse = false;
+  m4__symbol_chain *chain;
+  size_t i = skip ? 2 : 1;
   const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX);
-  m4_obstack *scratch = m4_arg_scratch (context);
+  char *str = NULL;
+  size_t len = obstack_object_size (obs);
 
   if (argv->argc <= i)
     return;
 
   if (argv->argc == i + 1)
     {
-      if (quote)
-       obstack_grow (obs, quotes->str1, quotes->len1);
-      m4_push_arg (context, obs, argv, i);
-      if (quote)
-       obstack_grow (obs, quotes->str2, quotes->len2);
+      m4__push_arg_quote (context, obs, argv, i, quote ? quotes : NULL);
       return;
     }
 
-  /* Compute the separator in the scratch space.  */
-  if (quote)
+  /* Since make_argv_ref puts data on obs, we must first close any
+     pending data.  The resulting symbol contents live entirely on
+     obs, so we call push_symbol with a level of -1.  */
+  if (len)
     {
-      obstack_grow (obs, quotes->str1, quotes->len1);
-      obstack_grow (scratch, quotes->str2, quotes->len2);
-      obstack_1grow (scratch, ',');
-      obstack_grow0 (scratch, quotes->str1, quotes->len1);
-      sep = (char *) obstack_finish (scratch);
-      sep_len += quotes->len1 + quotes->len2;
+      obstack_1grow (obs, '\0');
+      str = (char *) obstack_finish (obs);
     }
 
-  /* TODO push entire $@ ref, rather than each arg.  */
-  for ( ; i < argv->argc; i++)
+  /* TODO allow shift, $@, to push builtins without flatten.  */
+  value = make_argv_ref (&tmp, obs, -1, argv, i, true, quote ? quotes : NULL);
+  assert (value == &tmp);
+  if (len)
     {
-      value = m4_arg_symbol (argv, i);
-      if (use_sep)
-       obstack_grow (obs, sep, sep_len);
-      else
-       use_sep = true;
-      /* TODO handle builtin tokens?  */
-      inuse |= m4__push_symbol (context, value,
-                               context->expansion_level - 1, inuse);
+      chain = (m4__symbol_chain *) obstack_alloc (obs, sizeof *chain);
+      chain->next = value->u.u_c.chain;
+      value->u.u_c.chain = chain;
+      chain->type = M4__CHAIN_STR;
+      chain->quote_age = 0;
+      chain->u.u_s.str = str;
+      chain->u.u_s.len = len;
+      chain->u.u_s.level = SIZE_MAX;
     }
-  if (quote)
-    obstack_grow (obs, quotes->str2, quotes->len2);
-  if (inuse)
+  if (m4__push_symbol (context, value, -1, argv->inuse))
     arg_mark (argv);
 }
 
@@ -1390,7 +1489,7 @@ m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, bool skip,
 /* Given ARGV, return one greater than the number of arguments it
    describes.  */
 #undef m4_arg_argc
-unsigned int
+size_t
 m4_arg_argc (m4_macro_args *argv)
 {
   return argv->argc;
diff --git a/m4/output.c b/m4/output.c
index 21a28f7..d6c6cc5 100644
--- a/m4/output.c
+++ b/m4/output.c
@@ -604,7 +604,7 @@ m4_shipout_string (m4 *context, m4_obstack *obs, const char 
*s, size_t len,
    quote characters around S.  If LEN is SIZE_MAX, use the string
    length of S instead.  If MAX_LEN, reduce *MAX_LEN by LEN.  If LEN
    is larger than *MAX_LEN, then truncate output and return true;
-   otherwise return false.  */
+   otherwise return false.  Quotes do not count against MAX_LEN.  */
 bool
 m4_shipout_string_trunc (m4_obstack *obs, const char *s, size_t len,
                         const m4_string_pair *quotes, size_t *max_len)
diff --git a/m4/symtab.c b/m4/symtab.c
index 60afe63..0d2055e 100644
--- a/m4/symtab.c
+++ b/m4/symtab.c
@@ -533,75 +533,77 @@ 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 MAXLEN is less than SIZE_MAX,
-   then truncate text definitions to that length.  If MODULE, then
-   include which module defined a builtin.  */
-void
+   it to surround a text definition.  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_symbol_value *value, m4_obstack *obs,
-                      const m4_string_pair *quotes, size_t maxlen,
+                      const m4_string_pair *quotes, size_t *maxlen,
                       bool module)
 {
   const char *text;
-  size_t len;
-  bool truncated = false;
+  const m4_builtin *bp;
+  m4__symbol_chain *chain;
+  size_t len = maxlen ? *maxlen : SIZE_MAX;
+  bool result = false;
 
   switch (value->type)
     {
     case M4_SYMBOL_TEXT:
-      text = m4_get_symbol_value_text (value);
-      len = m4_get_symbol_value_len (value);
-      if (maxlen < len)
-       {
-         len = maxlen;
-         truncated = true;
-       }
+      if (m4_shipout_string_trunc (obs, m4_get_symbol_value_text (value),
+                                  m4_get_symbol_value_len (value), quotes,
+                                  &len))
+       result = true;
       break;
     case M4_SYMBOL_FUNC:
-      {
-       const m4_builtin *bp = m4_get_symbol_value_builtin (value);
-       static const m4_string_pair q1 = { "<", 1, ">", 1 };
-       text = bp->name;
-       len = strlen (text);
-       quotes = &q1;
-      }
+      bp = m4_get_symbol_value_builtin (value);
+      obstack_1grow (obs, '<');
+      obstack_grow (obs, bp->name, strlen (bp->name));
+      obstack_1grow (obs, '>');
       break;
     case M4_SYMBOL_PLACEHOLDER:
       text = m4_get_symbol_value_placeholder (value);
-      static const m4_string_pair q2 = { "<<", 2, ">>", 2 };
-      len = strlen (text);
-      quotes = &q2;
+      obstack_1grow (obs, '<');
+      obstack_1grow (obs, '<');
+      obstack_grow (obs, text, strlen (text));
+      obstack_1grow (obs, '>');
+      obstack_1grow (obs, '>');
       break;
     case M4_SYMBOL_COMP:
-      {
-       m4__symbol_chain *chain = value->u.u_c.chain;
-       if (quotes)
-         obstack_grow (obs, quotes->str1, quotes->len1);
-       while (chain)
-         {
-           /* TODO for now, assume all links are text.  */
-           assert (chain->type == M4__CHAIN_STR);
-           if (m4_shipout_string_trunc (obs, chain->u.u_s.str,
-                                        chain->u.u_s.len, NULL, &maxlen))
+      chain = value->u.u_c.chain;
+      if (quotes)
+       obstack_grow (obs, quotes->str1, quotes->len1);
+      while (chain && !result)
+       {
+         switch (chain->type)
+           {
+           case M4__CHAIN_STR:
+             if (m4_shipout_string_trunc (obs, chain->u.u_s.str,
+                                          chain->u.u_s.len, NULL, &len))
+               result = true;
+             break;
+           case M4__CHAIN_ARGV:
+             if (m4_arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
+                               chain->u.u_a.quotes, &len, module))
+               result = true;
              break;
+           default:
+             assert (!"m4_symbol_value_print");
+             abort ();
+           }
            chain = chain->next;
          }
-       if (quotes)
-         obstack_grow (obs, quotes->str2, quotes->len2);
-       assert (!module);
-       return;
-      }
+      if (quotes)
+       obstack_grow (obs, quotes->str2, quotes->len2);
+      assert (!module);
+      break;
     default:
-      assert (!"invalid token in symbol_value_print");
+      assert (!"m4_symbol_value_print");
       abort ();
     }
 
-  if (quotes)
-    obstack_grow (obs, quotes->str1, quotes->len1);
-  obstack_grow (obs, text, len);
-  if (truncated)
-    obstack_grow (obs, "...", 3);
-  if (quotes)
-    obstack_grow (obs, quotes->str2, quotes->len2);
   if (module && VALUE_MODULE (value))
     {
       obstack_1grow (obs, '{');
@@ -609,25 +611,30 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack 
*obs,
       obstack_grow (obs, text, strlen (text));
       obstack_1grow (obs, '}');
     }
+  if (maxlen)
+    *maxlen = len;
+  return result;
 }
 
 /* Grow OBS with a text representation of SYMBOL.  If QUOTES, then use
    it to surround each text definition.  If STACK, then append all
    pushdef'd values, rather than just the top.  If ARG_LENGTH is less
    than SIZE_MAX, then truncate text definitions to that length.  If
-   MODULE, then include which module defined a builtin.  */
+   MODULE, then include which module defined a builtin.  QUOTES and
+   MODULE do not count toward truncation.  */
 void
 m4_symbol_print (m4_symbol *symbol, m4_obstack *obs,
                 const m4_string_pair *quotes, bool stack, size_t arg_length,
                 bool module)
 {
   m4_symbol_value *value;
+  size_t len = arg_length;
 
   assert (symbol);
   assert (obs);
 
   value = m4_get_symbol_value (symbol);
-  m4_symbol_value_print (value, obs, quotes, arg_length, module);
+  m4_symbol_value_print (value, obs, quotes, &len, module);
   if (stack)
     {
       value = VALUE_NEXT (value);
@@ -635,7 +642,8 @@ m4_symbol_print (m4_symbol *symbol, m4_obstack *obs,
        {
          obstack_1grow (obs, ',');
          obstack_1grow (obs, ' ');
-         m4_symbol_value_print (value, obs, quotes, arg_length, module);
+         len = arg_length;
+         m4_symbol_value_print (value, obs, quotes, &len, module);
          value = VALUE_NEXT (value);
        }
     }
diff --git a/m4/syntax.c b/m4/syntax.c
index c39a9be..aff6444 100644
--- a/m4/syntax.c
+++ b/m4/syntax.c
@@ -709,7 +709,13 @@ set_quote_age (m4_syntax_table *syntax, bool reset, bool 
change)
    bits of quote_age; otherwise we increment syntax_age for each
    changesyntax, but saturate it at 0xffff rather than wrapping
    around.  Perhaps a cache of other frequently used states is
-   warranted, if changesyntax becomes more popular
+   warranted, if changesyntax becomes more popular.
+
+   Perhaps someday we will fix $@ expansion to use the current
+   settings of the comma category, or even allow multi-character
+   argument separators via changesyntax.  Until then, we use a literal
+   `,' in $@ expansion, therefore we must insist that `,' be an
+   argument separator for quote_age to be non-zero.
 
    Rather than check every token for an unquoted delimiter, we merely
    encode current_quote_age to 0 when things are unsafe, and non-zero
@@ -739,7 +745,8 @@ set_quote_age (m4_syntax_table *syntax, bool reset, bool 
change)
       && *syntax->quote.str1 != *syntax->quote.str2
       && *syntax->comm.str1 != *syntax->quote.str2
       && !m4_has_syntax (syntax, *syntax->comm.str1,
-                        M4_SYNTAX_OPEN | M4_SYNTAX_COMMA | M4_SYNTAX_CLOSE))
+                        M4_SYNTAX_OPEN | M4_SYNTAX_COMMA | M4_SYNTAX_CLOSE)
+      && m4_has_syntax (syntax, ',', M4_SYNTAX_COMMA))
     {
       syntax->quote_age = ((local_syntax_age << 16)
                           | ((*syntax->quote.str1 & 0xff) << 8)
diff --git a/m4/utility.c b/m4/utility.c
index d95e44e..69349fe 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -40,8 +40,8 @@ static const char *skip_space (m4 *, const char *);
    Return true if the macro is guaranteed to expand to the empty
    string, false otherwise.  */
 bool
-m4_bad_argc (m4 *context, int argc, const char *caller, unsigned int min,
-            unsigned int max, bool side_effect)
+m4_bad_argc (m4 *context, int argc, const char *caller, size_t min, size_t max,
+            bool side_effect)
 {
   if (argc - 1 < min)
     {
@@ -99,13 +99,13 @@ m4_numeric_arg (m4 *context, const char *caller, const char 
*arg, int *valuep)
    index START, separated by SEP, and quoted by the current quotes, if
    QUOTED is true.  */
 void
-m4_dump_args (m4 *context, m4_obstack *obs, unsigned int start,
-             m4_macro_args *argv, const char *sep, bool quoted)
+m4_dump_args (m4 *context, m4_obstack *obs, size_t start, m4_macro_args *argv,
+             const char *sep, bool quoted)
 {
-  unsigned int i;
+  size_t i;
   size_t len = strlen (sep);
   bool need_sep = false;
-  unsigned int argc = m4_arg_argc (argv);
+  size_t argc = m4_arg_argc (argv);
 
   for (i = start; i < argc; i++)
     {
diff --git a/modules/evalparse.c b/modules/evalparse.c
index 39b0d41..890816d 100644
--- a/modules/evalparse.c
+++ b/modules/evalparse.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007
-   Free Software Foundation, Inc.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007,
+   2008 Free Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -885,8 +885,7 @@ simple_term (m4 *context, eval_token et, number *v1)
 
 /* Main entry point, called from "eval" and "mpeval" builtins.  */
 void
-m4_evaluate (m4 *context, m4_obstack *obs, unsigned int argc,
-             m4_macro_args *argv)
+m4_evaluate (m4 *context, m4_obstack *obs, size_t argc, m4_macro_args *argv)
 {
   const char * me      = M4ARG (0);
   const char * str     = M4ARG (1);
diff --git a/modules/gnu.c b/modules/gnu.c
index 841e660..1f6bf29 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -499,7 +499,7 @@ M4BUILTIN_HANDLER (changesyntax)
 
   if (m4_expand_ranges)
     {
-      unsigned int i;
+      size_t i;
       for (i = 1; i < argc; i++)
        {
          const char *spec = M4ARG (i);
diff --git a/modules/m4.c b/modules/m4.c
index cd4c230..ccc847c 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -50,8 +50,7 @@
 extern void m4_set_sysval    (int value);
 extern void m4_sysval_flush  (m4 *context, bool report);
 extern void m4_dump_symbols  (m4 *context, m4_dump_symbol_data *data,
-                             unsigned int argc, m4_macro_args *argv,
-                             bool complain);
+                             size_t argc, m4_macro_args *argv, bool complain);
 extern const char *m4_expand_ranges (const char *s, m4_obstack *obs);
 extern void m4_make_temp     (m4 *context, m4_obstack *obs, const char *macro,
                              const char *name, size_t len, bool dir);
@@ -175,7 +174,7 @@ M4BUILTIN_HANDLER (define)
 M4BUILTIN_HANDLER (undefine)
 {
   const char *me = M4ARG (0);
-  unsigned int i;
+  size_t i;
   for (i = 1; i < argc; i++)
     {
       const char *name = M4ARG (i);
@@ -203,7 +202,7 @@ M4BUILTIN_HANDLER (pushdef)
 M4BUILTIN_HANDLER (popdef)
 {
   const char *me = M4ARG (0);
-  unsigned int i;
+  size_t i;
   for (i = 1; i < argc; i++)
     {
       const char *name = M4ARG (i);
@@ -230,7 +229,7 @@ M4BUILTIN_HANDLER (ifdef)
 M4BUILTIN_HANDLER (ifelse)
 {
   const char *me = M4ARG (0);
-  unsigned int index;
+  size_t index;
 
   /* The valid ranges of argc for ifelse is discontinuous, we cannot
      rely on the regular mechanisms.  */
@@ -305,7 +304,7 @@ dump_symbol_CB (m4_symbol_table *ignored, const char *name, 
m4_symbol *symbol,
 /* If there are no arguments, build a sorted list of all defined
    symbols, otherwise, only the specified symbols.  */
 void
-m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, unsigned int argc,
+m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, size_t argc,
                 m4_macro_args *argv, bool complain)
 {
   const char *me = M4ARG (0);
@@ -316,7 +315,7 @@ m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, 
unsigned int argc,
     m4_symtab_apply (M4SYMTAB, false, dump_symbol_CB, data);
   else
     {
-      unsigned int i;
+      size_t i;
       m4_symbol *symbol;
 
       for (i = 1; i < argc; i++)
@@ -374,7 +373,7 @@ M4BUILTIN_HANDLER (dumpdef)
 M4BUILTIN_HANDLER (defn)
 {
   const char *me = M4ARG (0);
-  unsigned int i;
+  size_t i;
 
   for (i = 1; i < argc; i++)
     {
@@ -577,7 +576,7 @@ M4BUILTIN_HANDLER (divnum)
 
 M4BUILTIN_HANDLER (undivert)
 {
-  unsigned int i = 0;
+  size_t i = 0;
   const char *me = M4ARG (0);
 
   if (argc == 1)
@@ -861,7 +860,7 @@ M4BUILTIN_HANDLER (m4wrap)
 
 M4BUILTIN_HANDLER (traceon)
 {
-  unsigned int i;
+  size_t i;
 
   if (argc == 1)
     m4_set_debug_level_opt (context, (m4_get_debug_level_opt (context)
@@ -874,7 +873,7 @@ M4BUILTIN_HANDLER (traceon)
 /* Disable tracing of all specified macros, or all, if none is specified.  */
 M4BUILTIN_HANDLER (traceoff)
 {
-  unsigned int i;
+  size_t i;
 
   if (argc == 1)
     m4_set_debug_level_opt (context, (m4_get_debug_level_opt (context)
diff --git a/modules/m4.h b/modules/m4.h
index 81dfef4..e1c3f96 100644
--- a/modules/m4.h
+++ b/modules/m4.h
@@ -1,5 +1,5 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 2003, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2006, 2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -39,12 +39,12 @@ typedef struct
 typedef void m4_sysval_flush_func (m4 *context, bool report);
 typedef void m4_set_sysval_func (int value);
 typedef void m4_dump_symbols_func (m4 *context, m4_dump_symbol_data *data,
-                                  unsigned int argc, m4_macro_args *argv,
+                                  size_t argc, m4_macro_args *argv,
                                   bool complain);
 typedef const char *m4_expand_ranges_func (const char *s, m4_obstack *obs);
 typedef void m4_make_temp_func (m4 *context, m4_obstack *obs,
                                const char *macro, const char *name,
-                                size_t len, bool dir);
+                               size_t len, bool dir);
 
 END_C_DECLS
 
diff --git a/modules/perl.c b/modules/perl.c
index f129af0..46cf2cd 100644
--- a/modules/perl.c
+++ b/modules/perl.c
@@ -1,5 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1999, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2006, 2007, 2008 Free Software
+   Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -114,7 +115,7 @@ M4FINISH_HANDLER (perl)
 M4BUILTIN_HANDLER (perleval)
 {
   SV *val;
-  unsigned int i;
+  size_t i;
 
   for (i = 1; i < argc; i++)
     {
diff --git a/tests/builtins.at b/tests/builtins.at
index b8d5386..68d151c 100644
--- a/tests/builtins.at
+++ b/tests/builtins.at
@@ -433,14 +433,17 @@ AT_CLEANUP
 AT_TEST_M4([ifelse],
 dnl ensure that comparisons work regardless of reference chains in the middle
 [[define(`e', `$@')define(`long', `01234567890123456789')
+dnl in isolation
 ifelse(long, `01234567890123456789', `yes', `no')
 ifelse(`01234567890123456789', long, `yes', `no')
 ifelse(long, `01234567890123456789-', `yes', `no')
 ifelse(`01234567890123456789-', long, `yes', `no')
+dnl through macro expansion
 ifelse(e(long), `01234567890123456789', `yes', `no')
 ifelse(`01234567890123456789', e(long), `yes', `no')
 ifelse(e(long), `01234567890123456789-', `yes', `no')
 ifelse(`01234567890123456789-', e(long), `yes', `no')
+dnl concatenate macro expansion with unquoted characters
 ifelse(-e(long), `-01234567890123456789', `yes', `no')
 ifelse(-`01234567890123456789', -e(long), `yes', `no')
 ifelse(-e(long), `-01234567890123456789-', `yes', `no')
@@ -449,6 +452,15 @@ ifelse(-e(long)-, `-01234567890123456789-', `yes', `no')
 ifelse(-`01234567890123456789-', -e(long)-, `yes', `no')
 ifelse(-e(long)-, `-01234567890123456789', `yes', `no')
 ifelse(`-01234567890123456789', -e(long)-, `yes', `no')
+dnl concatenate macro expansion with quoted characters
+ifelse(`-'e(long), `-01234567890123456789', `yes', `no')
+ifelse(-`01234567890123456789', `-'e(long), `yes', `no')
+ifelse(`-'e(long), `-01234567890123456789-', `yes', `no')
+ifelse(`-01234567890123456789-', `-'e(long), `yes', `no')
+ifelse(`-'e(long)`-', `-01234567890123456789-', `yes', `no')
+ifelse(-`01234567890123456789-', `-'e(long)`-', `yes', `no')
+ifelse(`-'e(long)`-', `-01234567890123456789', `yes', `no')
+ifelse(`-01234567890123456789', `-'e(long)`-', `yes', `no')
 ]], [[
 yes
 yes
@@ -466,6 +478,14 @@ yes
 yes
 no
 no
+yes
+yes
+no
+no
+yes
+yes
+no
+no
 ]])
 
 


hooks/post-receive
--
GNU M4 source repository




reply via email to

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