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


From: Eric Blake
Subject: [SCM] GNU M4 source repository branch, master, updated. cvs-readonly-42-gbf389f4
Date: Tue, 22 Jan 2008 20:38:29 +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=bf389f4092e0b674a521291b2c6f5862fd14072f

The branch, master has been updated
       via  bf389f4092e0b674a521291b2c6f5862fd14072f (commit)
       via  28cd2024b5c84141f2c3cf08602141b33eadd85a (commit)
       via  5307d448bacdf7f588a95f7bc44c520ce80827a6 (commit)
      from  782e3ac755755787d87a5057a6631329661be3ed (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 bf389f4092e0b674a521291b2c6f5862fd14072f
Author: Eric Blake <address@hidden>
Date:   Tue Jan 22 13:35:00 2008 -0700

    Rely on newer automake.
    
    * configure.ac (AM_INIT_AUTOMAKE): Require 1.10.1, and add lzma
    distribution.
    * bootstrap: Update automake requirement.
    (func_version): Fix --version output, broken since 2007-08-06.
    * HACKING: Likewise.  Add lzma requirement.
    * Makefile.am (clean-local-src): Not needed any more with newest
    Automake.
    (clean-local): Adjust.
    * TODO: Remove completed item.
    
    Signed-off-by: Eric Blake <address@hidden>

commit 28cd2024b5c84141f2c3cf08602141b33eadd85a
Author: Eric Blake <address@hidden>
Date:   Tue Jan 22 11:53:46 2008 -0700

    Doc tweak.
    
    * doc/m4.texinfo (Renamesyms): Avoid underfull hbox, and improve
    example.
    
    Signed-off-by: Eric Blake <address@hidden>

commit 5307d448bacdf7f588a95f7bc44c520ce80827a6
Author: Eric Blake <address@hidden>
Date:   Mon Jan 21 12:04:45 2008 -0700

    Stage 11: full circle for single argument references.
    
    Pass quoted strings through to argument collection in a single
    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.
    Speed impact: noticeable improvement, due to less copying.
    * m4/m4module.h (m4_arg_text): Add parameter.
    (M4ARG): Adjust.
    * m4/m4private.h (CHAR_QUOTE): New input engine sentinel.
    (m4__make_text_link): New prototype.
    (struct m4_symbol_chain): Add quote_age member.
    (struct m4_symbol_value): Add end member to chained symbol.
    (struct m4_macro_args): Add wrapper member.
    * m4/symtab.c (m4_symbol_value_print): Print composite tokens.
    (m4_symbol_value_copy, m4_symbol_value_delete): Recognize
    composite tokens.
    * m4/input.c (make_text_link): Rename...
    (m4__make_text_link): ...to this, and export.
    (m4_push_string_finish): Adjust caller.
    (make_text_link, m4__push_symbol): Update new field.
    (file_read, builtin_read, string_read, composite_read, next_char):
    Add parameter.
    (m4_skip_line, match_input, consume_syntax): Adjust callers.
    (append_quote_token): New function.
    (m4__next_token): Pass quoted strings onto argument collection.
    (m4_print_token) [DEBUG_INPUT]: Update.
    * m4/macro.c (expand_argument): Collect composite arguments.
    (collect_arguments): Update new field.
    (expand_macro): Reduce ref-count of back-references after use.
    (arg_mark, m4_arg_symbol, m4_make_argv_ref): Adjust to new member
    names.
    (m4_is_arg_text): Also recognize composite symbols as text.
    (m4_arg_text, m4_arg_len): Merge composite symbols as needed.
    (m4_arg_equal): Compare composite symbols.
    (m4_push_arg, m4_push_args): Handle composite symbols.
    (m4_arg_symbol): Relax assertion.
    (process_macro): Use single-argument references.
    * m4/output.c (m4_shipout_string_trunc): Update comment.
    * tests/macros.at (Rescanning macros): Augment test.
    
    Signed-off-by: Eric Blake <address@hidden>

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

Summary of changes:
 ChangeLog       |   63 +++++++++++++++
 HACKING         |    7 +-
 Makefile.am     |   13 +---
 TODO            |    3 -
 bootstrap       |   15 ++--
 configure.ac    |    4 +-
 doc/m4.texinfo  |   19 ++++-
 m4/input.c      |  236 +++++++++++++++++++++++++++++++++++-------------------
 m4/m4module.h   |    9 +-
 m4/m4private.h  |   17 +++-
 m4/macro.c      |  239 +++++++++++++++++++++++++++++++++++++++++++++++--------
 m4/output.c     |    3 +-
 m4/symtab.c     |  150 ++++++++++++++++++++++++++---------
 tests/macros.at |   20 +++++-
 14 files changed, 611 insertions(+), 187 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index cc00596..b911528 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,66 @@
+2008-01-22  Ralf Wildenhues  <address@hidden>
+       and Eric Blake  <address@hidden>
+
+       Rely on newer automake.
+       * configure.ac (AM_INIT_AUTOMAKE): Require 1.10.1, and add lzma
+       distribution.
+       * bootstrap: Update automake requirement.
+       (func_version): Fix --version output, broken since 2007-08-06.
+       * HACKING: Likewise.  Add lzma requirement.
+       * Makefile.am (clean-local-src): Not needed any more with newest
+       Automake.
+       (clean-local): Adjust.
+       * TODO: Remove completed item.
+
+2008-01-22  Eric Blake  <address@hidden>
+
+       Doc tweak.
+       * doc/m4.texinfo (Renamesyms): Avoid underfull hbox, and improve
+       example.
+
+2008-01-21  Eric Blake  <address@hidden>
+
+       Stage 11: full circle for single argument references.
+       Pass quoted strings through to argument collection in a single
+       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.
+       Speed impact: noticeable improvement, due to less copying.
+       * m4/m4module.h (m4_arg_text): Add parameter.
+       (M4ARG): Adjust.
+       * m4/m4private.h (CHAR_QUOTE): New input engine sentinel.
+       (m4__make_text_link): New prototype.
+       (struct m4_symbol_chain): Add quote_age member.
+       (struct m4_symbol_value): Add end member to chained symbol.
+       (struct m4_macro_args): Add wrapper member.
+       * m4/symtab.c (m4_symbol_value_print): Print composite tokens.
+       (m4_symbol_value_copy, m4_symbol_value_delete): Recognize
+       composite tokens.
+       * m4/input.c (make_text_link): Rename...
+       (m4__make_text_link): ...to this, and export.
+       (m4_push_string_finish): Adjust caller.
+       (make_text_link, m4__push_symbol): Update new field.
+       (file_read, builtin_read, string_read, composite_read, next_char):
+       Add parameter.
+       (m4_skip_line, match_input, consume_syntax): Adjust callers.
+       (append_quote_token): New function.
+       (m4__next_token): Pass quoted strings onto argument collection.
+       (m4_print_token) [DEBUG_INPUT]: Update.
+       * m4/macro.c (expand_argument): Collect composite arguments.
+       (collect_arguments): Update new field.
+       (expand_macro): Reduce ref-count of back-references after use.
+       (arg_mark, m4_arg_symbol, m4_make_argv_ref): Adjust to new member
+       names.
+       (m4_is_arg_text): Also recognize composite symbols as text.
+       (m4_arg_text, m4_arg_len): Merge composite symbols as needed.
+       (m4_arg_equal): Compare composite symbols.
+       (m4_push_arg, m4_push_args): Handle composite symbols.
+       (m4_arg_symbol): Relax assertion.
+       (process_macro): Use single-argument references.
+       * m4/output.c (m4_shipout_string_trunc): Update comment.
+       * tests/macros.at (Rescanning macros): Augment test.
+
 2008-01-16  Eric Blake  <address@hidden>
 
        Stage 10: avoid extra copying of strings and comments.
diff --git a/HACKING b/HACKING
index d9b978b..f81dd05 100644
--- a/HACKING
+++ b/HACKING
@@ -65,10 +65,11 @@ and is not part of a release distribution.
   - A pre-installed version of GNU M4 1.4.5 or later, built from a
   package
   - Autoconf 2.60 or later
-  - Automake 1.10a or later
+  - Automake 1.10.1 or later
   - CVS Head of Libtool (will become Libtool 2.0)
   - Gettext 0.16 or later
   - Help2man 1.29 or later
+  - LZMA Utils 4.32 or later (from <http://tukaani.org/lzma/>)
   - Texinfo 4.8 or later
   - Any prerequisites of the above (such as perl, tex)
   - A git checkout of gnulib.  A read-only copy of gnulib can be
@@ -234,7 +235,7 @@ yyyy-mm-dd  Name of Author  <address@hidden>  (tiny change)
 
 * Run 'make deltas' (pass LASTRELEASE=maj.min[.mic[alpha]] if needed) to
   create both diff and xdelta files between the previous release tarball
-  and the new.
+  and the new.  TODO - is xdelta still worth using?
 
 * Run '[../]./gnupload --to [dest].gnu.org:m4 [files]' to create
   detached gpg signature and clear signed directive files, and upload
@@ -429,7 +430,7 @@ issue.
 
 
 -- 
-Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 The canonical source of this file is maintained with the
 GNU M4 package.  Report bugs to address@hidden
diff --git a/Makefile.am b/Makefile.am
index 49db5ef..e79210c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
 ## Makefile.am - template for generating Makefile via Automake
 ##
-## Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software
-## Foundation, Inc.
+## Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008 Free
+## Software Foundation, Inc.
 ##
 ## This file is part of GNU M4.
 ##
@@ -86,13 +86,6 @@ src_m4_LDFLAGS       = $(AM_LDFLAGS) $(DLPREOPEN)
 src_m4_LDADD   = m4/libm4.la
 src_m4_DEPENDENCIES = $(PREOPEN_DEPENDENCIES) m4/libm4.la
 
-## Since we do not build a libtool library in src, Automake fails to infer
-## that there may be a libs directory there.  FIXME - revisit this if
-## automake 1.10 fixes the bug.
-clean-local-src:
-       -rm -rf src/.libs src/_libs
-
-
 ##                                                                      ##
 ##                 --- PASTED MANUALLY FROM GNULIB ---                  ##
 ##     To avoid adding unnecessary objects to libm4.la these gnulib     ##
@@ -441,7 +434,7 @@ OTHER_FILES = tests/iso8859.m4 tests/stackovf.test \
 DISTCLEANFILES += tests/atconfig tests/atlocal tests/m4
 MAINTAINERCLEANFILES += tests/generated.at '$(TESTSUITE)'
 
-clean-local: clean-local-src clean-local-tests
+clean-local: clean-local-tests
 
 ##                                                                      ##
 ##                   --- RULES FOR THE MAINTAINER ---                   ##
diff --git a/TODO b/TODO
index 78ee259..f138dd2 100644
--- a/TODO
+++ b/TODO
@@ -132,9 +132,6 @@ for any of these ideas or if you have others to add.
 
 * OTHER TOOLS
 
-  + Automake 1.11 will allow some Makefile.am cleanups:
-    http://lists.gnu.org/archive/html/m4-patches/2007-08/msg00008.html
-
   + Copy coreutils' idea of using rsync, not wget, for grabbing .po
     files efficiently.
 
diff --git a/bootstrap b/bootstrap
index 140ed0e..8da38fd 100755
--- a/bootstrap
+++ b/bootstrap
@@ -1,9 +1,12 @@
 #! /bin/sh
 
-# bootstrap (GNU M4) version 2007-11-05
-# Written by Gary V. Vaughan  <address@hidden>
+# bootstrap (GNU M4) version 2008-01-22
+# Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+# This is free software: you are free to change and redistribute it.
+# There is NO WARRANTY, to the extent permitted by law.
 
-# Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan  <address@hidden>
 
 # This file is part of GNU M4.
 #
@@ -37,7 +40,7 @@
 # This script bootstraps a git or CVS checkout of GNU M4 by correctly calling
 # out to parts of the GNU Build Platform.  Currently this requires GNU
 # Gettext 0.16 or better, Autoconf 2.60 or better, GNU M4 1.4.x or
-# better, a git snapshot of Automake 1.10a or better, a CVS snapshot
+# better, a git snapshot of Automake 1.10.1 or better, a CVS snapshot
 # of Libtool 2.1a or better, and the latest git or CVS checkout of Gnulib.
 # Libtool must be installed; either with the same --prefix as
 # automake, or made accessible to aclocal's search path via
@@ -175,8 +178,8 @@ func_help ()
 # Echo version message to standard output and exit.
 func_version ()
 {
-    $SED '/^# '$PROGRAM' (GNU /,/# warranty; / {
-       s/^# //; s/^# *$//;
+    $SED '/^# '$PROGRAM' (GNU /,/# Written by / {
+       s/^# //;
        s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/;
        p;
      }; d' < "$progpath"
diff --git a/configure.ac b/configure.ac
index d9790e9..bf2b5ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 # Configure template for GNU m4.                       -*-Autoconf-*-
 # Copyright (C) 1991, 1992, 1993, 1994, 2000, 2001, 2002, 2004, 2005, 2006,
-# 2007 Free Software Foundation, Inc.
+# 2007, 2008 Free Software Foundation, Inc.
 #
 # This file is part of GNU M4.
 #
@@ -66,7 +66,7 @@ M4_default_preload="M4_DEFAULT_PRELOAD"
 ## ------------------------ ##
 ## Automake Initialization. ##
 ## ------------------------ ##
-AM_INIT_AUTOMAKE([1.10a subdir-objects dist-bzip2 gnits])
+AM_INIT_AUTOMAKE([1.10.1 subdir-objects dist-bzip2 dist-lzma gnits])
 
 
 
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index df08093..5d87489 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -2460,14 +2460,27 @@ The macro @code{renamesyms} is recognized only with 
parameters.
 This macro was added in M4 2.0.
 @end deffn
 
-Here is an example that starts by performing a similar renaming to the
address@hidden option (or @option{-P}).  Where
address@hidden only renames M4 builtin macros,
+The following example starts with a rename similar to the
address@hidden option (or @option{-P}), prefixing every
+macro with @code{m4_}.  However, note that @option{-P} only renames M4
+builtin macros, even if other macros were defined previously, while
 @code{renamesyms} will rename any macros that match when it runs,
 including text macros.  The rest of the example demonstrates the
 behavior of unanchored regular expressions in symbol renaming.
 
address@hidden options: -Dfoo=bar -P
 @example
+$ @kbd{m4 -Dfoo=bar -P}
+foo
address@hidden
+m4_foo
address@hidden
+m4_defn(`foo')
address@hidden
address@hidden example
+
address@hidden
+$ @kbd{m4}
 define(`foo', `bar')
 @result{}
 renamesyms(`^.*$', `m4_\&')
diff --git a/m4/input.c b/m4/input.c
index 6dcaac0..0dcb0ae 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -93,29 +93,28 @@
    between input blocks must update the context accordingly.  */
 
 static int     file_peek               (m4_input_block *);
-static int     file_read               (m4_input_block *, m4 *, bool);
+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_read            (m4_input_block *, m4 *, bool);
+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_read             (m4_input_block *, m4 *, bool);
+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_read          (m4_input_block *, m4 *, bool);
+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 void    make_text_link          (m4_obstack *, m4_symbol_chain **,
-                                        m4_symbol_chain **);
 static void    init_builtin_token      (m4 *, m4_symbol_value *);
+static void    append_quote_token      (m4_obstack *, m4_symbol_value *);
 static bool    match_input             (m4 *, const char *, bool);
-static int     next_char               (m4 *, bool);
+static int     next_char               (m4 *, bool, bool);
 static int     peek_char               (m4 *);
 static bool    pop_input               (m4 *, bool);
 static void    unget_input             (int);
@@ -133,9 +132,10 @@ struct input_funcs
   int  (*peek_func)    (m4_input_block *);
 
   /* Read input, return an unsigned char, CHAR_BUILTIN if it is a
-     builtin, or CHAR_RETRY if none available.  If SAFE, then do not
-     alter the current file or line.  */
-  int  (*read_func)    (m4_input_block *, m4 *, bool safe);
+     builtin, or CHAR_RETRY if none available.  If ALLOW_QUOTE, then
+     CHAR_QUOTE may be returned.  If SAFE, then do not alter the
+     current file or line.  */
+  int  (*read_func)    (m4_input_block *, m4 *, bool allow_quote, bool safe);
 
   /* Unread a single unsigned character or CHAR_BUILTIN, must be the
      same character previously read by read_func.  */
@@ -269,7 +269,8 @@ file_peek (m4_input_block *me)
 }
 
 static int
-file_read (m4_input_block *me, m4 *context, bool safe M4_GNUC_UNUSED)
+file_read (m4_input_block *me, m4 *context, bool allow_quote M4_GNUC_UNUSED,
+          bool safe M4_GNUC_UNUSED)
 {
   int ch;
 
@@ -397,7 +398,7 @@ builtin_peek (m4_input_block *me)
 
 static int
 builtin_read (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
-             bool safe M4_GNUC_UNUSED)
+             bool allow_quote M4_GNUC_UNUSED, bool safe M4_GNUC_UNUSED)
 {
   if (me->u.u_b.read)
     return CHAR_RETRY;
@@ -479,7 +480,7 @@ string_peek (m4_input_block *me)
 
 static int
 string_read (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
-            bool safe M4_GNUC_UNUSED)
+            bool allow_quote M4_GNUC_UNUSED, bool safe M4_GNUC_UNUSED)
 {
   if (!me->u.u_s.len)
     return CHAR_RETRY;
@@ -560,7 +561,7 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, 
size_t level)
       next->funcs = &composite_funcs;
       next->u.u_c.chain = next->u.u_c.end = NULL;
     }
-  make_text_link (current_input, &next->u.u_c.chain, &next->u.u_c.end);
+  m4__make_text_link (current_input, &next->u.u_c.chain, &next->u.u_c.end);
   chain = (m4_symbol_chain *) obstack_alloc (current_input, sizeof *chain);
   if (next->u.u_c.end)
     next->u.u_c.end->next = chain;
@@ -568,6 +569,7 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, 
size_t level)
     next->u.u_c.chain = chain;
   next->u.u_c.end = chain;
   chain->next = NULL;
+  chain->quote_age = m4_get_symbol_value_quote_age (value);
   chain->str = m4_get_symbol_value_text (value);
   chain->len = m4_get_symbol_value_len (value);
   chain->level = level;
@@ -611,7 +613,8 @@ m4_push_string_finish (void)
          next->u.u_s.len = len;
        }
       else
-       make_text_link (current_input, &next->u.u_c.chain, &next->u.u_c.end);
+       m4__make_text_link (current_input, &next->u.u_c.chain,
+                           &next->u.u_c.end);
       next->prev = isp;
       ret = isp = next;
       input_change = true;
@@ -649,15 +652,19 @@ composite_peek (m4_input_block *me)
 }
 
 static int
-composite_read (m4_input_block *me, m4 *context, bool safe)
+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))
+       return CHAR_QUOTE;
       if (chain->str)
        {
          if (chain->len)
            {
+             /* Partial consumption invalidates quote age.  */
+             chain->quote_age = 0;
              chain->len--;
              return to_uchar (*chain->str++);
            }
@@ -668,8 +675,6 @@ composite_read (m4_input_block *me, m4 *context, bool safe)
          assert (!"implemented yet");
          abort ();
        }
-      if (safe)
-       return CHAR_RETRY;
       if (chain->level < SIZE_MAX)
        m4__adjust_refcount (context, chain->level, false);
       me->u.u_c.chain = chain = chain->next;
@@ -744,9 +749,9 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
 /* Given an obstack OBS, capture any unfinished text as a link in the
    chain that starts at *START and ends at *END.  START may be NULL if
    *END is non-NULL.  */
-static void
-make_text_link (m4_obstack *obs, m4_symbol_chain **start,
-               m4_symbol_chain **end)
+void
+m4__make_text_link (m4_obstack *obs, m4_symbol_chain **start,
+                   m4_symbol_chain **end)
 {
   m4_symbol_chain *chain;
   size_t len = obstack_object_size (obs);
@@ -762,6 +767,7 @@ make_text_link (m4_obstack *obs, m4_symbol_chain **start,
        *start = chain;
       *end = chain;
       chain->next = NULL;
+      chain->quote_age = 0;
       chain->str = str;
       chain->len = len;
       chain->level = SIZE_MAX;
@@ -905,13 +911,43 @@ init_builtin_token (m4 *context, m4_symbol_value *token)
   VALUE_MAX_ARGS (token)       = block->u.u_b.builtin->max_args;
 }
 
+/* When a QUOTE token is seen, convert VALUE to a composite (if it is
+   not one already), consisting of any unfinished text on OBS, as well
+   as the quoted token from the top of the input stack.  Use OBS for
+   any additional allocations needed to store the token chain.  */
+static void
+append_quote_token (m4_obstack *obs, m4_symbol_value *value)
+{
+  m4_symbol_chain *src_chain = isp->u.u_c.chain;
+  m4_symbol_chain *chain;
+  assert (isp->funcs == &composite_funcs && obs);
+
+  if (value->type == M4_SYMBOL_VOID)
+    {
+      value->type = M4_SYMBOL_COMP;
+      value->u.u_c.chain = value->u.u_c.end = NULL;
+    }
+  assert (value->type == M4_SYMBOL_COMP);
+  m4__make_text_link (obs, &value->u.u_c.chain, &value->u.u_c.end);
+  chain = (m4_symbol_chain *) obstack_copy (obs, src_chain, sizeof *chain);
+  if (value->u.u_c.end)
+    value->u.u_c.end->next = chain;
+  else
+    value->u.u_c.chain = chain;
+  value->u.u_c.end = chain;
+  value->u.u_c.end->next = NULL;
+  isp->u.u_c.chain = src_chain->next;
+}
+
 
 /* Low level input is done a character at a time.  The function
    next_char () is used to read and advance the input to the next
-   character.  If RETRY, then avoid returning CHAR_RETRY by popping
-   input.  */
+   character.  If ALLOW_QUOTE, and the current input matches the
+   current quote age, return CHAR_QUOTE and leave consumption of data
+   for append_quote_token.  If RETRY, then avoid returning CHAR_RETRY
+   by popping input.  */
 static int
-next_char (m4 *context, bool retry)
+next_char (m4 *context, bool allow_quote, bool retry)
 {
   int ch;
 
@@ -931,7 +967,8 @@ next_char (m4 *context, bool retry)
        }
 
       assert (isp->funcs->read_func);
-      while ((ch = isp->funcs->read_func (isp, context, !retry)) != CHAR_RETRY
+      while (((ch = isp->funcs->read_func (isp, context, allow_quote, !retry))
+             != CHAR_RETRY)
             || !retry)
        {
          /* if (!IS_IGNORE (ch)) */
@@ -960,7 +997,9 @@ peek_char (m4 *context)
       assert (block->funcs->peek_func);
       if ((ch = block->funcs->peek_func (block)) != CHAR_RETRY)
        {
-         return /* (IS_IGNORE (ch)) ? next_char (context, true) : */ ch;
+/*       if (IS_IGNORE (ch)) */
+/*         return next_char (context, false, true); */
+         return ch;
        }
 
       block = block->prev;
@@ -969,7 +1008,7 @@ peek_char (m4 *context)
 
 /* 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, false).  */
+   to call except immediately after next_char(context, allow, false).  */
 static void
 unget_input (int ch)
 {
@@ -987,7 +1026,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, true)) != CHAR_EOF && ch != '\n')
+  while ((ch = next_char (context, false, true)) != CHAR_EOF && ch != '\n')
     ;
   if (ch == CHAR_EOF)
     /* current_file changed; use the previous value we cached.  */
@@ -1032,14 +1071,14 @@ match_input (m4 *context, const char *s, bool consume)
   if (s[1] == '\0')
     {
       if (consume)
-       next_char (context, true);
+       next_char (context, false, true);
       return true;                     /* short match */
     }
 
-  next_char (context, true);
+  next_char (context, false, true);
   for (n = 1, t = s++; (ch = peek_char (context)) == to_uchar (*s++); )
     {
-      next_char (context, true);
+      next_char (context, false, true);
       n++;
       if (*s == '\0')          /* long match */
        {
@@ -1071,29 +1110,35 @@ match_input (m4 *context, const char *s, bool consume)
 
 /* While the current input character has the given SYNTAX, append it
    to OBS.  Take care not to pop input source unless the next source
-   would continue the chain.  Return true unless the chain ended with
+   would continue the chain.  Return true if the chain ended with
    CHAR_EOF.  */
 static bool
 consume_syntax (m4 *context, m4_obstack *obs, unsigned int syntax)
 {
   int ch;
+  bool allow_quote = m4__safe_quotes (M4SYNTAX);
   assert (syntax);
   while (1)
     {
       /* It is safe to call next_char without first checking
         peek_char, except at input source boundaries, which we detect
-        by CHAR_RETRY.  We exploit the fact that CHAR_EOF and
-        CHAR_MACRO do not satisfy any syntax categories.  */
-      while ((ch = next_char (context, false)) != CHAR_RETRY
+        by CHAR_RETRY.  We exploit the fact that CHAR_EOF,
+        CHAR_BUILTIN, and CHAR_QUOTE do not satisfy any syntax
+        categories.  */
+      while ((ch = next_char (context, allow_quote, false)) != CHAR_RETRY
             && m4_has_syntax (M4SYNTAX, ch, syntax))
-       obstack_1grow (obs, ch);
-      if (ch == CHAR_RETRY)
+       {
+         assert (ch < CHAR_EOF);
+         obstack_1grow (obs, ch);
+       }
+      if (ch == CHAR_RETRY || ch == CHAR_QUOTE)
        {
          ch = peek_char (context);
          if (m4_has_syntax (M4SYNTAX, ch, syntax))
            {
+             assert (ch < CHAR_EOF);
              obstack_1grow (obs, ch);
-             next_char (context, true);
+             next_char (context, false, true);
              continue;
            }
          return ch == CHAR_EOF;
@@ -1141,13 +1186,13 @@ m4_input_exit (void)
 }
 
 
-/* Parse and return a single token from the input stream, built in
-   TOKEN.  See m4__token_type for the valid return types, along with a
-   description of what TOKEN will contain.  If LINE is not NULL, set
-   *LINE to the line number where the token starts.  If OBS, expand
-   safe tokens (strings and comments) directly into OBS rather than in
-   a temporary staging area.  Report errors (unterminated comments or
-   strings) on behalf of CALLER, if non-NULL.
+/* Parse and return a single token from the input stream, constructed
+   into TOKEN.  See m4__token_type for the valid return types, along
+   with a description of what TOKEN will contain.  If LINE is not
+   NULL, set *LINE to the line number where the token starts.  If OBS,
+   expand safe tokens (strings and comments) directly into OBS rather
+   than in a temporary staging area.  Report errors (unterminated
+   comments or strings) on behalf of CALLER, if non-NULL.
 
    If OBS is NULL or the token expansion is unknown, the token text is
    collected on the obstack token_stack, which never contains more
@@ -1177,7 +1222,6 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
   do {
     obstack_free (&token_stack, token_bottom);
 
-
     /* Must consume an input character, but not until CHAR_BUILTIN is
        handled.  */
     ch = peek_char (context);
@@ -1186,28 +1230,29 @@ m4__next_token (m4 *context, m4_symbol_value *token, 
int *line,
 #ifdef DEBUG_INPUT
        xfprintf (stderr, "next_token -> EOF\n");
 #endif
-       next_char (context, true);
+       next_char (context, false, true);
        return M4_TOKEN_EOF;
       }
 
     if (ch == CHAR_BUILTIN)            /* BUILTIN TOKEN */
       {
        init_builtin_token (context, token);
-       next_char (context, true);
+       next_char (context, false, true);
 #ifdef DEBUG_INPUT
        m4_print_token ("next_token", M4_TOKEN_MACDEF, token);
 #endif
        return M4_TOKEN_MACDEF;
       }
 
-    next_char (context, true); /* Consume character we already peeked at.  */
+    /* Consume character we already peeked at.  */
+    next_char (context, false, true);
     file = m4_get_current_file (context);
     *line = m4_get_current_line (context);
 
     if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ESCAPE))
       {                                        /* ESCAPED WORD */
        obstack_1grow (&token_stack, ch);
-       if ((ch = next_char (context, true)) != CHAR_EOF)
+       if ((ch = next_char (context, false, true)) < CHAR_EOF)
          {
            obstack_1grow (&token_stack, ch);
            if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ALPHA))
@@ -1234,12 +1279,13 @@ m4__next_token (m4 *context, m4_symbol_value *token, 
int *line,
        quote_level = 1;
        while (1)
          {
-           ch = next_char (context, true);
+           ch = next_char (context, obs && m4__quote_age (M4SYNTAX), true);
            if (ch == CHAR_EOF)
              m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
                                _("end of file in string"));
-
-           if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_RQUOTE))
+           if (ch == CHAR_QUOTE)
+             append_quote_token (obs, token);
+           else if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_RQUOTE))
              {
                if (--quote_level == 0)
                  break;
@@ -1261,9 +1307,10 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
        if (obs)
          obs_safe = obs;
        quote_level = 1;
+       assert (!m4__quote_age (M4SYNTAX));
        while (1)
          {
-           ch = next_char (context, true);
+           ch = next_char (context, false, true);
            if (ch == CHAR_EOF)
              m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
                                _("end of file in string"));
@@ -1290,11 +1337,14 @@ m4__next_token (m4 *context, m4_symbol_value *token, 
int *line,
        if (obs && !m4_get_discard_comments_opt (context))
          obs_safe = obs;
        obstack_1grow (obs_safe, ch);
-       while ((ch = next_char (context, true)) != CHAR_EOF
+       while ((ch = next_char (context, false, true)) < CHAR_EOF
               && !m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ECOMM))
          obstack_1grow (obs_safe, ch);
        if (ch != CHAR_EOF)
-         obstack_1grow (obs_safe, ch);
+         {
+           assert (ch < CHAR_EOF);
+           obstack_1grow (obs_safe, ch);
+         }
        else
          m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
                            _("end of file in comment"));
@@ -1308,12 +1358,15 @@ m4__next_token (m4 *context, m4_symbol_value *token, 
int *line,
          obs_safe = obs;
        obstack_grow (obs_safe, context->syntax->bcomm.string,
                      context->syntax->bcomm.length);
-       while ((ch = next_char (context, true)) != CHAR_EOF
+       while ((ch = next_char (context, false, true)) < CHAR_EOF
               && !MATCH (context, ch, context->syntax->ecomm.string, true))
          obstack_1grow (obs_safe, ch);
        if (ch != CHAR_EOF)
-         obstack_grow (obs_safe, context->syntax->ecomm.string,
-                       context->syntax->ecomm.length);
+         {
+           assert (ch < CHAR_EOF);
+           obstack_grow (obs_safe, context->syntax->ecomm.string,
+                         context->syntax->ecomm.length);
+         }
        else
          m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
                            _("end of file in comment"));
@@ -1343,6 +1396,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
     else if (m4_is_syntax_single_quotes (M4SYNTAX)
             && m4_is_syntax_single_comments (M4SYNTAX))
       {                        /* EVERYTHING ELSE (SHORT QUOTES AND COMMENTS) 
*/
+       assert (ch < CHAR_EOF);
        obstack_1grow (&token_stack, ch);
 
        if (m4_has_syntax (M4SYNTAX, ch,
@@ -1374,6 +1428,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
       }
     else               /* EVERYTHING ELSE (LONG QUOTES OR COMMENTS) */
       {
+       assert (ch < CHAR_EOF);
        obstack_1grow (&token_stack, ch);
 
        if (m4_has_syntax (M4SYNTAX, ch,
@@ -1394,16 +1449,21 @@ m4__next_token (m4 *context, m4_symbol_value *token, 
int *line,
       }
   } while (type == M4_TOKEN_NONE);
 
-  if (obs_safe != obs)
+  if (token->type == M4_SYMBOL_VOID)
     {
-      len = obstack_object_size (&token_stack);
-      obstack_1grow (&token_stack, '\0');
+      if (obs_safe != obs)
+       {
+         len = obstack_object_size (&token_stack);
+         obstack_1grow (&token_stack, '\0');
 
-      m4_set_symbol_value_text (token, obstack_finish (&token_stack), len,
-                               m4__quote_age (M4SYNTAX));
+         m4_set_symbol_value_text (token, obstack_finish (&token_stack), len,
+                                   m4__quote_age (M4SYNTAX));
+       }
+      else
+       assert (type == M4_TOKEN_STRING);
     }
   else
-    assert (type == M4_TOKEN_STRING);
+    assert (token->type == M4_SYMBOL_COMP && type == M4_TOKEN_STRING);
   VALUE_MAX_ARGS (token) = -1;
 
 #ifdef DEBUG_INPUT
@@ -1440,46 +1500,58 @@ m4__next_token_is_open (m4 *context)
 int
 m4_print_token (const char *s, m4__token_type type, m4_symbol_value *token)
 {
-  xfprintf (stderr, "%s: ", s ? s : "m4input");
+  m4_obstack obs;
+  size_t len;
+
+  obstack_init (&obs);
+  if (!s)
+    s = "m4input";
+  obstack_grow (&obs, s, strlen (s));
+  obstack_1grow (&obs, ':');
+  obstack_1grow (&obs, ' ');
   switch (type)
     {                          /* TOKSW */
     case M4_TOKEN_EOF:
-      xfprintf (stderr, "eof\n");
+      obstack_grow (&obs, "eof", strlen ("eof"));
+      token = NULL;
       break;
     case M4_TOKEN_NONE:
-      xfprintf (stderr, "none\n");
+      obstack_grow (&obs, "none", strlen ("none"));
+      token = NULL;
       break;
     case M4_TOKEN_STRING:
-      xfprintf (stderr, "string\t\"%s\"\n", m4_get_symbol_value_text (token));
+      obstack_grow (&obs, "string\t", strlen ("string\t"));
       break;
     case M4_TOKEN_SPACE:
-      xfprintf (stderr, "space\t\"%s\"\n", m4_get_symbol_value_text (token));
+      obstack_grow (&obs, "space\t", strlen ("space\t"));
       break;
     case M4_TOKEN_WORD:
-      xfprintf (stderr, "word\t\"%s\"\n", m4_get_symbol_value_text (token));
+      obstack_grow (&obs, "word\t", strlen ("word\t"));
       break;
     case M4_TOKEN_OPEN:
-      xfprintf (stderr, "open\t\"%s\"\n", m4_get_symbol_value_text (token));
+      obstack_grow (&obs, "open\t", strlen ("open\t"));
       break;
     case M4_TOKEN_COMMA:
-      xfprintf (stderr, "comma\t\"%s\"\n", m4_get_symbol_value_text (token));
+      obstack_grow (&obs, "comma\t", strlen ("comma\t"));
       break;
     case M4_TOKEN_CLOSE:
-      xfprintf (stderr, "close\t\"%s\"\n", m4_get_symbol_value_text (token));
+      obstack_grow (&obs, "close\t", strlen ("close\t"));
       break;
     case M4_TOKEN_SIMPLE:
-      xfprintf (stderr, "simple\t\"%s\"\n", m4_get_symbol_value_text (token));
+      obstack_grow (&obs, "simple\t", strlen ("simple\t"));
       break;
     case M4_TOKEN_MACDEF:
-      {
-       const m4_builtin *bp;
-       bp = m4_builtin_find_by_func (NULL, m4_get_symbol_value_func (token));
-       assert (bp);
-       xfprintf (stderr, "builtin\t<%s>{%s}\n", bp->name,
-                 m4_get_module_name (VALUE_MODULE (token)));
-      }
+      obstack_grow (&obs, "builtin\t", strlen ("builtin\t"));
       break;
+    default:
+      abort ();
     }
+  if (token)
+    m4_symbol_value_print (token, &obs, true, "\"", "\"", SIZE_MAX, NULL);
+  obstack_1grow (&obs, '\n');
+  len = obstack_object_size (&obs);
+  fwrite (obstack_finish (&obs), 1, len, stderr);
+  obstack_free (&obs, NULL);
   return 0;
 }
 #endif /* DEBUG_INPUT */
diff --git a/m4/m4module.h b/m4/m4module.h
index 03025af..330a90e 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -1,7 +1,7 @@
 /* GNU m4 -- A simple macro processor
 
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1999, 2000, 2003,
-   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -102,8 +102,9 @@ struct m4_macro
        m4_module_import (context, STR (M), STR (S), obs)
 
 /* Grab the text contents of argument I, or abort if the argument is
-   not text.  Assumes that `m4_macro_args *argv' is in scope.  */
-#define M4ARG(i) m4_arg_text (argv, i)
+   not text.  Assumes that `m4 *context' and `m4_macro_args *argv' are
+   in scope.  */
+#define M4ARG(i) m4_arg_text (context, argv, i)
 
 extern bool    m4_bad_argc        (m4 *, int, const char *,
                                    unsigned int, unsigned int, bool);
@@ -304,7 +305,7 @@ 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_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);
diff --git a/m4/m4private.h b/m4/m4private.h
index 630a9b7..6a08455 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -35,7 +35,7 @@ typedef enum {
   M4_SYMBOL_TEXT,              /* Plain text, u.u_t is valid.  */
   M4_SYMBOL_FUNC,              /* Builtin function, u.func is valid.  */
   M4_SYMBOL_PLACEHOLDER,       /* Placeholder for unknown builtin from -R.  */
-  M4_SYMBOL_COMP               /* Composite symbol, u.chain is valid.  */
+  M4_SYMBOL_COMP               /* Composite symbol, u.u_c.c is valid.  */
 } m4__symbol_type;
 
 #define BIT_TEST(flags, bit)   (((flags) & (bit)) == (bit))
@@ -197,6 +197,7 @@ struct m4_symbol
 struct m4_symbol_chain
 {
   m4_symbol_chain *next;/* Pointer to next link of chain.  */
+  unsigned int quote_age; /* Quote_age of this link of chain, or 0.  */
   const char *str;     /* NUL-terminated string if text, or NULL.  */
   size_t len;          /* Length of str, or 0.  */
   size_t level;                /* Expansion level of content, or SIZE_MAX.  */
@@ -230,7 +231,11 @@ struct m4_symbol_value
       unsigned int     quote_age;
     } u_t;                     /* Valid when type is TEXT, PLACEHOLDER.  */
     const m4_builtin * builtin;/* Valid when type is FUNC.  */
-    m4_symbol_chain *  chain;  /* Valid when type is COMP.  */
+    struct
+    {
+      m4_symbol_chain *        chain;  /* First link of the chain.  */
+      m4_symbol_chain *        end;    /* Last link of the chain.  */
+    } u_c;                     /* Valid when type is COMP.  */
   } u;
 };
 
@@ -248,6 +253,9 @@ struct m4_macro_args
   bool_bitfield inuse : 1;
   /* False if all arguments are just text or func, true if this argv
      refers to another one.  */
+  bool_bitfield wrapper : 1;
+  /* False if all arguments belong to this argv, true if some of them
+     include references to another.  */
   bool_bitfield has_ref : 1;
   const char *argv0; /* The macro name being expanded.  */
   size_t argv0_len; /* Length of argv0.  */
@@ -365,7 +373,8 @@ extern void m4__symtab_remove_module_references 
(m4_symbol_table*,
    all other characters and sentinels. */
 #define CHAR_EOF       256     /* Character return on EOF.  */
 #define CHAR_BUILTIN   257     /* Character return for BUILTIN token.  */
-#define CHAR_RETRY     258     /* Character return for end of input block.  */
+#define CHAR_QUOTE     258     /* Character return for quoted string.  */
+#define CHAR_RETRY     259     /* Character return for end of input block.  */
 
 #define DEF_LQUOTE     "`"     /* Default left quote delimiter.  */
 #define DEF_RQUOTE     "\'"    /* Default right quote delimiter.  */
@@ -451,6 +460,8 @@ typedef enum {
   M4_TOKEN_MACDEF      /* Macro's definition (see "defn"), M4_SYMBOL_FUNC.  */
 } m4__token_type;
 
+extern void            m4__make_text_link (m4_obstack *, m4_symbol_chain **,
+                                           m4_symbol_chain **);
 extern bool            m4__push_symbol (m4 *, m4_symbol_value *, size_t);
 extern m4__token_type  m4__next_token (m4 *, m4_symbol_value *, int *,
                                        m4_obstack *, const char *);
diff --git a/m4/macro.c b/m4/macro.c
index 9963409..683dd26 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -334,9 +334,15 @@ expand_argument (m4 *context, m4_obstack *obs, 
m4_symbol_value *argp,
              len = obstack_object_size (obs);
              if (argp->type == M4_SYMBOL_FUNC && !len)
                return type == M4_TOKEN_COMMA;
-             obstack_1grow (obs, '\0');
-             VALUE_MODULE (argp) = NULL;
-             m4_set_symbol_value_text (argp, obstack_finish (obs), len, age);
+             if (argp->type != M4_SYMBOL_COMP)
+               {
+                 obstack_1grow (obs, '\0');
+                 VALUE_MODULE (argp) = NULL;
+                 m4_set_symbol_value_text (argp, obstack_finish (obs), len,
+                                           age);
+               }
+             else
+               m4__make_text_link (obs, NULL, &argp->u.u_c.end);
              return type == M4_TOKEN_COMMA;
            }
          /* fallthru */
@@ -360,6 +366,20 @@ expand_argument (m4 *context, m4_obstack *obs, 
m4_symbol_value *argp,
        case M4_TOKEN_STRING:
          if (!expand_token (context, obs, type, &token, line, first))
            age = 0;
+         if (token.type == M4_SYMBOL_COMP)
+           {
+             if (argp->type != M4_SYMBOL_COMP)
+               {
+                 argp->type = M4_SYMBOL_COMP;
+                 argp->u.u_c.chain = token.u.u_c.chain;
+               }
+             else
+               {
+                 assert (argp->u.u_c.end);
+                 argp->u.u_c.end->next = token.u.u_c.chain;
+               }
+             argp->u.u_c.end = token.u.u_c.end;
+           }
          break;
 
        case M4_TOKEN_MACDEF:
@@ -502,8 +522,23 @@ 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 can be reduced now.  */
-  /* TODO - support references in argv.  */
+  /* 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)
+             {
+               if (chain->level < SIZE_MAX)
+                 m4__adjust_refcount (context, chain->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
@@ -550,6 +585,7 @@ collect_arguments (m4 *context, const char *name, size_t 
len,
 
   args.argc = 1;
   args.inuse = false;
+  args.wrapper = false;
   args.has_ref = false;
   /* Must copy here, since we are consuming tokens, and since symbol
      table can be changed during argument collection.  */
@@ -587,11 +623,14 @@ collect_arguments (m4 *context, const char *name, size_t 
len,
              && m4_get_symbol_value_len (tokenp)
              && m4_get_symbol_value_quote_age (tokenp) != args.quote_age)
            args.quote_age = 0;
+         else if (tokenp->type == M4_SYMBOL_COMP)
+           args.has_ref = true;
        }
       while (more_args);
     }
   argv = (m4_macro_args *) obstack_finish (argv_stack);
   argv->argc = args.argc;
+  argv->has_ref = args.has_ref;
   if (args.quote_age != m4__quote_age (M4SYNTAX))
     argv->quote_age = 0;
   argv->arraylen = args.arraylen;
@@ -674,8 +713,7 @@ process_macro (m4 *context, m4_symbol_value *value, 
m4_obstack *obs,
              text = endp;
            }
          if (i < argc)
-           m4_shipout_string (context, obs, M4ARG (i), m4_arg_len (argv, i),
-                              false);
+           m4_push_arg (context, obs, argv, i);
          break;
 
        case '#':               /* number of arguments */
@@ -947,14 +985,14 @@ static void
 arg_mark (m4_macro_args *argv)
 {
   argv->inuse = true;
-  if (argv->has_ref)
+  if (argv->wrapper)
     {
       /* TODO for now we support only a single-length $@ chain.  */
       assert (argv->arraylen == 1
              && argv->array[0]->type == M4_SYMBOL_COMP
-             && !argv->array[0]->u.chain->next
-             && !argv->array[0]->u.chain->str);
-      argv->array[0]->u.chain->argv->inuse = true;
+             && !argv->array[0]->u.u_c.chain->next
+             && !argv->array[0]->u.u_c.chain->str);
+      argv->array[0]->u.u_c.chain->argv->inuse = true;
     }
 }
 
@@ -970,7 +1008,7 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index)
   if (argv->argc <= index)
     return &empty_symbol;
 
-  if (!argv->has_ref)
+  if (!argv->wrapper)
     return argv->array[index - 1];
   /* Must cycle through all array slots until we find index, since
      wrappers can contain multiple arguments.  */
@@ -979,7 +1017,7 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index)
       value = argv->array[i];
       if (value->type == M4_SYMBOL_COMP)
        {
-         m4_symbol_chain *chain = value->u.chain;
+         m4_symbol_chain *chain = value->u.u_c.chain;
          /* TODO - for now we support only a single $@ chain.  */
          assert (!chain->next && !chain->str);
          if (index < chain->argv->argc - (chain->index - 1))
@@ -994,7 +1032,6 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index)
       else if (--index == 0)
        break;
     }
-  assert (value->type != M4_SYMBOL_COMP);
   return value;
 }
 
@@ -1003,9 +1040,14 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index)
 bool
 m4_is_arg_text (m4_macro_args *argv, unsigned int index)
 {
+  m4_symbol_value *value;
   if (index == 0 || argv->argc <= index)
     return true;
-  return m4_is_symbol_value_text (m4_arg_symbol (argv, index));
+  value = m4_arg_symbol (argv, index);
+  /* Composite tokens are currently sequences of text only.  */
+  if (m4_is_symbol_value_text (value) || value->type == M4_SYMBOL_COMP)
+    return true;
+  return false;
 }
 
 /* Given ARGV, return true if argument INDEX is a builtin function.
@@ -1020,37 +1062,125 @@ m4_is_arg_func (m4_macro_args *argv, unsigned int 
index)
 
 /* Given ARGV, return the text at argument INDEX.  Abort if the
    argument is not text.  Index 0 is always text, and indices beyond
-   argc return the empty string.  */
+   argc return the empty string.  The result is always NUL-terminated,
+   even if it includes embedded NUL characters.  */
 const char *
-m4_arg_text (m4_macro_args *argv, unsigned int index)
+m4_arg_text (m4 *context, m4_macro_args *argv, unsigned int index)
 {
   m4_symbol_value *value;
+  m4_symbol_chain *chain;
+  m4_obstack *obs;
 
   if (index == 0)
     return argv->argv0;
   if (argv->argc <= index)
     return "";
   value = m4_arg_symbol (argv, index);
-  return m4_get_symbol_value_text (value);
+  if (m4_is_symbol_value_text (value))
+    return m4_get_symbol_value_text (value);
+  /* TODO - concatenate argv refs and functions?  For now, we assume
+     all chain elements are text.  */
+  assert (value->type == M4_SYMBOL_COMP);
+  chain = value->u.u_c.chain;
+  obs = m4_arg_scratch (context);
+  while (chain)
+    {
+      assert (chain->str);
+      obstack_grow (obs, chain->str, chain->len);
+      chain = chain->next;
+    }
+  obstack_1grow (obs, '\0');
+  return (char *) obstack_finish (obs);
 }
 
 /* Given ARGV, compare text arguments INDEXA and INDEXB for equality.
    Both indices must be non-zero.  Return true if the arguments
    contain the same contents; often more efficient than
-   !strcmp (m4_arg_text (argv, indexa), m4_arg_text (argv, indexb)).  */
+   !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_symbol_value *sa = m4_arg_symbol (argv, indexa);
   m4_symbol_value *sb = m4_arg_symbol (argv, indexb);
+  m4_symbol_chain tmpa;
+  m4_symbol_chain tmpb;
+  m4_symbol_chain *ca = &tmpa;
+  m4_symbol_chain *cb = &tmpb;
 
+  /* Quick tests.  */
   if (sa == &empty_symbol || sb == &empty_symbol)
     return sa == sb;
+  if (m4_is_symbol_value_text (sa) && m4_is_symbol_value_text (sb))
+    return (m4_get_symbol_value_len (sa) == m4_get_symbol_value_len (sb)
+           && memcmp (m4_get_symbol_value_text (sa),
+                      m4_get_symbol_value_text (sb),
+                      m4_get_symbol_value_len (sa)) == 0);
+
+  /* Convert both arguments to chains, if not one already.  */
   /* TODO - allow builtin tokens in the comparison?  */
-  assert (m4_is_symbol_value_text (sa) && m4_is_symbol_value_text (sb));
-  return (m4_get_symbol_value_len (sa) == m4_get_symbol_value_len (sb)
-         && strcmp (m4_get_symbol_value_text (sa),
-                    m4_get_symbol_value_text (sb)) == 0);
+  if (m4_is_symbol_value_text (sa))
+    {
+      tmpa.next = NULL;
+      tmpa.str = m4_get_symbol_value_text (sa);
+      tmpa.len = m4_get_symbol_value_len (sa);
+    }
+  else
+    {
+      assert (sa->type == M4_SYMBOL_COMP);
+      ca = sa->u.u_c.chain;
+    }
+  if (m4_is_symbol_value_text (sb))
+    {
+      tmpb.next = NULL;
+      tmpb.str = m4_get_symbol_value_text (sb);
+      tmpb.len = m4_get_symbol_value_len (sb);
+    }
+  else
+    {
+      assert (sb->type == M4_SYMBOL_COMP);
+      cb = sb->u.u_c.chain;
+    }
+
+  /* Compare each link of the chain.  */
+  while (ca && cb)
+    {
+      /* TODO support comparison against $@ refs.  */
+      assert (ca->str && cb->str);
+      if (ca->len == cb->len)
+       {
+         if (memcmp (ca->str, cb->str, ca->len) != 0)
+           return false;
+         ca = ca->next;
+         cb = cb->next;
+       }
+      else if (ca->len < cb->len)
+       {
+         if (memcmp (ca->str, cb->str, ca->len) != 0)
+           return false;
+         tmpb.next = cb->next;
+         tmpb.str = cb->str + ca->len;
+         tmpb.len = cb->len - ca->len;
+         ca = ca->next;
+         cb = &tmpb;
+       }
+      else
+       {
+         assert (cb->len < ca->len);
+         if (memcmp (ca->str, cb->str, cb->len) != 0)
+           return false;
+         tmpa.next = ca->next;
+         tmpa.str = ca->str + cb->len;
+         tmpa.len = ca->len - cb->len;
+         ca = &tmpa;
+         cb = cb->next;
+       }
+    }
+
+  /* If we get this far, the two arguments are equal only if both
+     chains are exhausted.  */
+  assert (ca != cb || !ca);
+  return ca == cb;
 }
 
 /* Given ARGV, return true if argument INDEX is the empty string.
@@ -1069,13 +1199,28 @@ size_t
 m4_arg_len (m4_macro_args *argv, unsigned int index)
 {
   m4_symbol_value *value;
+  m4_symbol_chain *chain;
+  size_t len;
 
   if (index == 0)
     return argv->argv0_len;
   if (argv->argc <= index)
     return 0;
   value = m4_arg_symbol (argv, index);
-  return m4_get_symbol_value_len (value);
+  if (m4_is_symbol_value_text (value))
+    return m4_get_symbol_value_len (value);
+  /* TODO - for now, we assume all chain links are text.  */
+  assert (value->type == M4_SYMBOL_COMP);
+  chain = value->u.u_c.chain;
+  len = 0;
+  while (chain)
+    {
+      assert (chain->str);
+      len += chain->len;
+      chain = chain->next;
+    }
+  assert (len);
+  return len;
 }
 
 /* Given ARGV, return the builtin function referenced by argument
@@ -1105,11 +1250,11 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, 
const char *argv0,
 
   /* When making a reference through a reference, point to the
      original if possible.  */
-  if (argv->has_ref)
+  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.chain;
+      chain = argv->array[0]->u.u_c.chain;
       assert (!chain->next && !chain->str);
       argv = chain->argv;
       index += chain->index - 1;
@@ -1130,10 +1275,12 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, 
const char *argv0,
       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.chain = chain;
+      value->u.u_c.chain = value->u.u_c.end = chain;
       chain->next = NULL;
+      chain->quote_age = argv->quote_age;
       chain->str = NULL;
       chain->len = 0;
       chain->level = context->expansion_level - 1;
@@ -1170,9 +1317,23 @@ m4_push_arg (m4 *context, m4_obstack *obs, m4_macro_args 
*argv,
        return;
     }
   /* TODO handle builtin tokens?  */
-  assert (value->type == M4_SYMBOL_TEXT);
-  if (m4__push_symbol (context, value, context->expansion_level - 1))
-    arg_mark (argv);
+  if (value->type == M4_SYMBOL_TEXT)
+    {
+      if (m4__push_symbol (context, value, context->expansion_level - 1))
+       arg_mark (argv);
+    }
+  else if (value->type == M4_SYMBOL_COMP)
+    {
+      /* TODO - really handle composites; for now, just flatten the
+        composite and push its text.  */
+      m4_symbol_chain *chain = value->u.u_c.chain;
+      while (chain)
+       {
+         assert (chain->str);
+         obstack_grow (obs, chain->str, chain->len);
+         chain = chain->next;
+       }
+    }
 }
 
 /* Push series of comma-separated arguments from ARGV, which should
@@ -1184,6 +1345,7 @@ m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, bool skip,
              bool quote)
 {
   m4_symbol_value *value;
+  m4_symbol_chain *chain;
   unsigned int i = skip ? 2 : 1;
   const char *sep = ",";
   size_t sep_len = 1;
@@ -1226,8 +1388,21 @@ m4_push_args (m4 *context, m4_obstack *obs, 
m4_macro_args *argv, bool skip,
       else
        use_sep = true;
       /* TODO handle builtin tokens?  */
-      assert (value->type == M4_SYMBOL_TEXT);
-      inuse |= m4__push_symbol (context, value, context->expansion_level - 1);
+      if (value->type == M4_SYMBOL_TEXT)
+       inuse |= m4__push_symbol (context, value,
+                                 context->expansion_level - 1);
+      else
+       {
+         /* TODO handle composite text.  */
+         assert (value->type == M4_SYMBOL_COMP);
+         chain = value->u.u_c.chain;
+         while (chain)
+           {
+             assert (chain->str);
+             obstack_grow (obs, chain->str, chain->len);
+             chain = chain->next;
+           }
+       }
     }
   if (quote)
     obstack_grow (obs, rquote, strlen (rquote));
diff --git a/m4/output.c b/m4/output.c
index f745efe..dc2194f 100644
--- a/m4/output.c
+++ b/m4/output.c
@@ -602,7 +602,8 @@ m4_shipout_string (m4 *context, m4_obstack *obs, const char 
*s, size_t len,
    current 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.  */
+   true; otherwise return false.  CONTEXT may be NULL if QUOTED is
+   false.  */
 bool
 m4_shipout_string_trunc (m4 *context, m4_obstack *obs, const char *s,
                         size_t len, bool quoted, size_t *max_len)
diff --git a/m4/symtab.c b/m4/symtab.c
index 30a61ed..3ff6f0d 100644
--- a/m4/symtab.c
+++ b/m4/symtab.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2005, 2006, 2007
-   Free Software Foundation, Inc.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2005, 2006,
+   2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -326,10 +326,21 @@ m4_symbol_value_delete (m4_symbol_value *value)
          m4_hash_apply (VALUE_ARG_SIGNATURE (value), arg_destroy_CB, NULL);
          m4_hash_delete (VALUE_ARG_SIGNATURE (value));
        }
-      if (m4_is_symbol_value_text (value))
-       free ((char *) m4_get_symbol_value_text (value));
-      else if (m4_is_symbol_value_placeholder (value))
-       free ((char *) m4_get_symbol_value_placeholder (value));
+      switch (value->type)
+       {
+       case M4_SYMBOL_TEXT:
+         free ((char *) m4_get_symbol_value_text (value));
+         break;
+       case M4_SYMBOL_PLACEHOLDER:
+         free ((char *) m4_get_symbol_value_placeholder (value));
+         break;
+       case M4_SYMBOL_VOID:
+       case M4_SYMBOL_FUNC:
+         break;
+       default:
+         assert (!"m4_symbol_value_delete");
+         abort ();
+       }
       free (value);
     }
 }
@@ -392,10 +403,21 @@ m4_symbol_value_copy (m4_symbol_value *dest, 
m4_symbol_value *src)
   assert (dest);
   assert (src);
 
-  if (m4_is_symbol_value_text (dest))
-    free ((char *) m4_get_symbol_value_text (dest));
-  else if (m4_is_symbol_value_placeholder (dest))
-    free ((char *) m4_get_symbol_value_placeholder (dest));
+  switch (dest->type)
+    {
+    case M4_SYMBOL_TEXT:
+      free ((char *) m4_get_symbol_value_text (dest));
+      break;
+    case M4_SYMBOL_PLACEHOLDER:
+      free ((char *) m4_get_symbol_value_placeholder (dest));
+      break;
+    case M4_SYMBOL_VOID:
+    case M4_SYMBOL_FUNC:
+      break;
+    default:
+      assert (!"m4_symbol_value_delete");
+      abort ();
+    }
 
   if (VALUE_ARG_SIGNATURE (dest))
     {
@@ -411,19 +433,54 @@ m4_symbol_value_copy (m4_symbol_value *dest, 
m4_symbol_value *src)
 
   /* Caller is supposed to free text token strings, so we have to
      copy the string not just its address in that case.  */
-  if (m4_is_symbol_value_text (src))
+  switch (src->type)
     {
-      size_t len = m4_get_symbol_value_len (src);
-      unsigned int age = m4_get_symbol_value_quote_age (src);
-      m4_set_symbol_value_text (dest,
-                               xmemdup (m4_get_symbol_value_text (src),
-                                        len + 1), len, age);
+    case M4_SYMBOL_TEXT:
+      {
+       size_t len = m4_get_symbol_value_len (src);
+       unsigned int age = m4_get_symbol_value_quote_age (src);
+       m4_set_symbol_value_text (dest,
+                                 xmemdup (m4_get_symbol_value_text (src),
+                                          len + 1), len, age);
+      }
+      break;
+    case M4_SYMBOL_FUNC:
+      /* Nothing further to do.  */
+      break;
+    case M4_SYMBOL_PLACEHOLDER:
+      m4_set_symbol_value_placeholder (dest,
+                                      xstrdup (m4_get_symbol_value_placeholder
+                                               (src)));
+      break;
+    case M4_SYMBOL_COMP:
+      {
+       m4_symbol_chain *chain = src->u.u_c.chain;
+       size_t len = 0;
+       char *str;
+       char *p;
+       while (chain)
+         {
+           /* TODO for now, only text links are supported.  */
+           assert (chain->str);
+           len += chain->len;
+           chain = chain->next;
+         }
+       p = str = xcharalloc (len + 1);
+       chain = src->u.u_c.chain;
+       while (chain)
+         {
+           memcpy (p, chain->str, chain->len);
+           p += chain->len;
+           chain = chain->next;
+         }
+       *p = '\0';
+       m4_set_symbol_value_text (dest, str, len, 0);
+      }
+      break;
+    default:
+      assert (!"m4_symbol_value_copy");
+      abort ();
     }
-  else if (m4_is_symbol_value_placeholder (src))
-    m4_set_symbol_value_placeholder (dest,
-                                    xstrdup (m4_get_symbol_value_placeholder
-                                             (src)));
-
   if (VALUE_ARG_SIGNATURE (src))
     VALUE_ARG_SIGNATURE (dest) = m4_hash_dup (VALUE_ARG_SIGNATURE (src),
                                              arg_copy_CB);
@@ -488,8 +545,9 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack 
*obs, bool quote,
   size_t len;
   bool truncated = false;
 
-  if (m4_is_symbol_value_text (value))
+  switch (value->type)
     {
+    case M4_SYMBOL_TEXT:
       text = m4_get_symbol_value_text (value);
       len = m4_get_symbol_value_len (value);
       if (maxlen < len)
@@ -497,27 +555,45 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack 
*obs, bool quote,
          len = maxlen;
          truncated = true;
        }
-    }
-  else if (m4_is_symbol_value_func (value))
-    {
-      const m4_builtin *bp = m4_get_symbol_value_builtin (value);
-      text = bp->name;
-      len = strlen (text);
-      lquote = "<";
-      rquote = ">";
-      quote = true;
-    }
-  else if (m4_is_symbol_value_placeholder (value))
-    {
+      break;
+    case M4_SYMBOL_FUNC:
+      {
+       const m4_builtin *bp = m4_get_symbol_value_builtin (value);
+       text = bp->name;
+       len = strlen (text);
+       lquote = "<";
+       rquote = ">";
+       quote = true;
+      }
+      break;
+    case M4_SYMBOL_PLACEHOLDER:
       text = m4_get_symbol_value_placeholder (value);
       /* FIXME - is it worth translating "placeholder for "?  */
       len = strlen (text);
       lquote = "<placeholder for ";
       rquote = ">";
       quote = true;
-    }
-  else
-    {
+      break;
+    case M4_SYMBOL_COMP:
+      {
+       m4_symbol_chain *chain = value->u.u_c.chain;
+       if (quote)
+         obstack_grow (obs, lquote, strlen (lquote));
+       while (chain)
+         {
+           /* TODO for now, assume all links are text.  */
+           assert (chain->str);
+           if (m4_shipout_string_trunc (NULL, obs, chain->str, chain->len,
+                                        false, &maxlen))
+             break;
+           chain = chain->next;
+         }
+       if (quote)
+         obstack_grow (obs, rquote, strlen (rquote));
+       assert (!module);
+       return;
+      }
+    default:
       assert (!"invalid token in symbol_value_print");
       abort ();
     }
diff --git a/tests/macros.at b/tests/macros.at
index 367d47e..3d74356 100644
--- a/tests/macros.at
+++ b/tests/macros.at
@@ -1,5 +1,5 @@
 # Hand crafted tests for GNU M4.                               -*- Autotest -*-
-# Copyright (C) 2001, 2006, 2007 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 # This file is part of GNU M4.
 #
@@ -535,6 +535,24 @@ AT_CHECK_M4([in], [0], [[40
 ]])
 
 AT_DATA([in], [[define(`echo', `$@')dnl
+define(`foo', echo(`01234567890123456789')echo(`98765432109876543210'))dnl
+foo
+]])
+
+AT_CHECK_M4([in], [0], [[0123456789012345678998765432109876543210
+]])
+
+AT_DATA([in], [[define(`a', `A')define(`echo', `$@')define(`join', `$1$2')dnl
+define(`abcdefghijklmnopqrstuvwxyz', `Z')dnl
+join(`a', `bcdefghijklmnopqrstuvwxyz')
+join(`a', echo(`bcdefghijklmnopqrstuvwxyz'))
+]])
+
+AT_CHECK_M4([in], [0], [[Z
+Z
+]])
+
+AT_DATA([in], [[define(`echo', `$@')dnl
 echo(echo(`01234567890123456789', `01234567890123456789')
 echo(`98765432109876543210', `98765432109876543210'))
 len((echo(`01234567890123456789',


hooks/post-receive
--
GNU M4 source repository




reply via email to

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