m4-patches
[Top][All Lists]
Advanced

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

[12/18] argv_ref speedup: several code cleanups


From: Eric Blake
Subject: [12/18] argv_ref speedup: several code cleanups
Date: Sat, 26 Jan 2008 00:26:54 -0700
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071031 Thunderbird/2.0.0.9 Mnenhy/0.7.5.666

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Next in the series.  This focuses on three general code cleanups that made
later patches in the series easier.  First - make token_chain a
discriminated union (smaller struct with fewer fields to initialize per
use, and the ability to add new union types rather than a binary decision
of string vs $@ reference).  Second - make passing quotes around simpler
(a single struct, with contents and lengths, which will make embedded NUL
in quotes easier to manage, and avoids some strlen).  Third - add a new
macro for the common case of calling arg_len (a later patch adds a
parameter that is typically false; besides, with embedded NUL, the length
becomes a much more important query).  On the master branch, I took the
time to split this into three commits, but since it was one commit in the
argv_ref series, and since some of the hunks overlap, I left it that way
for branch-1_4.  There is a slight memory savings due to a smaller struct,
and a negligible net speed penalty due to added code such as new quote
handling.

2008-01-26  Eric Blake  <address@hidden>

        Stage 12: make token_chain a union, add string_pair.
        Shrink size of symbol chains by using a union.  Make passing quote
        delimiters around more efficient.  Other code cleanups.
        Memory impact: slight improvement, due to smaller struct.
        Speed impact: slight penalty, due to more bookkeeping.
        * src/m4.h (STRING): Delete typedef.
        (struct string_pair, enum token_chain_type): New types.
        (struct token_chain): Reduce size via a union.
        (ARG_LEN): New macro.
        (ARG): Move here...
        * src/builtin.c (ARG): ...from here.
        (dump_args, define_macro, m4_dumpdef, m4_builtin, m4_indir)
        (m4_defn, mkstemp_helper, m4_maketemp, m4_mkstemp, m4___file__)
        (m4___program__, m4_m4wrap, m4_len, m4_index, m4_substr)
        (m4_regexp, m4_patsubst): Adjust callers.
        * src/input.c (rquote, lquote, bcomm, ecomm): Delete...
        (curr_quote, curr_comm): ...replaced by these.
        (make_text_link, push_token, pop_input, input_print, peek_input)
        (next_char_1, input_init, set_quotes, set_comment, set_quote_age)
        (next_token, peek_token): Adjust callers.
        * src/macro.c (expand_macro, arg_token, arg_mark, arg_text)
        (arg_equal, arg_len, make_argv_ref, push_arg, push_args):
        Likewise.
        * src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE, format):
        Likewise.
        * src/freeze.c (produce_frozen_state): Likewise.
        * src/debug.c (trace_format, trace_pre): Likewise.
        (debug_decode): Don't lose partial traces prior to reducing
        debugmode.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFHmuC884KuGfSFAYARAjpMAKCrnciqgfVV8Sld4Kc1OOFANlp4pACgnY0U
/7/ej+YFnmewMvk8VqGeZEo=
=lnQU
-----END PGP SIGNATURE-----
>From 29a7a47303ce73626300a1e3fd09f623bbd57e60 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Fri, 25 Jan 2008 22:45:17 -0700
Subject: [PATCH] Stage 12a: make m4_symbol_chain a union.

* m4/m4private.h (enum m4__symbol_chain_type): New enum.
(struct m4_symbol_chain): Rename...
(struct m4__symbol_chain): ...to this, since it is internal.
* m4/symtab.c (m4_symbol_value_copy, m4_symbol_value_print): All
callers updated.
* m4/input.c (struct m4_input_block, m4__push_symbol)
(composite_peek, composite_read, composite_unget)
(composite_clean, m4__make_text_link, append_quote_token): Likewise.
* m4/macro.c (expand_macro, arg_mark, m4_arg_symbol, m4_arg_text)
(m4_arg_equal, m4_arg_len, m4_make_argv_ref, m4_push_arg)
(m4_push_args): Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog      |   19 ++++++++
 m4/input.c     |  126 ++++++++++++++++++++++++++++---------------------------
 m4/m4private.h |   46 ++++++++++++++------
 m4/macro.c     |  108 ++++++++++++++++++++++++-----------------------
 m4/symtab.c    |   18 ++++----
 5 files changed, 179 insertions(+), 138 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 81643e7..a3f821e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2008-01-26  Eric Blake  <address@hidden>
+
+       Stage 12a: make m4_symbol_chain a union.
+       Shrink size of symbol chains by using a union.
+       Memory impact: slight improvement, due to smaller struct.
+       Speed impact: slight improvement, due to less bookkeeping.
+       * m4/m4private.h (enum m4__symbol_chain_type): New enum.
+       (struct m4_symbol_chain): Rename...
+       (struct m4__symbol_chain): ...to this, since it is internal.
+       * m4/symtab.c (m4_symbol_value_copy, m4_symbol_value_print): All
+       callers updated.
+       * m4/input.c (struct m4_input_block, m4__push_symbol)
+       (composite_peek, composite_read, composite_unget)
+       (composite_clean, composite_print, m4__make_text_link)
+       (append_quote_token): Likewise.
+       * m4/macro.c (expand_macro, arg_mark, m4_arg_symbol, m4_arg_text)
+       (m4_arg_equal, m4_arg_len, m4_make_argv_ref, m4_push_arg)
+       (m4_push_args): Likewise.
+
 2008-01-23  Eric Blake  <address@hidden>
 
        Adjust to recent libtool interface change.
diff --git a/m4/input.c b/m4/input.c
index 0dcb0ae..f45db18 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -186,8 +186,8 @@ struct m4_input_block
       u_b;     /* See builtin_funcs.  */
       struct
        {
-         m4_symbol_chain *chain;       /* Current link in chain.  */
-         m4_symbol_chain *end;         /* Last link in chain.  */
+         m4__symbol_chain *chain;      /* Current link in chain.  */
+         m4__symbol_chain *end;        /* Last link in chain.  */
        }
       u_c;     /* See composite_funcs.  */
     }
@@ -539,7 +539,7 @@ m4_push_string_init (m4 *context)
 bool
 m4__push_symbol (m4 *context, m4_symbol_value *value, size_t level)
 {
-  m4_symbol_chain *chain;
+  m4__symbol_chain *chain;
   bool result = false;
 
   assert (next);
@@ -562,20 +562,18 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, 
size_t level)
       next->u.u_c.chain = next->u.u_c.end = NULL;
     }
   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);
+  chain = (m4__symbol_chain *) obstack_alloc (current_input, sizeof *chain);
   if (next->u.u_c.end)
     next->u.u_c.end->next = chain;
   else
     next->u.u_c.chain = chain;
   next->u.u_c.end = chain;
   chain->next = NULL;
+  chain->type = M4__CHAIN_STR;
   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;
-  chain->argv = NULL;
-  chain->index = 0;
-  chain->flatten = false;
+  chain->u.u_s.str = m4_get_symbol_value_text (value);
+  chain->u.u_s.len = m4_get_symbol_value_len (value);
+  chain->u.u_s.level = level;
   if (level < SIZE_MAX)
     {
       m4__adjust_refcount (context, level, true);
@@ -632,18 +630,19 @@ m4_push_string_finish (void)
 static int
 composite_peek (m4_input_block *me)
 {
-  m4_symbol_chain *chain = me->u.u_c.chain;
+  m4__symbol_chain *chain = me->u.u_c.chain;
   while (chain)
     {
-      if (chain->str)
-       {
-         if (chain->len)
-           return to_uchar (chain->str[0]);
-       }
-      else
+      switch (chain->type)
        {
+       case M4__CHAIN_STR:
+         if (chain->u.u_s.len)
+           return to_uchar (chain->u.u_s.str[0]);
+         break;
+       case M4__CHAIN_ARGV:
          /* TODO - peek into argv.  */
-         assert (!"implemented yet");
+       default:
+         assert (!"composite_peek");
          abort ();
        }
       chain = chain->next;
@@ -654,29 +653,30 @@ composite_peek (m4_input_block *me)
 static int
 composite_read (m4_input_block *me, m4 *context, bool allow_quote, bool safe)
 {
-  m4_symbol_chain *chain = me->u.u_c.chain;
+  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)
+      switch (chain->type)
        {
-         if (chain->len)
+       case M4__CHAIN_STR:
+         if (chain->u.u_s.len)
            {
              /* Partial consumption invalidates quote age.  */
              chain->quote_age = 0;
-             chain->len--;
-             return to_uchar (*chain->str++);
+             chain->u.u_s.len--;
+             return to_uchar (*chain->u.u_s.str++);
            }
-       }
-      else
-       {
+         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.  */
-         assert (!"implemented yet");
+       default:
+         assert (!"composite_read");
          abort ();
        }
-      if (chain->level < SIZE_MAX)
-       m4__adjust_refcount (context, chain->level, false);
       me->u.u_c.chain = chain = chain->next;
     }
   return CHAR_RETRY;
@@ -685,17 +685,18 @@ composite_read (m4_input_block *me, m4 *context, bool 
allow_quote, bool safe)
 static void
 composite_unget (m4_input_block *me, int ch)
 {
-  m4_symbol_chain *chain = me->u.u_c.chain;
-  if (chain->str)
-    {
-      assert (ch < CHAR_EOF && to_uchar (chain->str[-1]) == ch);
-      chain->str--;
-      chain->len++;
-    }
-  else
+  m4__symbol_chain *chain = me->u.u_c.chain;
+  switch (chain->type)
     {
+    case M4__CHAIN_STR:
+      assert (ch < CHAR_EOF && to_uchar (chain->u.u_s.str[-1]) == ch);
+      chain->u.u_s.str--;
+      chain->u.u_s.len++;
+      break;
+    case M4__CHAIN_ARGV:
       /* TODO support argv ref.  */
-      assert (!"implemented yet");
+    default:
+      assert (!"composite_unget");
       abort ();
     }
 }
@@ -703,20 +704,23 @@ composite_unget (m4_input_block *me, int ch)
 static bool
 composite_clean (m4_input_block *me, m4 *context, bool cleanup)
 {
-  m4_symbol_chain *chain = me->u.u_c.chain;
+  m4__symbol_chain *chain = me->u.u_c.chain;
   assert (!chain || !cleanup);
   while (chain)
     {
-      if (chain->str)
-       assert (!chain->len);
-      else
+      switch (chain->type)
        {
+       case M4__CHAIN_STR:
+         assert (!chain->u.u_s.len);
+         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.  */
-         assert (!"implemented yet");
+       default:
+         assert (!"composite_clean");
          abort ();
        }
-      if (chain->level < SIZE_MAX)
-       m4__adjust_refcount (context, chain->level, false);
       me->u.u_c.chain = chain = chain->next;
     }
   return true;
@@ -727,7 +731,7 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
 {
   bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
   size_t maxlen = m4_get_max_debug_arg_length_opt (context);
-  m4_symbol_chain *chain = me->u.u_c.chain;
+  m4__symbol_chain *chain = me->u.u_c.chain;
   const char *lquote = m4_get_syntax_lquote (M4SYNTAX);
   const char *rquote = m4_get_syntax_rquote (M4SYNTAX);
 
@@ -736,9 +740,9 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
   while (chain)
     {
       /* TODO support argv refs as well.  */
-      assert (chain->str);
-      if (m4_shipout_string_trunc (context, obs, chain->str, chain->len, false,
-                                  &maxlen))
+      assert (chain->type == M4__CHAIN_STR);
+      if (m4_shipout_string_trunc (context, obs, chain->u.u_s.str,
+                                  chain->u.u_s.len, false, &maxlen))
        break;
       chain = chain->next;
     }
@@ -750,30 +754,28 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
    chain that starts at *START and ends at *END.  START may be NULL if
    *END is non-NULL.  */
 void
-m4__make_text_link (m4_obstack *obs, m4_symbol_chain **start,
-                   m4_symbol_chain **end)
+m4__make_text_link (m4_obstack *obs, m4__symbol_chain **start,
+                   m4__symbol_chain **end)
 {
-  m4_symbol_chain *chain;
+  m4__symbol_chain *chain;
   size_t len = obstack_object_size (obs);
 
   assert (end && (start || *end));
   if (len)
     {
       char *str = (char *) obstack_finish (obs);
-      chain = (m4_symbol_chain *) obstack_alloc (obs, sizeof *chain);
+      chain = (m4__symbol_chain *) obstack_alloc (obs, sizeof *chain);
       if (*end)
        (*end)->next = chain;
       else
        *start = chain;
       *end = chain;
       chain->next = NULL;
+      chain->type = M4__CHAIN_STR;
       chain->quote_age = 0;
-      chain->str = str;
-      chain->len = len;
-      chain->level = SIZE_MAX;
-      chain->argv = NULL;
-      chain->index = 0;
-      chain->flatten = false;
+      chain->u.u_s.str = str;
+      chain->u.u_s.len = len;
+      chain->u.u_s.level = SIZE_MAX;
     }
 }
 
@@ -918,8 +920,8 @@ init_builtin_token (m4 *context, m4_symbol_value *token)
 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;
+  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)
@@ -929,7 +931,7 @@ append_quote_token (m4_obstack *obs, m4_symbol_value *value)
     }
   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);
+  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
diff --git a/m4/m4private.h b/m4/m4private.h
index 6a08455..dab41c0 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -185,7 +185,7 @@ extern m4_module *  m4__module_find (const char *name);
 
 /* --- SYMBOL TABLE MANAGEMENT --- */
 
-typedef struct m4_symbol_chain m4_symbol_chain;
+typedef struct m4__symbol_chain m4__symbol_chain;
 
 struct m4_symbol
 {
@@ -193,17 +193,35 @@ struct m4_symbol
   m4_symbol_value *value;      /* Linked list of pushdef'd values.  */
 };
 
+/* Type of a link in a symbol chain.  */
+enum m4__symbol_chain_type
+{
+  M4__CHAIN_STR,       /* Link contains a string, u.u_s is valid.  */
+  /* TODO Add M4__CHAIN_FUNC.  */
+  M4__CHAIN_ARGV       /* Link contains a $@ reference, u.u_a is valid.  */
+};
+
 /* Composite symbols are built of a linked list of chain objects.  */
-struct m4_symbol_chain
+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.  */
-  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__symbol_chain *next;              /* Pointer to next link of chain.  */
+  enum m4__symbol_chain_type type;     /* Type of this link.  */
+  unsigned int quote_age;              /* Quote_age of this link, or 0.  */
+  union
+  {
+    struct
+    {
+      const char *str;         /* Pointer to text.  */
+      size_t len;              /* Remaining length of str.  */
+      size_t level;            /* Expansion level of content, or SIZE_MAX.  */
+    } 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.  */
+    } u_a;                     /* M4__CHAIN_ARGV.  */
+  } u;
 };
 
 /* A symbol value is used both for values associated with a macro
@@ -233,8 +251,8 @@ struct m4_symbol_value
     const m4_builtin * builtin;/* Valid when type is FUNC.  */
     struct
     {
-      m4_symbol_chain *        chain;  /* First link of the chain.  */
-      m4_symbol_chain *        end;    /* Last link of the chain.  */
+      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;
 };
@@ -460,8 +478,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 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 683dd26..6671c9c 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -525,7 +525,7 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
   /* If argv contains references, those refcounts must be reduced now.  */
   if (argv->has_ref)
     {
-      m4_symbol_chain *chain;
+      m4__symbol_chain *chain;
       size_t i;
       for (i = 0; i < argv->arraylen; i++)
        if (argv->array[i]->type == M4_SYMBOL_COMP)
@@ -533,8 +533,9 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
            chain = argv->array[i]->u.u_c.chain;
            while (chain)
              {
-               if (chain->level < SIZE_MAX)
-                 m4__adjust_refcount (context, chain->level, false);
+               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;
              }
          }
@@ -991,8 +992,8 @@ arg_mark (m4_macro_args *argv)
       assert (argv->arraylen == 1
              && argv->array[0]->type == M4_SYMBOL_COMP
              && !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;
+             && argv->array[0]->u.u_c.chain->type == M4__CHAIN_ARGV);
+      argv->array[0]->u.u_c.chain->u.u_a.argv->inuse = true;
     }
 }
 
@@ -1017,17 +1018,18 @@ 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.u_c.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))
+         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->argv, chain->index - 1 + index);
-             if (chain->flatten && m4_is_symbol_value_func (value))
+             value = m4_arg_symbol (chain->u.u_a.argv,
+                                    chain->u.u_a.index - 1 + index);
+             if (chain->u.u_a.flatten && m4_is_symbol_value_func (value))
                value = &empty_symbol;
              break;
            }
-         index -= chain->argv->argc - chain->index;
+         index -= chain->u.u_a.argv->argc - chain->u.u_a.index;
        }
       else if (--index == 0)
        break;
@@ -1068,7 +1070,7 @@ const char *
 m4_arg_text (m4 *context, m4_macro_args *argv, unsigned int index)
 {
   m4_symbol_value *value;
-  m4_symbol_chain *chain;
+  m4__symbol_chain *chain;
   m4_obstack *obs;
 
   if (index == 0)
@@ -1085,8 +1087,8 @@ m4_arg_text (m4 *context, m4_macro_args *argv, unsigned 
int index)
   obs = m4_arg_scratch (context);
   while (chain)
     {
-      assert (chain->str);
-      obstack_grow (obs, chain->str, chain->len);
+      assert (chain->type == M4__CHAIN_STR);
+      obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
       chain = chain->next;
     }
   obstack_1grow (obs, '\0');
@@ -1103,10 +1105,10 @@ 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;
+  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)
@@ -1122,8 +1124,9 @@ m4_arg_equal (m4_macro_args *argv, unsigned int indexa, 
unsigned int indexb)
   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);
+      tmpa.type = M4__CHAIN_STR;
+      tmpa.u.u_s.str = m4_get_symbol_value_text (sa);
+      tmpa.u.u_s.len = m4_get_symbol_value_len (sa);
     }
   else
     {
@@ -1133,8 +1136,9 @@ m4_arg_equal (m4_macro_args *argv, unsigned int indexa, 
unsigned int indexb)
   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);
+      tmpb.type = M4__CHAIN_STR;
+      tmpb.u.u_s.str = m4_get_symbol_value_text (sb);
+      tmpb.u.u_s.len = m4_get_symbol_value_len (sb);
     }
   else
     {
@@ -1146,32 +1150,32 @@ m4_arg_equal (m4_macro_args *argv, unsigned int indexa, 
unsigned int indexb)
   while (ca && cb)
     {
       /* TODO support comparison against $@ refs.  */
-      assert (ca->str && cb->str);
-      if (ca->len == cb->len)
+      assert (ca->type == M4__CHAIN_STR && cb->type == M4__CHAIN_STR);
+      if (ca->u.u_s.len == cb->u.u_s.len)
        {
-         if (memcmp (ca->str, cb->str, ca->len) != 0)
+         if (memcmp (ca->u.u_s.str, cb->u.u_s.str, ca->u.u_s.len) != 0)
            return false;
          ca = ca->next;
          cb = cb->next;
        }
-      else if (ca->len < cb->len)
+      else if (ca->u.u_s.len < cb->u.u_s.len)
        {
-         if (memcmp (ca->str, cb->str, ca->len) != 0)
+         if (memcmp (ca->u.u_s.str, cb->u.u_s.str, ca->u.u_s.len) != 0)
            return false;
          tmpb.next = cb->next;
-         tmpb.str = cb->str + ca->len;
-         tmpb.len = cb->len - ca->len;
+         tmpb.u.u_s.str = cb->u.u_s.str + ca->u.u_s.len;
+         tmpb.u.u_s.len = cb->u.u_s.len - ca->u.u_s.len;
          ca = ca->next;
          cb = &tmpb;
        }
       else
        {
-         assert (cb->len < ca->len);
-         if (memcmp (ca->str, cb->str, cb->len) != 0)
+         assert (cb->u.u_s.len < ca->u.u_s.len);
+         if (memcmp (ca->u.u_s.str, cb->u.u_s.str, cb->u.u_s.len) != 0)
            return false;
          tmpa.next = ca->next;
-         tmpa.str = ca->str + cb->len;
-         tmpa.len = ca->len - cb->len;
+         tmpa.u.u_s.str = ca->u.u_s.str + cb->u.u_s.len;
+         tmpa.u.u_s.len = ca->u.u_s.len - cb->u.u_s.len;
          ca = &tmpa;
          cb = cb->next;
        }
@@ -1199,7 +1203,7 @@ size_t
 m4_arg_len (m4_macro_args *argv, unsigned int index)
 {
   m4_symbol_value *value;
-  m4_symbol_chain *chain;
+  m4__symbol_chain *chain;
   size_t len;
 
   if (index == 0)
@@ -1215,8 +1219,8 @@ m4_arg_len (m4_macro_args *argv, unsigned int index)
   len = 0;
   while (chain)
     {
-      assert (chain->str);
-      len += chain->len;
+      assert (chain->type == M4__CHAIN_STR);
+      len += chain->u.u_s.len;
       chain = chain->next;
     }
   assert (len);
@@ -1244,7 +1248,7 @@ 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;
+  m4__symbol_chain *chain;
   unsigned int index = skip ? 2 : 1;
   m4_obstack *obs = m4_arg_scratch (context);
 
@@ -1255,9 +1259,9 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, const 
char *argv0,
       /* 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->str);
-      argv = chain->argv;
-      index += chain->index - 1;
+      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)
     {
@@ -1272,7 +1276,7 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, const 
char *argv0,
                                                                  array)
                                                        + sizeof value));
       value = (m4_symbol_value *) obstack_alloc (obs, sizeof *value);
-      chain = (m4_symbol_chain *) obstack_alloc (obs, sizeof *chain);
+      chain = (m4__symbol_chain *) obstack_alloc (obs, sizeof *chain);
       new_argv->arraylen = 1;
       new_argv->array[0] = value;
       new_argv->wrapper = true;
@@ -1280,13 +1284,11 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, 
const char *argv0,
       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->str = NULL;
-      chain->len = 0;
-      chain->level = context->expansion_level - 1;
-      chain->argv = argv;
-      chain->index = index;
-      chain->flatten = flatten;
+      chain->u.u_a.argv = argv;
+      chain->u.u_a.index = index;
+      chain->u.u_a.flatten = flatten;
     }
   new_argv->argc = argv->argc - (index - 1);
   new_argv->inuse = false;
@@ -1326,11 +1328,11 @@ m4_push_arg (m4 *context, m4_obstack *obs, 
m4_macro_args *argv,
     {
       /* TODO - really handle composites; for now, just flatten the
         composite and push its text.  */
-      m4_symbol_chain *chain = value->u.u_c.chain;
+      m4__symbol_chain *chain = value->u.u_c.chain;
       while (chain)
        {
-         assert (chain->str);
-         obstack_grow (obs, chain->str, chain->len);
+         assert (chain->type == M4__CHAIN_STR);
+         obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
          chain = chain->next;
        }
     }
@@ -1345,7 +1347,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;
+  m4__symbol_chain *chain;
   unsigned int i = skip ? 2 : 1;
   const char *sep = ",";
   size_t sep_len = 1;
@@ -1398,8 +1400,8 @@ m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, bool skip,
          chain = value->u.u_c.chain;
          while (chain)
            {
-             assert (chain->str);
-             obstack_grow (obs, chain->str, chain->len);
+             assert (chain->type == M4__CHAIN_STR);
+             obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
              chain = chain->next;
            }
        }
diff --git a/m4/symtab.c b/m4/symtab.c
index 3ff6f0d..1c419eb 100644
--- a/m4/symtab.c
+++ b/m4/symtab.c
@@ -454,23 +454,23 @@ m4_symbol_value_copy (m4_symbol_value *dest, 
m4_symbol_value *src)
       break;
     case M4_SYMBOL_COMP:
       {
-       m4_symbol_chain *chain = src->u.u_c.chain;
+       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;
+           assert (chain->type == M4__CHAIN_STR);
+           len += chain->u.u_s.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;
+           memcpy (p, chain->u.u_s.str, chain->u.u_s.len);
+           p += chain->u.u_s.len;
            chain = chain->next;
          }
        *p = '\0';
@@ -576,15 +576,15 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack 
*obs, bool quote,
       break;
     case M4_SYMBOL_COMP:
       {
-       m4_symbol_chain *chain = value->u.u_c.chain;
+       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))
+           assert (chain->type == M4__CHAIN_STR);
+           if (m4_shipout_string_trunc (NULL, obs, chain->u.u_s.str,
+                                        chain->u.u_s.len, NULL, &maxlen))
              break;
            chain = chain->next;
          }
-- 
1.5.3.8


>From 726430fec6c0f3807f8f9e4d2c681f5300dc7694 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Fri, 25 Jan 2008 23:49:38 -0700
Subject: [PATCH] Stage 12b: add m4_string_pair.

* m4/m4module.h (m4_string_pair): New type.
(m4_get_syntax_quotes, m4_get_syntax_comments): New prototypes.
(m4_symbol_value_print, m4_symbol_print, m4_shipout_string_trunc):
Alter signature.
* m4/m4private.h (struct m4_string): Delete.
(struct m4_syntax_table): Combine quote and comment members.
(m4_get_syntax_lquote, m4_get_syntax_rquote, m4_get_syntax_bcomm)
(m4_get_syntax_ecomm): Adjust accessors.
(m4_get_syntax_quotes, m4_get_syntax_comments): New fast
accessors.
* m4/symtab.c (m4_symbol_value_print, m4_symbol_print):
Alter signatures.
* m4/input.c (string_print, composite_print, m4_input_print):
All callers updated.
* m4/syntax.c (m4_syntax_delete, m4_set_syntax)
(check_is_single_quotes, m4_set_quotes, set_quote_age)
(m4_get_syntax_lquote, m4_get_syntax_rquote)
(m4_get_syntax_quotes, check_is_single_comments, m4_set_comment)
(m4_get_syntax_bcomm, m4_get_syntax_ecomm)
(m4_get_syntax_comments): Likewise.
* m4/macro.c (trace_prepre, trace_pre, m4_push_args): Likewise.
* m4/output.c (m4_shipout_string, m4_shipout_string_trunc):
Likewise.
* modules/m4.c (dumpdef, m4_make_temp): Likewise.
* src/freeze.c (produce_frozen_state): Likewise.
* tests/freeze.at (reloading unknown builtin): Update test.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog       |   63 +++++++++++++++++------
 m4/input.c      |   73 +++++++++++++++------------
 m4/m4module.h   |   27 +++++++---
 m4/m4private.h  |   21 +++-----
 m4/macro.c      |   37 +++++++-------
 m4/output.c     |   29 +++++------
 m4/symtab.c     |   64 +++++++++++-------------
 m4/syntax.c     |  150 ++++++++++++++++++++++++++++++------------------------
 modules/m4.c    |   21 +++-----
 src/freeze.c    |   31 +++++-------
 tests/freeze.at |    4 +-
 11 files changed, 281 insertions(+), 239 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a3f821e..67ce533 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,21 +1,52 @@
 2008-01-26  Eric Blake  <address@hidden>
 
-       Stage 12a: make m4_symbol_chain a union.
-       Shrink size of symbol chains by using a union.
-       Memory impact: slight improvement, due to smaller struct.
-       Speed impact: slight improvement, due to less bookkeeping.
-       * m4/m4private.h (enum m4__symbol_chain_type): New enum.
-       (struct m4_symbol_chain): Rename...
-       (struct m4__symbol_chain): ...to this, since it is internal.
-       * m4/symtab.c (m4_symbol_value_copy, m4_symbol_value_print): All
-       callers updated.
-       * m4/input.c (struct m4_input_block, m4__push_symbol)
-       (composite_peek, composite_read, composite_unget)
-       (composite_clean, composite_print, m4__make_text_link)
-       (append_quote_token): Likewise.
-       * m4/macro.c (expand_macro, arg_mark, m4_arg_symbol, m4_arg_text)
-       (m4_arg_equal, m4_arg_len, m4_make_argv_ref, m4_push_arg)
-       (m4_push_args): Likewise.
+       Stage 12b: add m4_string_pair.
+       Make passing quote delimiters around more efficient.
+       Memory impact: none.
+       Speed impact: slight penalty, due to more bookkeeping.
+       * m4/m4module.h (m4_string_pair): New type.
+       (m4_get_syntax_quotes, m4_get_syntax_comments): New prototypes.
+       (m4_symbol_value_print, m4_symbol_print, m4_shipout_string_trunc):
+       Alter signature.
+       * m4/m4private.h (struct m4_string): Delete.
+       (struct m4_syntax_table): Combine quote and comment members.
+       (m4_get_syntax_lquote, m4_get_syntax_rquote, m4_get_syntax_bcomm)
+       (m4_get_syntax_ecomm): Adjust accessors.
+       (m4_get_syntax_quotes, m4_get_syntax_comments): New fast
+       accessors.
+       * m4/symtab.c (m4_symbol_value_print, m4_symbol_print):
+       Alter signatures.
+       * m4/input.c (string_print, composite_print, m4_input_print):
+       All callers updated.
+       * m4/syntax.c (m4_syntax_delete, m4_set_syntax)
+       (check_is_single_quotes, m4_set_quotes, set_quote_age)
+       (m4_get_syntax_lquote, m4_get_syntax_rquote)
+       (m4_get_syntax_quotes, check_is_single_comments, m4_set_comment)
+       (m4_get_syntax_bcomm, m4_get_syntax_ecomm)
+       (m4_get_syntax_comments): Likewise.
+       * m4/macro.c (trace_prepre, trace_pre, m4_push_args): Likewise.
+       * m4/output.c (m4_shipout_string, m4_shipout_string_trunc):
+       Likewise.
+       * modules/m4.c (dumpdef, m4_make_temp): Likewise.
+       * src/freeze.c (produce_frozen_state): Likewise.
+       * tests/freeze.at (reloading unknown builtin): Update test.
+
+        Stage 12a: make m4_symbol_chain a union.
+        Shrink size of symbol chains by using a union.
+        Memory impact: slight improvement, due to smaller struct.
+        Speed impact: slight improvement, due to less bookkeeping.
+        * m4/m4private.h (enum m4__symbol_chain_type): New enum.
+        (struct m4_symbol_chain): Rename...
+        (struct m4__symbol_chain): ...to this, since it is internal.
+        * m4/symtab.c (m4_symbol_value_copy, m4_symbol_value_print): All
+        callers updated.
+        * m4/input.c (struct m4_input_block, m4__push_symbol)
+        (composite_peek, composite_read, composite_unget)
+        (composite_clean, composite_print, m4__make_text_link)
+        (append_quote_token): Likewise.
+        * m4/macro.c (expand_macro, arg_mark, m4_arg_symbol, m4_arg_text)
+        (m4_arg_equal, m4_arg_len, m4_make_argv_ref, m4_push_arg)
+        (m4_push_args): Likewise.
 
 2008-01-23  Eric Blake  <address@hidden>
 
diff --git a/m4/input.c b/m4/input.c
index f45db18..4adce9d 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -502,8 +502,9 @@ string_print (m4_input_block *me, m4 *context, m4_obstack 
*obs)
   bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
   size_t arg_length = m4_get_max_debug_arg_length_opt (context);
 
-  m4_shipout_string_trunc (context, obs, me->u.u_s.str, me->u.u_s.len,
-                          quote, &arg_length);
+  m4_shipout_string_trunc (obs, me->u.u_s.str, me->u.u_s.len,
+                          quote ? m4_get_syntax_quotes (M4SYNTAX) : NULL,
+                          &arg_length);
 }
 
 /* First half of m4_push_string ().  The pointer next points to the
@@ -732,22 +733,30 @@ composite_print (m4_input_block *me, m4 *context, 
m4_obstack *obs)
   bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
   size_t maxlen = m4_get_max_debug_arg_length_opt (context);
   m4__symbol_chain *chain = me->u.u_c.chain;
-  const char *lquote = m4_get_syntax_lquote (M4SYNTAX);
-  const char *rquote = m4_get_syntax_rquote (M4SYNTAX);
+  const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX);
+  bool done = false;
 
   if (quote)
-    m4_shipout_string (context, obs, lquote, SIZE_MAX, false);
-  while (chain)
+    m4_shipout_string (context, obs, quotes->str1, quotes->len1, false);
+  while (chain && !done)
     {
-      /* TODO support argv refs as well.  */
-      assert (chain->type == M4__CHAIN_STR);
-      if (m4_shipout_string_trunc (context, obs, chain->u.u_s.str,
-                                  chain->u.u_s.len, false, &maxlen))
-       break;
+      switch (chain->type)
+       {
+       case M4__CHAIN_STR:
+         if (m4_shipout_string_trunc (obs, chain->u.u_s.str,
+                                      chain->u.u_s.len, NULL, &maxlen))
+           done = true;
+         break;
+       case M4__CHAIN_ARGV:
+         /* TODO support argv refs as well.  */
+       default:
+         assert (!"composite_print");
+         abort ();
+       }
       chain = chain->next;
     }
   if (quote)
-    m4_shipout_string (context, obs, rquote, SIZE_MAX, false);
+    m4_shipout_string (context, obs, quotes->str2, quotes->len2, false);
 }
 
 /* Given an obstack OBS, capture any unfinished text as a link in the
@@ -789,13 +798,11 @@ m4_input_print (m4 *context, m4_obstack *obs, 
m4_input_block *input)
   assert (context && obs);
   if (input == NULL)
     {
-      bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
-      if (quote)
+      if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
        {
-         const char *lquote = m4_get_syntax_lquote (M4SYNTAX);
-         const char *rquote = m4_get_syntax_rquote (M4SYNTAX);
-         obstack_grow (obs, lquote, strlen (lquote));
-         obstack_grow (obs, rquote, strlen (rquote));
+         const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX);
+         obstack_grow (obs, quotes->str1, quotes->len1);
+         obstack_grow (obs, quotes->str2, quotes->len2);
        }
     }
   else
@@ -1304,7 +1311,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int 
*line,
        type = M4_TOKEN_STRING;
       }
     else if (!m4_is_syntax_single_quotes (M4SYNTAX)
-            && MATCH (context, ch, context->syntax->lquote.string, true))
+            && MATCH (context, ch, context->syntax->quote.str1, true))
       {                                        /* QUOTED STRING, LONGER QUOTES 
*/
        if (obs)
          obs_safe = obs;
@@ -1316,18 +1323,18 @@ m4__next_token (m4 *context, m4_symbol_value *token, 
int *line,
            if (ch == CHAR_EOF)
              m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
                                _("end of file in string"));
-           if (MATCH (context, ch, context->syntax->rquote.string, true))
+           if (MATCH (context, ch, context->syntax->quote.str2, true))
              {
                if (--quote_level == 0)
                  break;
-               obstack_grow (obs_safe, context->syntax->rquote.string,
-                             context->syntax->rquote.length);
+               obstack_grow (obs_safe, context->syntax->quote.str2,
+                             context->syntax->quote.len2);
              }
-           else if (MATCH (context, ch, context->syntax->lquote.string, true))
+           else if (MATCH (context, ch, context->syntax->quote.str1, true))
              {
                quote_level++;
-               obstack_grow (obs_safe, context->syntax->lquote.string,
-                             context->syntax->lquote.length);
+               obstack_grow (obs_safe, context->syntax->quote.str1,
+                             context->syntax->quote.len2);
              }
            else
              obstack_1grow (obs_safe, ch);
@@ -1354,20 +1361,20 @@ m4__next_token (m4 *context, m4_symbol_value *token, 
int *line,
                ? M4_TOKEN_NONE : M4_TOKEN_STRING);
       }
     else if (!m4_is_syntax_single_comments (M4SYNTAX)
-            && MATCH (context, ch, context->syntax->bcomm.string, true))
+            && MATCH (context, ch, context->syntax->comm.str1, true))
       {                                        /* COMMENT, LONGER DELIM */
        if (obs && !m4_get_discard_comments_opt (context))
          obs_safe = obs;
-       obstack_grow (obs_safe, context->syntax->bcomm.string,
-                     context->syntax->bcomm.length);
+       obstack_grow (obs_safe, context->syntax->comm.str1,
+                     context->syntax->comm.len1);
        while ((ch = next_char (context, false, true)) < CHAR_EOF
-              && !MATCH (context, ch, context->syntax->ecomm.string, true))
+              && !MATCH (context, ch, context->syntax->comm.str2, true))
          obstack_1grow (obs_safe, ch);
        if (ch != CHAR_EOF)
          {
            assert (ch < CHAR_EOF);
-           obstack_grow (obs_safe, context->syntax->ecomm.string,
-                         context->syntax->ecomm.length);
+           obstack_grow (obs_safe, context->syntax->comm.str2,
+                         context->syntax->comm.len2);
          }
        else
          m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller,
@@ -1489,9 +1496,9 @@ m4__next_token_is_open (m4 *context)
                                       | M4_SYNTAX_ALPHA | M4_SYNTAX_LQUOTE
                                       | M4_SYNTAX_ACTIVE))
       || (!m4_is_syntax_single_comments (M4SYNTAX)
-         && MATCH (context, ch, context->syntax->bcomm.string, false))
+         && MATCH (context, ch, context->syntax->comm.str1, false))
       || (!m4_is_syntax_single_quotes (M4SYNTAX)
-         && MATCH (context, ch, context->syntax->lquote.string, false)))
+         && MATCH (context, ch, context->syntax->quote.str1, false)))
     return false;
   return m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_OPEN);
 }
diff --git a/m4/m4module.h b/m4/m4module.h
index 330a90e..eac4c29 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -38,6 +38,7 @@ typedef struct m4_symbol_value        m4_symbol_value;
 typedef struct m4_input_block  m4_input_block;
 typedef struct m4_module       m4_module;
 typedef struct m4_macro_args   m4_macro_args;
+typedef struct m4_string_pair  m4_string_pair;
 
 typedef struct obstack         m4_obstack;
 
@@ -77,6 +78,15 @@ struct m4_macro
   const char *value;
 };
 
+/* Describe a pair of strings, such as begin and end quotes.  */
+struct m4_string_pair
+{
+  char *str1;          /* First string.  */
+  size_t len1;         /* First length.  */
+  char *str2;          /* Second string.  */
+  size_t len2;         /* Second length.  */
+};
+
 #define M4BUILTIN(name)                                                        
\
   static void CONC (builtin_, name)                                    \
    (m4 *context, m4_obstack *obs, unsigned int argc, m4_macro_args *argv);
@@ -231,12 +241,11 @@ 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 *, bool,
-                                        const char *, const char *, size_t,
+extern void    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);
-extern void    m4_symbol_print         (m4_symbol *, m4_obstack *, bool,
-                                        const char *, const char *, bool,
-                                        size_t, bool);
 extern bool    m4_symbol_value_groks_macro     (m4_symbol_value *);
 
 #define m4_is_symbol_void(symbol)                                      \
@@ -313,7 +322,7 @@ extern size_t       m4_arg_len              (m4_macro_args 
*, unsigned int);
 extern m4_builtin_func *m4_arg_func    (m4_macro_args *, unsigned int);
 extern m4_obstack *m4_arg_scratch      (m4 *);
 extern m4_macro_args *m4_make_argv_ref (m4 *, m4_macro_args *, const char *,
-                                         size_t, bool, bool);
+                                        size_t, bool, bool);
 extern void    m4_push_arg             (m4 *, m4_obstack *, m4_macro_args *,
                                         unsigned int);
 extern void    m4_push_args            (m4 *, m4_obstack *, m4_macro_args *,
@@ -384,6 +393,8 @@ extern      const char *     m4_get_syntax_lquote   
(m4_syntax_table *syntax);
 extern const char *     m4_get_syntax_rquote   (m4_syntax_table *syntax);
 extern const char *     m4_get_syntax_bcomm    (m4_syntax_table *syntax);
 extern const char *     m4_get_syntax_ecomm    (m4_syntax_table *syntax);
+extern const m4_string_pair *m4_get_syntax_quotes      (m4_syntax_table *);
+extern const m4_string_pair *m4_get_syntax_comments    (m4_syntax_table *);
 
 extern bool             m4_is_syntax_single_quotes     (m4_syntax_table *);
 extern bool             m4_is_syntax_single_comments   (m4_syntax_table *);
@@ -462,8 +473,8 @@ extern void m4_divert_text          (m4 *, m4_obstack *, 
const char *,
 extern void    m4_shipout_int          (m4_obstack *, int);
 extern void    m4_shipout_string       (m4 *, m4_obstack *, const char *,
                                         size_t, bool);
-extern bool    m4_shipout_string_trunc (m4 *, m4_obstack *, const char *,
-                                        size_t, bool, size_t *);
+extern bool    m4_shipout_string_trunc (m4_obstack *, const char *, size_t,
+                                        const m4_string_pair *, size_t *);
 
 extern void    m4_make_diversion    (m4 *, int);
 extern void    m4_insert_diversion  (m4 *, int);
diff --git a/m4/m4private.h b/m4/m4private.h
index dab41c0..4261c4c 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -399,21 +399,14 @@ extern void m4__symtab_remove_module_references 
(m4_symbol_table*,
 #define DEF_BCOMM      "#"     /* Default begin comment delimiter.  */
 #define DEF_ECOMM      "\n"    /* Default end comment delimiter.  */
 
-typedef struct {
-  char *string;                /* characters of the string */
-  size_t length;       /* length of the string */
-} m4_string;
-
 struct m4_syntax_table {
   /* Please read the comment at the top of input.c for details.  table
      holds the current syntax, and orig holds the default syntax.  */
   unsigned short table[CHAR_RETRY];
   unsigned short orig[CHAR_RETRY];
 
-  m4_string lquote;
-  m4_string rquote;
-  m4_string bcomm;
-  m4_string ecomm;
+  m4_string_pair quote;        /* Quote delimiters.  */
+  m4_string_pair comm; /* Comment delimiters.  */
 
   /* True iff strlen(lquote) == strlen(rquote) == 1 and lquote is not
      interfering with macro names.  */
@@ -442,10 +435,12 @@ struct m4_syntax_table {
 /* Fast macro versions of syntax table accessor functions,
    that also have an identically named function exported in m4module.h.  */
 #ifdef NDEBUG
-#  define m4_get_syntax_lquote(S)              ((S)->lquote.string)
-#  define m4_get_syntax_rquote(S)              ((S)->rquote.string)
-#  define m4_get_syntax_bcomm(S)               ((S)->bcomm.string)
-#  define m4_get_syntax_ecomm(S)               ((S)->ecomm.string)
+#  define m4_get_syntax_lquote(S)              ((S)->quote.str1)
+#  define m4_get_syntax_rquote(S)              ((S)->quote.str2)
+#  define m4_get_syntax_bcomm(S)               ((S)->comm.str1)
+#  define m4_get_syntax_ecomm(S)               ((S)->comm.str2)
+#  define m4_get_syntax_quotes(S)              (&(S)->quote)
+#  define m4_get_syntax_comments(S)            (&(S)->comm)
 
 #  define m4_is_syntax_single_quotes(S)                ((S)->is_single_quotes)
 #  define m4_is_syntax_single_comments(S)      ((S)->is_single_comments)
diff --git a/m4/macro.c b/m4/macro.c
index 6671c9c..4753984 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -888,16 +888,16 @@ trace_flush (m4 *context)
 static void
 trace_prepre (m4 *context, const char *name, size_t id, m4_symbol_value *value)
 {
-  bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
-  const char *lquote = m4_get_syntax_lquote (M4SYNTAX);
-  const char *rquote = m4_get_syntax_rquote (M4SYNTAX);
+  const m4_string_pair *quotes = NULL;
   size_t arg_length = m4_get_max_debug_arg_length_opt (context);
   bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE);
 
+  if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
+    quotes = m4_get_syntax_quotes (M4SYNTAX);
   trace_header (context, id);
   trace_format (context, "%s ... = ", name);
-  m4_symbol_value_print (value, &context->trace_messages,
-                        quote, lquote, rquote, arg_length, module);
+  m4_symbol_value_print (value, &context->trace_messages, quotes, arg_length,
+                        module);
   trace_flush (context);
 }
 
@@ -914,12 +914,12 @@ trace_pre (m4 *context, size_t id, m4_macro_args *argv)
 
   if (1 < argc && m4_is_debug_bit (context, M4_DEBUG_TRACE_ARGS))
     {
-      bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
-      const char *lquote = m4_get_syntax_lquote (M4SYNTAX);
-      const char *rquote = m4_get_syntax_rquote (M4SYNTAX);
+      const m4_string_pair *quotes = NULL;
       size_t arg_length = m4_get_max_debug_arg_length_opt (context);
       bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE);
 
+      if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
+       quotes = m4_get_syntax_quotes (M4SYNTAX);
       trace_format (context, "(");
       for (i = 1; i < argc; i++)
        {
@@ -927,8 +927,8 @@ trace_pre (m4 *context, size_t id, m4_macro_args *argv)
            trace_format (context, ", ");
 
          m4_symbol_value_print (m4_arg_symbol (argv, i),
-                                &context->trace_messages, quote, lquote,
-                                rquote, arg_length, module);
+                                &context->trace_messages, quotes, arg_length,
+                                module);
        }
       trace_format (context, ")");
     }
@@ -1353,8 +1353,7 @@ m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, bool skip,
   size_t sep_len = 1;
   bool use_sep = false;
   bool inuse = false;
-  const char *lquote = m4_get_syntax_lquote (M4SYNTAX);
-  const char *rquote = m4_get_syntax_rquote (M4SYNTAX);
+  const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX);
   m4_obstack *scratch = m4_arg_scratch (context);
 
   if (argv->argc <= i)
@@ -1363,22 +1362,22 @@ m4_push_args (m4 *context, m4_obstack *obs, 
m4_macro_args *argv, bool skip,
   if (argv->argc == i + 1)
     {
       if (quote)
-       obstack_grow (obs, lquote, strlen (lquote));
+       obstack_grow (obs, quotes->str1, quotes->len1);
       m4_push_arg (context, obs, argv, i);
       if (quote)
-       obstack_grow (obs, rquote, strlen (rquote));
+       obstack_grow (obs, quotes->str2, quotes->len2);
       return;
     }
 
   /* Compute the separator in the scratch space.  */
   if (quote)
     {
-      obstack_grow (obs, lquote, strlen (lquote));
-      obstack_grow (scratch, rquote, strlen (rquote));
+      obstack_grow (obs, quotes->str1, quotes->len1);
+      obstack_grow (scratch, quotes->str2, quotes->len2);
       obstack_1grow (scratch, ',');
-      obstack_grow0 (scratch, lquote, strlen (lquote));
+      obstack_grow0 (scratch, quotes->str1, quotes->len1);
       sep = (char *) obstack_finish (scratch);
-      sep_len += strlen (lquote) + strlen (rquote);
+      sep_len += quotes->len1 + quotes->len2;
     }
 
   /* TODO push entire $@ ref, rather than each arg.  */
@@ -1407,7 +1406,7 @@ m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args 
*argv, bool skip,
        }
     }
   if (quote)
-    obstack_grow (obs, rquote, strlen (rquote));
+    obstack_grow (obs, quotes->str2, quotes->len2);
   if (inuse)
     arg_mark (argv);
 }
diff --git a/m4/output.c b/m4/output.c
index dc2194f..21a28f7 100644
--- a/m4/output.c
+++ b/m4/output.c
@@ -595,27 +595,27 @@ void
 m4_shipout_string (m4 *context, m4_obstack *obs, const char *s, size_t len,
                   bool quoted)
 {
-  m4_shipout_string_trunc (context, obs, s, len, quoted, NULL);
+  m4_shipout_string_trunc (obs, s, len,
+                          quoted ? m4_get_syntax_quotes (M4SYNTAX) : NULL,
+                          NULL);
 }
 
-/* Output the text S, of length LEN, to OBS.  If QUOTED, also output
-   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.  CONTEXT may be NULL if QUOTED is
-   false.  */
+/* Output the text S, of length LEN, to OBS.  If QUOTES, also output
+   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.  */
 bool
-m4_shipout_string_trunc (m4 *context, m4_obstack *obs, const char *s,
-                        size_t len, bool quoted, size_t *max_len)
+m4_shipout_string_trunc (m4_obstack *obs, const char *s, size_t len,
+                        const m4_string_pair *quotes, size_t *max_len)
 {
   size_t max = max_len ? *max_len : SIZE_MAX;
 
   assert (obs && s);
   if (len == SIZE_MAX)
     len = strlen (s);
-  if (quoted)
-    obstack_grow (obs, context->syntax->lquote.string,
-                 context->syntax->lquote.length);
+  if (quotes)
+    obstack_grow (obs, quotes->str1, quotes->len1);
   if (len < max)
     {
       obstack_grow (obs, s, len);
@@ -627,9 +627,8 @@ m4_shipout_string_trunc (m4 *context, m4_obstack *obs, 
const char *s,
       obstack_grow (obs, "...", 3);
       max = 0;
     }
-  if (quoted)
-    obstack_grow (obs, context->syntax->rquote.string,
-                 context->syntax->rquote.length);
+  if (quotes)
+    obstack_grow (obs, quotes->str2, quotes->len2);
   if (max_len)
     *max_len = max;
   return max == 0;
diff --git a/m4/symtab.c b/m4/symtab.c
index 1c419eb..60afe63 100644
--- a/m4/symtab.c
+++ b/m4/symtab.c
@@ -532,13 +532,13 @@ m4_set_symbol_name_traced (m4_symbol_table *symtab, const 
char *name,
   return result;
 }
 
-/* Grow OBS with a text representation of VALUE.  If QUOTE, then
-   surround a text definition by LQUOTE and RQUOTE.  If MAXLEN is less
-   than SIZE_MAX, then truncate text definitions to that length.  If
-   MODULE, then include which module defined a builtin.  */
+/* 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
-m4_symbol_value_print (m4_symbol_value *value, m4_obstack *obs, bool quote,
-                      const char *lquote, const char *rquote, size_t maxlen,
+m4_symbol_value_print (m4_symbol_value *value, m4_obstack *obs,
+                      const m4_string_pair *quotes, size_t maxlen,
                       bool module)
 {
   const char *text;
@@ -559,37 +559,34 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack 
*obs, bool quote,
     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);
-       lquote = "<";
-       rquote = ">";
-       quote = true;
+       quotes = &q1;
       }
       break;
     case M4_SYMBOL_PLACEHOLDER:
       text = m4_get_symbol_value_placeholder (value);
-      /* FIXME - is it worth translating "placeholder for "?  */
+      static const m4_string_pair q2 = { "<<", 2, ">>", 2 };
       len = strlen (text);
-      lquote = "<placeholder for ";
-      rquote = ">";
-      quote = true;
+      quotes = &q2;
       break;
     case M4_SYMBOL_COMP:
       {
        m4__symbol_chain *chain = value->u.u_c.chain;
-       if (quote)
-         obstack_grow (obs, lquote, strlen (lquote));
+       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 (NULL, obs, chain->u.u_s.str,
+           if (m4_shipout_string_trunc (obs, chain->u.u_s.str,
                                         chain->u.u_s.len, NULL, &maxlen))
              break;
            chain = chain->next;
          }
-       if (quote)
-         obstack_grow (obs, rquote, strlen (rquote));
+       if (quotes)
+         obstack_grow (obs, quotes->str2, quotes->len2);
        assert (!module);
        return;
       }
@@ -598,13 +595,13 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack 
*obs, bool quote,
       abort ();
     }
 
-  if (quote)
-    obstack_grow (obs, lquote, strlen (lquote));
+  if (quotes)
+    obstack_grow (obs, quotes->str1, quotes->len1);
   obstack_grow (obs, text, len);
   if (truncated)
     obstack_grow (obs, "...", 3);
-  if (quote)
-    obstack_grow (obs, rquote, strlen (rquote));
+  if (quotes)
+    obstack_grow (obs, quotes->str2, quotes->len2);
   if (module && VALUE_MODULE (value))
     {
       obstack_1grow (obs, '{');
@@ -614,16 +611,15 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack 
*obs, bool quote,
     }
 }
 
-/* Grow OBS with a text representation of SYMBOL.  If QUOTE, then
-   surround each text definition by LQUOTE and RQUOTE.  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.  */
+/* 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.  */
 void
-m4_symbol_print (m4_symbol *symbol, m4_obstack *obs, bool quote,
-                const char *lquote, const char *rquote, bool stack,
-                size_t arg_length, bool module)
+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;
 
@@ -631,8 +627,7 @@ m4_symbol_print (m4_symbol *symbol, m4_obstack *obs, bool 
quote,
   assert (obs);
 
   value = m4_get_symbol_value (symbol);
-  m4_symbol_value_print (value, obs, quote, lquote, rquote, arg_length,
-                        module);
+  m4_symbol_value_print (value, obs, quotes, arg_length, module);
   if (stack)
     {
       value = VALUE_NEXT (value);
@@ -640,8 +635,7 @@ m4_symbol_print (m4_symbol *symbol, m4_obstack *obs, bool 
quote,
        {
          obstack_1grow (obs, ',');
          obstack_1grow (obs, ' ');
-         m4_symbol_value_print (value, obs, quote, lquote, rquote,
-                                arg_length, module);
+         m4_symbol_value_print (value, obs, quotes, arg_length, module);
          value = VALUE_NEXT (value);
        }
     }
diff --git a/m4/syntax.c b/m4/syntax.c
index 0bce3c0..c39a9be 100644
--- a/m4/syntax.c
+++ b/m4/syntax.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2002, 2004, 2006, 2007
-   Free Software Foundation, Inc.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2002, 2004, 2006,
+   2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -168,10 +168,10 @@ m4_syntax_delete (m4_syntax_table *syntax)
 {
   assert (syntax);
 
-  free (syntax->lquote.string);
-  free (syntax->rquote.string);
-  free (syntax->bcomm.string);
-  free (syntax->ecomm.string);
+  free (syntax->quote.str1);
+  free (syntax->quote.str2);
+  free (syntax->comm.str1);
+  free (syntax->comm.str2);
   free (syntax);
 }
 
@@ -371,23 +371,23 @@ m4_set_syntax (m4_syntax_table *syntax, char key, char 
action,
         properties.  */
       memcpy (syntax->table, syntax->orig, sizeof syntax->orig);
 
-      free (syntax->lquote.string);
-      free (syntax->rquote.string);
-      free (syntax->bcomm.string);
-      free (syntax->ecomm.string);
-
-      syntax->lquote.string    = xstrdup (DEF_LQUOTE);
-      syntax->lquote.length    = strlen (syntax->lquote.string);
-      syntax->rquote.string    = xstrdup (DEF_RQUOTE);
-      syntax->rquote.length    = strlen (syntax->rquote.string);
-      syntax->bcomm.string     = xstrdup (DEF_BCOMM);
-      syntax->bcomm.length     = strlen (syntax->bcomm.string);
-      syntax->ecomm.string     = xstrdup (DEF_ECOMM);
-      syntax->ecomm.length     = strlen (syntax->ecomm.string);
-
-      add_syntax_attribute (syntax, to_uchar (syntax->rquote.string[0]),
+      free (syntax->quote.str1);
+      free (syntax->quote.str2);
+      free (syntax->comm.str1);
+      free (syntax->comm.str2);
+
+      syntax->quote.str1       = xstrdup (DEF_LQUOTE);
+      syntax->quote.len1       = 1;
+      syntax->quote.str2       = xstrdup (DEF_RQUOTE);
+      syntax->quote.len2       = 1;
+      syntax->comm.str1                = xstrdup (DEF_BCOMM);
+      syntax->comm.len1                = 1;
+      syntax->comm.str2                = xstrdup (DEF_ECOMM);
+      syntax->comm.len2                = 1;
+
+      add_syntax_attribute (syntax, to_uchar (syntax->quote.str2[0]),
                            M4_SYNTAX_RQUOTE);
-      add_syntax_attribute (syntax, to_uchar (syntax->ecomm.string[0]),
+      add_syntax_attribute (syntax, to_uchar (syntax->comm.str2[0]),
                            M4_SYNTAX_ECOMM);
 
       syntax->is_single_quotes         = true;
@@ -432,10 +432,10 @@ check_is_single_quotes (m4_syntax_table *syntax)
 
   if (! syntax->is_single_quotes)
     return false;
-  assert (syntax->lquote.length == 1 && syntax->rquote.length == 1);
+  assert (syntax->quote.len1 == 1 && syntax->quote.len2 == 1);
 
-  if (m4_has_syntax (syntax, *syntax->lquote.string, M4_SYNTAX_LQUOTE)
-      && m4_has_syntax (syntax, *syntax->rquote.string, M4_SYNTAX_RQUOTE))
+  if (m4_has_syntax (syntax, *syntax->quote.str1, M4_SYNTAX_LQUOTE)
+      && m4_has_syntax (syntax, *syntax->quote.str2, M4_SYNTAX_RQUOTE))
     return true;
 
   /* The most recent action invalidated our current lquote/rquote.  If
@@ -470,8 +470,8 @@ check_is_single_quotes (m4_syntax_table *syntax)
     syntax->is_single_quotes = false;
   else if (syntax->is_single_quotes)
     {
-      *syntax->lquote.string = lquote;
-      *syntax->rquote.string = rquote;
+      *syntax->quote.str1 = lquote;
+      *syntax->quote.str2 = rquote;
     }
   return syntax->is_single_quotes;
 }
@@ -485,10 +485,10 @@ check_is_single_comments (m4_syntax_table *syntax)
 
   if (! syntax->is_single_comments)
     return false;
-  assert (syntax->bcomm.length == 1 && syntax->ecomm.length == 1);
+  assert (syntax->comm.len1 == 1 && syntax->comm.len2 == 1);
 
-  if (m4_has_syntax (syntax, *syntax->bcomm.string, M4_SYNTAX_BCOMM)
-      && m4_has_syntax (syntax, *syntax->ecomm.string, M4_SYNTAX_ECOMM))
+  if (m4_has_syntax (syntax, *syntax->comm.str1, M4_SYNTAX_BCOMM)
+      && m4_has_syntax (syntax, *syntax->comm.str2, M4_SYNTAX_ECOMM))
     return true;
 
   /* The most recent action invalidated our current bcomm/ecomm.  If
@@ -523,8 +523,8 @@ check_is_single_comments (m4_syntax_table *syntax)
     syntax->is_single_comments = false;
   else if (syntax->is_single_comments)
     {
-      *syntax->bcomm.string = bcomm;
-      *syntax->ecomm.string = ecomm;
+      *syntax->comm.str1 = bcomm;
+      *syntax->comm.str2 = ecomm;
     }
   return syntax->is_single_comments;
 }
@@ -572,24 +572,24 @@ m4_set_quotes (m4_syntax_table *syntax, const char *lq, 
const char *rq)
   else if (!rq || (*lq && !*rq))
     rq = DEF_RQUOTE;
 
-  if (strcmp (syntax->lquote.string, lq) == 0
-      && strcmp (syntax->rquote.string, rq) == 0)
+  if (strcmp (syntax->quote.str1, lq) == 0
+      && strcmp (syntax->quote.str2, rq) == 0)
     return;
 
-  free (syntax->lquote.string);
-  free (syntax->rquote.string);
-  syntax->lquote.string = xstrdup (lq);
-  syntax->lquote.length = strlen (syntax->lquote.string);
-  syntax->rquote.string = xstrdup (rq);
-  syntax->rquote.length = strlen (syntax->rquote.string);
+  free (syntax->quote.str1);
+  free (syntax->quote.str2);
+  syntax->quote.str1 = xstrdup (lq);
+  syntax->quote.len1 = strlen (lq);
+  syntax->quote.str2 = xstrdup (rq);
+  syntax->quote.len2 = strlen (rq);
 
   /* changequote overrides syntax_table, but be careful when it is
      used to select a start-quote sequence that is effectively
      disabled.  */
 
   syntax->is_single_quotes
-    = (syntax->lquote.length == 1 && syntax->rquote.length == 1
-       && !m4_has_syntax (syntax, *syntax->lquote.string,
+    = (syntax->quote.len1 == 1 && syntax->quote.len2 == 1
+       && !m4_has_syntax (syntax, *syntax->quote.str1,
                          (M4_SYNTAX_IGNORE | M4_SYNTAX_ESCAPE
                           | M4_SYNTAX_ALPHA | M4_SYNTAX_NUM)));
 
@@ -605,9 +605,9 @@ m4_set_quotes (m4_syntax_table *syntax, const char *lq, 
const char *rq)
 
   if (syntax->is_single_quotes)
     {
-      add_syntax_attribute (syntax, to_uchar (syntax->lquote.string[0]),
+      add_syntax_attribute (syntax, to_uchar (syntax->quote.str1[0]),
                            M4_SYNTAX_LQUOTE);
-      add_syntax_attribute (syntax, to_uchar (syntax->rquote.string[0]),
+      add_syntax_attribute (syntax, to_uchar (syntax->quote.str2[0]),
                            M4_SYNTAX_RQUOTE);
     }
   if (syntax->is_macro_escaped)
@@ -634,24 +634,24 @@ m4_set_comment (m4_syntax_table *syntax, const char *bc, 
const char *ec)
   else if (!ec || (*bc && !*ec))
     ec = DEF_ECOMM;
 
-  if (strcmp (syntax->bcomm.string, bc) == 0
-      && strcmp (syntax->ecomm.string, ec) == 0)
+  if (strcmp (syntax->comm.str1, bc) == 0
+      && strcmp (syntax->comm.str2, ec) == 0)
     return;
 
-  free (syntax->bcomm.string);
-  free (syntax->ecomm.string);
-  syntax->bcomm.string = xstrdup (bc);
-  syntax->bcomm.length = strlen (syntax->bcomm.string);
-  syntax->ecomm.string = xstrdup (ec);
-  syntax->ecomm.length = strlen (syntax->ecomm.string);
+  free (syntax->comm.str1);
+  free (syntax->comm.str2);
+  syntax->comm.str1 = xstrdup (bc);
+  syntax->comm.len1 = strlen (bc);
+  syntax->comm.str2 = xstrdup (ec);
+  syntax->comm.len2 = strlen (ec);
 
   /* changecom overrides syntax_table, but be careful when it is used
      to select a start-comment sequence that is effectively
      disabled.  */
 
   syntax->is_single_comments
-    = (syntax->bcomm.length == 1 && syntax->ecomm.length == 1
-       && !m4_has_syntax (syntax, *syntax->bcomm.string,
+    = (syntax->comm.len1 == 1 && syntax->comm.len2 == 1
+       && !m4_has_syntax (syntax, *syntax->comm.str1,
                          (M4_SYNTAX_IGNORE | M4_SYNTAX_ESCAPE
                           | M4_SYNTAX_ALPHA | M4_SYNTAX_NUM
                           | M4_SYNTAX_LQUOTE)));
@@ -667,9 +667,9 @@ m4_set_comment (m4_syntax_table *syntax, const char *bc, 
const char *ec)
     }
   if (syntax->is_single_comments)
     {
-      add_syntax_attribute (syntax, to_uchar (syntax->bcomm.string[0]),
+      add_syntax_attribute (syntax, to_uchar (syntax->comm.str1[0]),
                            M4_SYNTAX_BCOMM);
-      add_syntax_attribute (syntax, to_uchar (syntax->ecomm.string[0]),
+      add_syntax_attribute (syntax, to_uchar (syntax->comm.str2[0]),
                            M4_SYNTAX_ECOMM);
     }
   if (syntax->is_macro_escaped)
@@ -728,22 +728,22 @@ set_quote_age (m4_syntax_table *syntax, bool reset, bool 
change)
   else
     local_syntax_age = syntax->syntax_age;
   if (local_syntax_age < 0xffff && syntax->is_single_quotes
-      && !m4_has_syntax (syntax, *syntax->lquote.string,
+      && !m4_has_syntax (syntax, *syntax->quote.str1,
                         (M4_SYNTAX_ALPHA | M4_SYNTAX_NUM | M4_SYNTAX_OPEN
                          | M4_SYNTAX_COMMA | M4_SYNTAX_CLOSE
                          | M4_SYNTAX_SPACE))
-      && !m4_has_syntax (syntax, *syntax->rquote.string,
+      && !m4_has_syntax (syntax, *syntax->quote.str2,
                         (M4_SYNTAX_ALPHA | M4_SYNTAX_NUM | M4_SYNTAX_OPEN
                          | M4_SYNTAX_COMMA | M4_SYNTAX_CLOSE
                          | M4_SYNTAX_SPACE))
-      && *syntax->lquote.string != *syntax->rquote.string
-      && *syntax->bcomm.string != *syntax->lquote.string
-      && !m4_has_syntax (syntax, *syntax->bcomm.string,
+      && *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))
     {
       syntax->quote_age = ((local_syntax_age << 16)
-                          | ((*syntax->lquote.string & 0xff) << 8)
-                          | (*syntax->rquote.string & 0xff));
+                          | ((*syntax->quote.str1 & 0xff) << 8)
+                          | (*syntax->quote.str2 & 0xff));
     }
   else
     syntax->quote_age = 0;
@@ -757,7 +757,7 @@ const char *
 m4_get_syntax_lquote (m4_syntax_table *syntax)
 {
   assert (syntax);
-  return syntax->lquote.string;
+  return syntax->quote.str1;
 }
 
 #undef m4_get_syntax_rquote
@@ -765,7 +765,15 @@ const char *
 m4_get_syntax_rquote (m4_syntax_table *syntax)
 {
   assert (syntax);
-  return syntax->rquote.string;
+  return syntax->quote.str2;
+}
+
+#undef m4_get_syntax_quotes
+const m4_string_pair *
+m4_get_syntax_quotes (m4_syntax_table *syntax)
+{
+  assert (syntax);
+  return &syntax->quote;
 }
 
 #undef m4_is_syntax_single_quotes
@@ -781,7 +789,7 @@ const char *
 m4_get_syntax_bcomm (m4_syntax_table *syntax)
 {
   assert (syntax);
-  return syntax->bcomm.string;
+  return syntax->comm.str1;
 }
 
 #undef m4_get_syntax_ecomm
@@ -789,7 +797,15 @@ const char *
 m4_get_syntax_ecomm (m4_syntax_table *syntax)
 {
   assert (syntax);
-  return syntax->ecomm.string;
+  return syntax->comm.str2;
+}
+
+#undef m4_get_syntax_comments
+const m4_string_pair *
+m4_get_syntax_comments (m4_syntax_table *syntax)
+{
+  assert (syntax);
+  return &syntax->comm;
 }
 
 #undef m4_is_syntax_single_comments
diff --git a/modules/m4.c b/modules/m4.c
index 4b1416e..42dd661 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -341,13 +341,13 @@ m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, 
unsigned int argc,
 M4BUILTIN_HANDLER (dumpdef)
 {
   m4_dump_symbol_data data;
-  bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
-  const char *lquote = m4_get_syntax_lquote (M4SYNTAX);
-  const char *rquote = m4_get_syntax_rquote (M4SYNTAX);
+  const m4_string_pair *quotes = NULL;
   bool stack = m4_is_debug_bit (context, M4_DEBUG_TRACE_STACK);
   size_t arg_length = m4_get_max_debug_arg_length_opt (context);
   bool module = m4_is_debug_bit (context, M4_DEBUG_TRACE_MODULE);
 
+  if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
+    quotes = m4_get_syntax_quotes (M4SYNTAX);
   data.obs = m4_arg_scratch (context);
   m4_dump_symbols (context, &data, argc, argv, true);
 
@@ -359,8 +359,7 @@ M4BUILTIN_HANDLER (dumpdef)
       obstack_grow (obs, data.base[0], strlen (data.base[0]));
       obstack_1grow (obs, ':');
       obstack_1grow (obs, '\t');
-      m4_symbol_print (symbol, obs, quote, lquote, rquote, stack, arg_length,
-                      module);
+      m4_symbol_print (symbol, obs, quotes, stack, arg_length, module);
       obstack_1grow (obs, '\n');
     }
 
@@ -698,8 +697,7 @@ m4_make_temp (m4 *context, m4_obstack *obs, const char 
*macro,
   int fd;
   int i;
   char *name;
-  const char *tmp;
-  size_t qlen;
+  const m4_string_pair *quotes = m4_get_syntax_quotes (M4SYNTAX);
 
   if (m4_get_safer_opt (context))
     {
@@ -711,15 +709,13 @@ m4_make_temp (m4 *context, m4_obstack *obs, const char 
*macro,
      user forgot to supply them.  Output must be quoted if
      successful.  */
   assert (obstack_object_size (obs) == 0);
-  tmp = m4_get_syntax_lquote (M4SYNTAX);
-  qlen = strlen (tmp);
-  obstack_grow (obs, tmp, qlen);
+  obstack_grow (obs, quotes->str1, quotes->len1);
   obstack_grow (obs, pattern, len);
   for (i = 0; len > 0 && i < 6; i++)
     if (pattern[--len] != 'X')
       break;
   obstack_grow0 (obs, "XXXXXX", 6 - i);
-  name = (char *) obstack_base (obs) + qlen;
+  name = (char *) obstack_base (obs) + quotes->len1;
 
   /* Make the temporary object.  */
   errno = 0;
@@ -741,8 +737,7 @@ m4_make_temp (m4 *context, m4_obstack *obs, const char 
*macro,
        close (fd);
       /* Remove NUL, then finish quote.  */
       obstack_blank (obs, -1);
-      tmp = m4_get_syntax_rquote (M4SYNTAX);
-      obstack_grow (obs, tmp, strlen (tmp));
+      obstack_grow (obs, quotes->str2, quotes->len2);
     }
 }
 
diff --git a/src/freeze.c b/src/freeze.c
index cdadf60..941b761 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006, 2007
-   Free Software Foundation, Inc.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006,
+   2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -213,6 +213,7 @@ produce_frozen_state (m4 *context, const char *name)
 {
   FILE *file = fopen (name, O_BINARY ? "wb" : "w");
   const char *str;
+  const m4_string_pair *pair;
 
   if (!file)
     {
@@ -227,28 +228,22 @@ produce_frozen_state (m4 *context, const char *name)
   fputs ("V2\n", file);
 
   /* Dump quote delimiters.  */
-
-  if (strcmp (m4_get_syntax_lquote (M4SYNTAX), DEF_LQUOTE)
-      || strcmp (m4_get_syntax_rquote (M4SYNTAX), DEF_RQUOTE))
+  pair = m4_get_syntax_quotes (M4SYNTAX);
+  if (strcmp (pair->str1, DEF_LQUOTE) || strcmp (pair->str2, DEF_RQUOTE))
     {
-      xfprintf (file, "Q%zu,%zu\n", M4SYNTAX->lquote.length,
-              M4SYNTAX->rquote.length);
-      produce_mem_dump (file, M4SYNTAX->lquote.string,
-                       M4SYNTAX->lquote.length);
-      produce_mem_dump (file, M4SYNTAX->rquote.string,
-                       M4SYNTAX->rquote.length);
+      xfprintf (file, "Q%zu,%zu\n", pair->len1, pair->len2);
+      produce_mem_dump (file, pair->str1, pair->len1);
+      produce_mem_dump (file, pair->str2, pair->len2);
       fputc ('\n', file);
     }
 
   /* Dump comment delimiters.  */
-
-  if (strcmp (m4_get_syntax_bcomm (M4SYNTAX), DEF_BCOMM)
-      || strcmp (m4_get_syntax_ecomm (M4SYNTAX), DEF_ECOMM))
+  pair = m4_get_syntax_comments (M4SYNTAX);
+  if (strcmp (pair->str1, DEF_BCOMM) || strcmp (pair->str2, DEF_ECOMM))
     {
-      xfprintf (file, "C%zu,%zu\n", M4SYNTAX->bcomm.length,
-               M4SYNTAX->ecomm.length);
-      produce_mem_dump (file, M4SYNTAX->bcomm.string, M4SYNTAX->bcomm.length);
-      produce_mem_dump (file, M4SYNTAX->ecomm.string, M4SYNTAX->ecomm.length);
+      xfprintf (file, "C%zu,%zu\n", pair->len1, pair->len2);
+      produce_mem_dump (file, pair->str1, pair->len1);
+      produce_mem_dump (file, pair->str2, pair->len2);
       fputc ('\n', file);
     }
 
diff --git a/tests/freeze.at b/tests/freeze.at
index 530553a..2a84843 100644
--- a/tests/freeze.at
+++ b/tests/freeze.at
@@ -1,5 +1,5 @@
 # Hand crafted tests for GNU M4.                               -*- Autotest -*-
-# Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+# Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
 
 # This file is part of GNU M4.
 #
@@ -360,7 +360,7 @@ a
 [[m4:input.m4:4: Warning: a: builtin `b' requested by frozen file not found
 m4:input.m4:6: Warning: a: builtin `b' requested by frozen file not found
 m4:input.m4:8: Warning: a: builtin `b' requested by frozen file not found
-a:     <placeholder for b>
+a:     <<b>>
 c:     `'
 ]])
 
-- 
1.5.3.8


>From 88382ff9ef2efddf6279fb8af908ddd07210e70c Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Sat, 26 Jan 2008 00:08:48 -0700
Subject: [PATCH] Stage 12c: add macro for m4_arg_len.

* m4/m4module.h (M4ARGLEN): New macro.
* m4/macro.c (process_macro): Adjust all callers.
* m4/utility.c (m4_dump_args): Likewise.
* modules/m4.c (divert, maketemp, mkstemp, m4wrap, len, index)
(substr): Likewise.
* modules/gnu.c (builtin, indir, mkdtemp, patsubst, regexp)
(renamesyms): Likewise.
* modules/stdlib.c (setenv): Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog        |   71 ++++++++++++++++++++++++++++++++----------------------
 m4/m4module.h    |    5 ++++
 m4/macro.c       |    4 +-
 m4/utility.c     |    5 +--
 modules/gnu.c    |   24 ++++++++----------
 modules/m4.c     |   23 +++++++----------
 modules/stdlib.c |    9 ++++---
 7 files changed, 77 insertions(+), 64 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 67ce533..25c408e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,35 +1,48 @@
 2008-01-26  Eric Blake  <address@hidden>
 
-       Stage 12b: add m4_string_pair.
-       Make passing quote delimiters around more efficient.
+       Stage 12c: add macro for m4_arg_len.
+       Make a common action easier to type.
        Memory impact: none.
-       Speed impact: slight penalty, due to more bookkeeping.
-       * m4/m4module.h (m4_string_pair): New type.
-       (m4_get_syntax_quotes, m4_get_syntax_comments): New prototypes.
-       (m4_symbol_value_print, m4_symbol_print, m4_shipout_string_trunc):
-       Alter signature.
-       * m4/m4private.h (struct m4_string): Delete.
-       (struct m4_syntax_table): Combine quote and comment members.
-       (m4_get_syntax_lquote, m4_get_syntax_rquote, m4_get_syntax_bcomm)
-       (m4_get_syntax_ecomm): Adjust accessors.
-       (m4_get_syntax_quotes, m4_get_syntax_comments): New fast
-       accessors.
-       * m4/symtab.c (m4_symbol_value_print, m4_symbol_print):
-       Alter signatures.
-       * m4/input.c (string_print, composite_print, m4_input_print):
-       All callers updated.
-       * m4/syntax.c (m4_syntax_delete, m4_set_syntax)
-       (check_is_single_quotes, m4_set_quotes, set_quote_age)
-       (m4_get_syntax_lquote, m4_get_syntax_rquote)
-       (m4_get_syntax_quotes, check_is_single_comments, m4_set_comment)
-       (m4_get_syntax_bcomm, m4_get_syntax_ecomm)
-       (m4_get_syntax_comments): Likewise.
-       * m4/macro.c (trace_prepre, trace_pre, m4_push_args): Likewise.
-       * m4/output.c (m4_shipout_string, m4_shipout_string_trunc):
-       Likewise.
-       * modules/m4.c (dumpdef, m4_make_temp): Likewise.
-       * src/freeze.c (produce_frozen_state): Likewise.
-       * tests/freeze.at (reloading unknown builtin): Update test.
+       Speed impact: none.
+       * m4/m4module.h (M4ARGLEN): New macro.
+       * m4/macro.c (process_macro): Adjust all callers.
+       * m4/utility.c (m4_dump_args): Likewise.
+       * modules/m4.c (divert, maketemp, mkstemp, m4wrap, len, index)
+       (substr): Likewise.
+       * modules/gnu.c (builtin, indir, mkdtemp, patsubst, regexp)
+       (renamesyms): Likewise.
+       * modules/stdlib.c (setenv): Likewise.
+
+        Stage 12b: add m4_string_pair.
+        Make passing quote delimiters around more efficient.
+        Memory impact: none.
+        Speed impact: slight penalty, due to more bookkeeping.
+        * m4/m4module.h (m4_string_pair): New type.
+        (m4_get_syntax_quotes, m4_get_syntax_comments): New prototypes.
+        (m4_symbol_value_print, m4_symbol_print, m4_shipout_string_trunc):
+        Alter signature.
+        * m4/m4private.h (struct m4_string): Delete.
+        (struct m4_syntax_table): Combine quote and comment members.
+        (m4_get_syntax_lquote, m4_get_syntax_rquote, m4_get_syntax_bcomm)
+        (m4_get_syntax_ecomm): Adjust accessors.
+        (m4_get_syntax_quotes, m4_get_syntax_comments): New fast
+        accessors.
+        * m4/symtab.c (m4_symbol_value_print, m4_symbol_print):
+        Alter signatures.
+        * m4/input.c (string_print, composite_print, m4_input_print):
+        All callers updated.
+        * m4/syntax.c (m4_syntax_delete, m4_set_syntax)
+        (check_is_single_quotes, m4_set_quotes, set_quote_age)
+        (m4_get_syntax_lquote, m4_get_syntax_rquote)
+        (m4_get_syntax_quotes, check_is_single_comments, m4_set_comment)
+        (m4_get_syntax_bcomm, m4_get_syntax_ecomm)
+        (m4_get_syntax_comments): Likewise.
+        * m4/macro.c (trace_prepre, trace_pre, m4_push_args): Likewise.
+        * m4/output.c (m4_shipout_string, m4_shipout_string_trunc):
+        Likewise.
+        * modules/m4.c (dumpdef, m4_make_temp): Likewise.
+        * src/freeze.c (produce_frozen_state): Likewise.
+        * tests/freeze.at (reloading unknown builtin): Update test.
 
         Stage 12a: make m4_symbol_chain a union.
         Shrink size of symbol chains by using a union.
diff --git a/m4/m4module.h b/m4/m4module.h
index eac4c29..24d6a45 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -116,6 +116,11 @@ struct m4_string_pair
    in scope.  */
 #define M4ARG(i) m4_arg_text (context, argv, i)
 
+/* Grab the length of the text contents of argument I, or abort if the
+   argument is not text.  Assumes that `m4_macro_args *argv' is in
+   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_numeric_arg     (m4 *, const char *, const char *, int *);
diff --git a/m4/macro.c b/m4/macro.c
index 4753984..88ee391 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -763,8 +763,8 @@ process_macro (m4 *context, m4_symbol_value *value, 
m4_obstack *obs,
                    {
                      i = SYMBOL_ARG_INDEX (*arg);
                      assert (i < argc);
-                     m4_shipout_string (context, obs, M4ARG (i),
-                                        m4_arg_len (argv, i), false);
+                     m4_shipout_string (context, obs, M4ARG (i), M4ARGLEN (i),
+                                        false);
                    }
                }
              else
diff --git a/m4/utility.c b/m4/utility.c
index b367717..d95e44e 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1998, 1999, 2003,
-   2006, 2007 Free Software Foundation, Inc.
+   2006, 2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -114,8 +114,7 @@ m4_dump_args (m4 *context, m4_obstack *obs, unsigned int 
start,
       else
        need_sep = true;
 
-      m4_shipout_string (context, obs, M4ARG (i), m4_arg_len (argv, i),
-                         quoted);
+      m4_shipout_string (context, obs, M4ARG (i), M4ARGLEN (i), quoted);
     }
 }
 
diff --git a/modules/gnu.c b/modules/gnu.c
index 91cfb54..841e660 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -1,5 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 2000, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2000, 2004, 2005, 2006, 2007, 2008 Free Software
+   Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -446,8 +447,7 @@ M4BUILTIN_HANDLER (builtin)
            {
              m4_macro_args *new_argv;
              bool flatten = (bp->flags & M4_BUILTIN_GROKS_MACRO) == 0;
-             new_argv = m4_make_argv_ref (context, argv, name,
-                                          m4_arg_len (argv, 1),
+             new_argv = m4_make_argv_ref (context, argv, name, M4ARGLEN (1),
                                           true, flatten);
              bp->func (context, obs, argc - 1, new_argv);
            }
@@ -682,8 +682,8 @@ M4BUILTIN_HANDLER (indir)
        {
          m4_macro_args *new_argv;
          bool flatten = !m4_symbol_groks_macro (symbol);
-         new_argv = m4_make_argv_ref (context, argv, name,
-                                      m4_arg_len (argv, 1), true, flatten);
+         new_argv = m4_make_argv_ref (context, argv, name, M4ARGLEN (1),
+                                      true, flatten);
          m4_macro_call (context, m4_get_symbol_value (symbol), obs,
                         argc - 1, new_argv);
        }
@@ -701,8 +701,7 @@ M4BUILTIN_HANDLER (mkdtemp)
   M4_MODULE_IMPORT (m4, m4_make_temp);
 
   if (m4_make_temp)
-    m4_make_temp (context, obs, M4ARG (0), M4ARG (1), m4_arg_len (argv, 1),
-                 true);
+    m4_make_temp (context, obs, M4ARG (0), M4ARG (1), M4ARGLEN (1), true);
   else
     assert (!"Unable to import from m4 module");
 }
@@ -746,11 +745,11 @@ M4BUILTIN_HANDLER (patsubst)
       return;
     }
 
-  buf = regexp_compile (context, me, pattern, m4_arg_len (argv, 2), resyntax);
+  buf = regexp_compile (context, me, pattern, M4ARGLEN (2), resyntax);
   if (!buf)
     return;
 
-  regexp_substitute (context, obs, me, M4ARG (1), m4_arg_len (argv, 1),
+  regexp_substitute (context, obs, me, M4ARG (1), M4ARGLEN (1),
                     pattern, buf, replace, false);
 }
 
@@ -817,12 +816,12 @@ M4BUILTIN_HANDLER (regexp)
       return;
     }
 
-  buf = regexp_compile (context, me, pattern, m4_arg_len (argv, 2), resyntax);
+  buf = regexp_compile (context, me, pattern, M4ARGLEN (2), resyntax);
   if (!buf)
     return;
 
   victim = M4ARG (1);
-  len = m4_arg_len (argv, 1);
+  len = M4ARGLEN (1);
   startpos = regexp_search (buf, victim, len, 0, len, replace == NULL);
 
   if (startpos == -2)
@@ -873,8 +872,7 @@ M4BUILTIN_HANDLER (renamesyms)
            return;
        }
 
-      buf = regexp_compile (context, me, regexp, m4_arg_len (argv, 1),
-                           resyntax);
+      buf = regexp_compile (context, me, regexp, M4ARGLEN (1), resyntax);
       if (!buf)
        return;
 
diff --git a/modules/m4.c b/modules/m4.c
index 42dd661..cd4c230 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -560,7 +560,7 @@ M4BUILTIN_HANDLER (divert)
   if (argc >= 2 && !m4_numeric_arg (context, M4ARG (0), M4ARG (1), &i))
     return;
   m4_make_diversion (context, i);
-  m4_divert_text (context, NULL, M4ARG (2), m4_arg_len (argv, 2),
+  m4_divert_text (context, NULL, M4ARG (2), M4ARGLEN (2),
                  m4_get_current_line (context));
 }
 
@@ -758,9 +758,9 @@ M4BUILTIN_HANDLER (maketemp)
           maketemp(XXXXXXXX) -> `X00nnnnn', where nnnnn is 16-bit pid
       */
       const char *str = M4ARG (1);
-      size_t len = m4_arg_len (argv, 1);
-      int i;
-      int len2;
+      size_t len = M4ARGLEN (1);
+      size_t i;
+      size_t len2;
 
       for (i = len; i > 1; i--)
        if (str[i - 1] != 'X')
@@ -778,15 +778,13 @@ M4BUILTIN_HANDLER (maketemp)
        }
     }
   else
-    m4_make_temp (context, obs, M4ARG (0), M4ARG (1), m4_arg_len (argv, 1),
-                 false);
+    m4_make_temp (context, obs, M4ARG (0), M4ARG (1), M4ARGLEN (1), false);
 }
 
 /* Use the first argument as a template for a temporary file name.  */
 M4BUILTIN_HANDLER (mkstemp)
 {
-  m4_make_temp (context, obs, M4ARG (0), M4ARG (1), m4_arg_len (argv, 1),
-               false);
+  m4_make_temp (context, obs, M4ARG (0), M4ARG (1), M4ARGLEN (1), false);
 }
 
 /* Print all arguments on standard error.  */
@@ -850,7 +848,7 @@ M4BUILTIN_HANDLER (m4wrap)
 {
   assert (obstack_object_size (obs) == 0);
   if (m4_get_posixly_correct_opt (context))
-    m4_shipout_string (context, obs, M4ARG (1), m4_arg_len (argv, 1), false);
+    m4_shipout_string (context, obs, M4ARG (1), M4ARGLEN (1), false);
   else
     m4_dump_args (context, obs, 1, argv, " ", false);
   obstack_1grow (obs, '\0');
@@ -894,7 +892,7 @@ M4BUILTIN_HANDLER (traceoff)
 /* Expand to the length of the first argument.  */
 M4BUILTIN_HANDLER (len)
 {
-  m4_shipout_int (obs, m4_arg_len (argv, 1));
+  m4_shipout_int (obs, M4ARGLEN (1));
 }
 
 /* The macro expands to the first index of the second argument in the first
@@ -908,8 +906,7 @@ M4BUILTIN_HANDLER (index)
 
   /* Rely on the optimizations guaranteed by gnulib's memmem
      module.  */
-  result = (char *) memmem (haystack, m4_arg_len (argv, 1),
-                           needle, m4_arg_len (argv, 2));
+  result = (char *) memmem (haystack, M4ARGLEN (1), needle, M4ARGLEN (2));
   if (result)
     retval = result - haystack;
 
@@ -934,7 +931,7 @@ M4BUILTIN_HANDLER (substr)
       return;
     }
 
-  length = avail = m4_arg_len (argv, 1);
+  length = avail = M4ARGLEN (1);
   if (!m4_numeric_arg (context, me, M4ARG (2), &start))
     return;
 
diff --git a/modules/stdlib.c b/modules/stdlib.c
index 0063ed8..0849d3d 100644
--- a/modules/stdlib.c
+++ b/modules/stdlib.c
@@ -1,5 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1999, 2000, 2001, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2006, 2007, 2008 Free Software
+   Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -122,12 +123,12 @@ M4BUILTIN_HANDLER (setenv)
     return;
 
   assert (obstack_object_size (obs) == 0);
-  obstack_grow (obs, M4ARG (1), m4_arg_len (argv, 1));
+  obstack_grow (obs, M4ARG (1), M4ARGLEN (1));
   obstack_1grow (obs, '=');
-  obstack_grow0 (obs, M4ARG (2), m4_arg_len (argv, 2));
+  obstack_grow0 (obs, M4ARG (2), M4ARGLEN (2));
 
   {
-    char *env = obstack_finish (obs);
+    char *env = (char *) obstack_finish (obs);
     putenv (env);
   }
 #endif /* HAVE_PUTENV */
-- 
1.5.3.8

>From 6ef07af4d24ef59c2a6939ef9b7265cb4d01e19c Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Sat, 27 Oct 2007 08:03:03 -0600
Subject: [PATCH] Stage 12: make token_chain a union, add string_pair.

* src/m4.h (STRING): Delete typedef.
(struct string_pair, enum token_chain_type): New types.
(struct token_chain): Reduce size via a union.
(ARG_LEN): New macro.
(ARG): Move here...
* src/builtin.c (ARG): ...from here.
(dump_args, define_macro, m4_dumpdef, m4_builtin, m4_indir)
(m4_defn, mkstemp_helper, m4_maketemp, m4_mkstemp, m4___file__)
(m4___program__, m4_m4wrap, m4_len, m4_index, m4_substr)
(m4_regexp, m4_patsubst): Adjust callers.
* src/input.c (rquote, lquote, bcomm, ecomm): Delete...
(curr_quote, curr_comm): ...replaced by these.
(make_text_link, push_token, pop_input, input_print, peek_input)
(next_char_1, input_init, set_quotes, set_comment, set_quote_age)
(next_token, peek_token): Adjust callers.
* src/macro.c (expand_macro, arg_token, arg_mark, arg_text)
(arg_equal, arg_len, make_argv_ref, push_arg, push_args):
Likewise.
* src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE, format):
Likewise.
* src/freeze.c (produce_frozen_state): Likewise.
* src/debug.c (trace_format, trace_pre): Likewise.
(debug_decode): Don't lose partial traces prior to reducing
debugmode.

(cherry picked from commit d8324ac481f69682f6953ba3fb0c60cf67c7e8d7)

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog     |   32 ++++++++++
 src/builtin.c |   62 +++++++++----------
 src/debug.c   |   39 ++++++-------
 src/format.c  |   10 ++--
 src/freeze.c  |   23 +++-----
 src/input.c   |  185 ++++++++++++++++++++++++++++++--------------------------
 src/m4.h      |   60 +++++++++++++++----
 src/macro.c   |  103 +++++++++++++++++---------------
 8 files changed, 292 insertions(+), 222 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 15549a6..7213833 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2008-01-26  Eric Blake  <address@hidden>
+
+       Stage 12: make token_chain a union, add string_pair.
+       Shrink size of symbol chains by using a union.  Make passing quote
+       delimiters around more efficient.  Other code cleanups.
+       Memory impact: slight improvement, due to smaller struct.
+       Speed impact: slight penalty, due to more bookkeeping.
+       * src/m4.h (STRING): Delete typedef.
+       (struct string_pair, enum token_chain_type): New types.
+       (struct token_chain): Reduce size via a union.
+       (ARG_LEN): New macro.
+       (ARG): Move here...
+       * src/builtin.c (ARG): ...from here.
+       (dump_args, define_macro, m4_dumpdef, m4_builtin, m4_indir)
+       (m4_defn, mkstemp_helper, m4_maketemp, m4_mkstemp, m4___file__)
+       (m4___program__, m4_m4wrap, m4_len, m4_index, m4_substr)
+       (m4_regexp, m4_patsubst): Adjust callers.
+       * src/input.c (rquote, lquote, bcomm, ecomm): Delete...
+       (curr_quote, curr_comm): ...replaced by these.
+       (make_text_link, push_token, pop_input, input_print, peek_input)
+       (next_char_1, input_init, set_quotes, set_comment, set_quote_age)
+       (next_token, peek_token): Adjust callers.
+       * src/macro.c (expand_macro, arg_token, arg_mark, arg_text)
+       (arg_equal, arg_len, make_argv_ref, push_arg, push_args):
+       Likewise.
+       * src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE, format):
+       Likewise.
+       * src/freeze.c (produce_frozen_state): Likewise.
+       * src/debug.c (trace_format, trace_pre): Likewise.
+       (debug_decode): Don't lose partial traces prior to reducing
+       debugmode.
+
 2008-01-22  Eric Blake  <address@hidden>
 
        Stage 11: full circle for single argument references.
diff --git a/src/builtin.c b/src/builtin.c
index 007ca55..c89ad44 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -30,10 +30,6 @@
 # include <sys/wait.h>
 #endif
 
-/* Grab the text at argv index I.  Assumes a macro_argument *argv is
-   in scope.  */
-#define ARG(i) arg_text (argv, i)
-
 /* Initialization of builtin and predefined macros.  The table
    "builtin_tab" is both used for initialization, and by the "builtin"
    builtin.  */
@@ -623,10 +619,10 @@ dump_args (struct obstack *obs, int start, 
macro_arguments *argv,
       else
        dump_sep = true;
       if (quoted)
-       obstack_grow (obs, lquote.string, lquote.length);
-      obstack_grow (obs, ARG (i), arg_len (argv, i));
+       obstack_grow (obs, curr_quote.str1, curr_quote.len1);
+      obstack_grow (obs, ARG (i), ARG_LEN (i));
       if (quoted)
-       obstack_grow (obs, rquote.string, rquote.length);
+       obstack_grow (obs, curr_quote.str2, curr_quote.len2);
     }
 }
 
@@ -668,14 +664,14 @@ define_macro (int argc, macro_arguments *argv, 
symbol_lookup mode)
 
   if (argc == 2)
     {
-      define_user_macro (ARG (1), arg_len (argv, 1), "", mode);
+      define_user_macro (ARG (1), ARG_LEN (1), "", mode);
       return;
     }
 
   switch (arg_type (argv, 2))
     {
     case TOKEN_TEXT:
-      define_user_macro (ARG (1), arg_len (argv, 1), ARG (2), mode);
+      define_user_macro (ARG (1), ARG_LEN (1), ARG (2), mode);
       break;
 
     case TOKEN_FUNC:
@@ -865,7 +861,8 @@ m4_dumpdef (struct obstack *obs, int argc, macro_arguments 
*argv)
        case TOKEN_TEXT:
          if (debug_level & DEBUG_TRACE_QUOTE)
            DEBUG_PRINT3 ("%s%s%s\n",
-                         lquote.string, SYMBOL_TEXT (data.base[0]), 
rquote.string);
+                         curr_quote.str1, SYMBOL_TEXT (data.base[0]),
+                         curr_quote.str2);
          else
            DEBUG_PRINT1 ("%s\n", SYMBOL_TEXT (data.base[0]));
          break;
@@ -916,7 +913,7 @@ m4_builtin (struct obstack *obs, int argc, macro_arguments 
*argv)
     m4_warn (0, me, _("undefined builtin `%s'"), name);
   else
     {
-      macro_arguments *new_argv = make_argv_ref (argv, name, arg_len (argv, 1),
+      macro_arguments *new_argv = make_argv_ref (argv, name, ARG_LEN (1),
                                                 true, !bp->groks_macro_args);
       bp->func (obs, argc - 1, new_argv);
     }
@@ -950,7 +947,7 @@ m4_indir (struct obstack *obs, int argc, macro_arguments 
*argv)
     m4_warn (0, me, _("undefined macro `%s'"), name);
   else
     {
-      macro_arguments *new_argv = make_argv_ref (argv, name, arg_len (argv, 1),
+      macro_arguments *new_argv = make_argv_ref (argv, name, ARG_LEN (1),
                                                 true, !SYMBOL_MACRO_ARGS (s));
       call_macro (s, argc - 1, new_argv, obs);
     }
@@ -982,9 +979,9 @@ m4_defn (struct obstack *obs, int argc, macro_arguments 
*argv)
       switch (SYMBOL_TYPE (s))
        {
        case TOKEN_TEXT:
-         obstack_grow (obs, lquote.string, lquote.length);
+         obstack_grow (obs, curr_quote.str1, curr_quote.len1);
          obstack_grow (obs, SYMBOL_TEXT (s), strlen (SYMBOL_TEXT (s)));
-         obstack_grow (obs, rquote.string, rquote.length);
+         obstack_grow (obs, curr_quote.str2, curr_quote.len2);
          break;
 
        case TOKEN_FUNC:
@@ -1429,13 +1426,13 @@ mkstemp_helper (struct obstack *obs, const char *me, 
const char *pattern,
   /* Guarantee that there are six trailing 'X' characters, even if the
      user forgot to supply them.  Output must be quoted if
      successful.  */
-  obstack_grow (obs, lquote.string, lquote.length);
+  obstack_grow (obs, curr_quote.str1, curr_quote.len1);
   obstack_grow (obs, pattern, len);
   for (i = 0; len > 0 && i < 6; i++)
     if (pattern[--len] != 'X')
       break;
   obstack_grow0 (obs, "XXXXXX", 6 - i);
-  name = (char *) obstack_base (obs) + lquote.length;
+  name = (char *) obstack_base (obs) + curr_quote.len1;
 
   errno = 0;
   fd = mkstemp (name);
@@ -1449,7 +1446,7 @@ mkstemp_helper (struct obstack *obs, const char *me, 
const char *pattern,
       close (fd);
       /* Remove NUL, then finish quote.  */
       obstack_blank (obs, -1);
-      obstack_grow (obs, rquote.string, rquote.length);
+      obstack_grow (obs, curr_quote.str2, curr_quote.len2);
     }
 }
 
@@ -1474,7 +1471,7 @@ m4_maketemp (struct obstack *obs, int argc, 
macro_arguments *argv)
           maketemp(XXXXXXXX) -> `X00nnnnn', where nnnnn is 16-bit pid
       */
       const char *str = ARG (1);
-      size_t len = arg_len (argv, 1);
+      size_t len = ARG_LEN (1);
       size_t i;
       size_t len2;
 
@@ -1495,7 +1492,7 @@ m4_maketemp (struct obstack *obs, int argc, 
macro_arguments *argv)
        }
     }
   else
-    mkstemp_helper (obs, me, ARG (1), arg_len (argv, 1));
+    mkstemp_helper (obs, me, ARG (1), ARG_LEN (1));
 }
 
 static void
@@ -1505,7 +1502,7 @@ m4_mkstemp (struct obstack *obs, int argc, 
macro_arguments *argv)
 
   if (bad_argc (me, argc, 1, 1))
     return;
-  mkstemp_helper (obs, me, ARG (1), arg_len (argv, 1));
+  mkstemp_helper (obs, me, ARG (1), ARG_LEN (1));
 }
 
 /*----------------------------------------.
@@ -1532,9 +1529,9 @@ static void
 m4___file__ (struct obstack *obs, int argc, macro_arguments *argv)
 {
   bad_argc (ARG (0), argc, 0, 0);
-  obstack_grow (obs, lquote.string, lquote.length);
+  obstack_grow (obs, curr_quote.str1, curr_quote.len1);
   obstack_grow (obs, current_file, strlen (current_file));
-  obstack_grow (obs, rquote.string, rquote.length);
+  obstack_grow (obs, curr_quote.str2, curr_quote.len2);
 }
 
 static void
@@ -1548,9 +1545,9 @@ static void
 m4___program__ (struct obstack *obs, int argc, macro_arguments *argv)
 {
   bad_argc (ARG (0), argc, 0, 0);
-  obstack_grow (obs, lquote.string, lquote.length);
+  obstack_grow (obs, curr_quote.str1, curr_quote.len1);
   obstack_grow (obs, program_name, strlen (program_name));
-  obstack_grow (obs, rquote.string, rquote.length);
+  obstack_grow (obs, curr_quote.str2, curr_quote.len2);
 }
 
 /* This section contains various macros for exiting, saving input until
@@ -1601,7 +1598,7 @@ m4_m4wrap (struct obstack *obs, int argc, macro_arguments 
*argv)
   if (bad_argc (ARG (0), argc, 1, -1))
     return;
   if (no_gnu_extensions)
-    obstack_grow (obs, ARG (1), arg_len (argv, 1));
+    obstack_grow (obs, ARG (1), ARG_LEN (1));
   else
     dump_args (obs, 1, argv, " ", false);
   obstack_1grow (obs, '\0');
@@ -1748,7 +1745,7 @@ m4_len (struct obstack *obs, int argc, macro_arguments 
*argv)
 {
   if (bad_argc (ARG (0), argc, 1, 1))
     return;
-  shipout_int (obs, arg_len (argv, 1));
+  shipout_int (obs, ARG_LEN (1));
 }
 
 /*-------------------------------------------------------------------------.
@@ -1777,8 +1774,7 @@ m4_index (struct obstack *obs, int argc, macro_arguments 
*argv)
 
   /* Rely on the optimizations guaranteed by gnulib's memmem
      module.  */
-  result = (char *) memmem (haystack, arg_len (argv, 1),
-                           needle, arg_len (argv, 2));
+  result = (char *) memmem (haystack, ARG_LEN (1), needle, ARG_LEN (2));
   if (result)
     retval = result - haystack;
 
@@ -1808,7 +1804,7 @@ m4_substr (struct obstack *obs, int argc, macro_arguments 
*argv)
       return;
     }
 
-  length = avail = arg_len (argv, 1);
+  length = avail = ARG_LEN (1);
   if (!numeric_arg (me, ARG (2), &start))
     return;
 
@@ -2074,14 +2070,14 @@ m4_regexp (struct obstack *obs, int argc, 
macro_arguments *argv)
              argc == 3 ? "" : "{", repl, argc == 3 ? "" : "}");
 #endif /* DEBUG_REGEX */
 
-  msg = compile_pattern (regexp, arg_len (argv, 2), &buf, &regs);
+  msg = compile_pattern (regexp, ARG_LEN (2), &buf, &regs);
   if (msg != NULL)
     {
       m4_warn (0, me, _("bad regular expression: `%s': %s"), regexp, msg);
       return;
     }
 
-  length = arg_len (argv, 1);
+  length = ARG_LEN (1);
   /* Avoid overhead of allocating regs if we won't use it.  */
   startpos = re_search (buf, victim, length, 0, length,
                        argc == 3 ? NULL : regs);
@@ -2142,14 +2138,14 @@ m4_patsubst (struct obstack *obs, int argc, 
macro_arguments *argv)
     xfprintf (trace_file, "p:{%s}:{%s}\n", regexp, repl);
 #endif /* DEBUG_REGEX */
 
-  msg = compile_pattern (regexp, arg_len (argv, 2), &buf, &regs);
+  msg = compile_pattern (regexp, ARG_LEN (2), &buf, &regs);
   if (msg != NULL)
     {
       m4_warn (0, me, _("bad regular expression `%s': %s"), regexp, msg);
       return;
     }
 
-  length = arg_len (argv, 1);
+  length = ARG_LEN (1);
 
   offset = 0;
   matchpos = 0;
diff --git a/src/debug.c b/src/debug.c
index 2ca7a0d..d6b2ddc 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -1,7 +1,7 @@
 /* GNU m4 -- A simple macro processor
 
-   Copyright (C) 1991, 1992, 1993, 1994, 2004, 2006, 2007 Free Software
-   Foundation, Inc.
+   Copyright (C) 1991, 1992, 1993, 1994, 2004, 2006, 2007, 2008 Free
+   Software Foundation, Inc.
 
    This file is part of GNU M4.
 
@@ -110,12 +110,6 @@ debug_decode (const char *opts)
            }
        }
     }
-
-  /* This is to avoid screwing up the trace output due to changes in the
-     debug_level.  */
-
-  obstack_free (&trace, obstack_finish (&trace));
-
   return level;
 }
 
@@ -283,11 +277,11 @@ trace_format (const char *fmt, ...)
          break;
 
        case 'l':
-         s = (debug_level & DEBUG_TRACE_QUOTE) ? lquote.string : "";
+         s = (debug_level & DEBUG_TRACE_QUOTE) ? curr_quote.str1 : "";
          break;
 
        case 'r':
-         s = (debug_level & DEBUG_TRACE_QUOTE) ? rquote.string : "";
+         s = (debug_level & DEBUG_TRACE_QUOTE) ? curr_quote.str2 : "";
          break;
 
        case 'd':
@@ -309,6 +303,7 @@ trace_format (const char *fmt, ...)
 
 /*------------------------------------------------------------------.
 | Format the standard header attached to all tracing output lines.  |
+| ID is the current macro id.                                       |
 `------------------------------------------------------------------*/
 
 static void
@@ -342,10 +337,10 @@ trace_flush (void)
   obstack_free (&trace, line);
 }
 
-/*-------------------------------------------------------------.
-| Do pre-argument-collction tracing for macro NAME.  Used from |
-| expand_macro ().                                            |
-`-------------------------------------------------------------*/
+/*----------------------------------------------------------------.
+| Do pre-argument-collection tracing for macro NAME, with a given |
+| ID.  Used from expand_macro ().                                 |
+`----------------------------------------------------------------*/
 
 void
 trace_prepre (const char *name, int id)
@@ -355,10 +350,11 @@ trace_prepre (const char *name, int id)
   trace_flush ();
 }
 
-/*-----------------------------------------------------------------------.
-| Format the parts of a trace line, that can be made before the macro is |
-| actually expanded.  Used from expand_macro ().                        |
-`-----------------------------------------------------------------------*/
+/*-----------------------------------------------------------------.
+| Format the parts of a trace line that are known before the macro |
+| is actually expanded.  Called for the macro NAME with ID, and    |
+| arguments ARGV.  Used from expand_macro ().                      |
+`-----------------------------------------------------------------*/
 
 void
 trace_pre (const char *name, int id, macro_arguments *argv)
@@ -382,7 +378,7 @@ trace_pre (const char *name, int id, macro_arguments *argv)
          switch (arg_type (argv, i))
            {
            case TOKEN_TEXT:
-             trace_format ("%l%S%r", arg_text (argv, i));
+             trace_format ("%l%S%r", ARG (i));
              break;
 
            case TOKEN_FUNC:
@@ -412,8 +408,9 @@ trace_pre (const char *name, int id, macro_arguments *argv)
 }
 
 /*-------------------------------------------------------------------.
-| Format the final part of a trace line and print it all.  Used from |
-| expand_macro ().                                                  |
+| Format the final part of a trace line and print it all.  Print     |
+| details for macro NAME with ID, given arguemnts ARGV and expansion |
+| EXPANDED.  Used from expand_macro ().                              |
 `-------------------------------------------------------------------*/
 
 void
diff --git a/src/format.c b/src/format.c
index 9c9508d..6808ad5 100644
--- a/src/format.c
+++ b/src/format.c
@@ -101,16 +101,16 @@ arg_double (const char *me, const char *str)
 }
 
 #define ARG_INT(i, argc, argv)                                         \
-  ((argc <= ++i) ? 0 : arg_int (me, arg_text (argv, i)))
+  ((argc <= ++i) ? 0 : arg_int (me, ARG (i)))
 
 #define ARG_LONG(i, argc, argv)                                                
\
-  ((argc <= ++i) ? 0L : arg_long (me, arg_text (argv, i)))
+  ((argc <= ++i) ? 0L : arg_long (me, ARG (i)))
 
 #define ARG_STR(i, argc, argv)                                         \
-  ((argc <= ++i) ? "" : arg_text (argv, i))
+  ((argc <= ++i) ? "" : ARG (i))
 
 #define ARG_DOUBLE(i, argc, argv)                                      \
-  ((argc <= ++i) ? 0.0 : arg_double (me, arg_text (argv, i)))
+  ((argc <= ++i) ? 0.0 : arg_double (me, ARG (i)))
 
 
 /*------------------------------------------------------------------.
@@ -124,7 +124,7 @@ arg_double (const char *me, const char *str)
 void
 format (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = arg_text (argv, 0);
+  const char *me = ARG (0);            /* Macro name.  */
   const char *f;                       /* Format control string.  */
   const char *fmt;                     /* Position within f.  */
   char fstart[] = "%'+- 0#*.*hhd";     /* Current format spec.  */
diff --git a/src/freeze.c b/src/freeze.c
index 52a69d1..383d008 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
 
-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006, 2007
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006, 2007, 2008
    Free Software Foundation, Inc.
 
    This file is part of GNU M4.
@@ -71,23 +71,16 @@ produce_frozen_state (const char *name)
 
   /* Dump quote delimiters.  */
 
-  if (strcmp (lquote.string, DEF_LQUOTE) || strcmp (rquote.string, DEF_RQUOTE))
-    {
-      xfprintf (file, "Q%d,%d\n", (int) lquote.length, (int) rquote.length);
-      fputs (lquote.string, file);
-      fputs (rquote.string, file);
-      fputc ('\n', file);
-    }
+  if (strcmp (curr_quote.str1, DEF_LQUOTE)
+      || strcmp (curr_quote.str2, DEF_RQUOTE))
+    xfprintf (file, "Q%d,%d\n%s%s\n", (int) curr_quote.len1,
+             (int) curr_quote.len2, curr_quote.str1, curr_quote.str2);
 
   /* Dump comment delimiters.  */
 
-  if (strcmp (bcomm.string, DEF_BCOMM) || strcmp (ecomm.string, DEF_ECOMM))
-    {
-      xfprintf (file, "C%d,%d\n", (int) bcomm.length, (int) ecomm.length);
-      fputs (bcomm.string, file);
-      fputs (ecomm.string, file);
-      fputc ('\n', file);
-    }
+  if (strcmp (curr_comm.str1, DEF_BCOMM) || strcmp (curr_comm.str2, DEF_ECOMM))
+    xfprintf (file, "C%d,%d\n%s%s\n", (int) curr_comm.len1,
+             (int) curr_comm.len2, curr_comm.str1, curr_comm.str2);
 
   /* Dump all symbols.  */
 
diff --git a/src/input.c b/src/input.c
index 9f25e8f..5890bd2 100644
--- a/src/input.c
+++ b/src/input.c
@@ -156,12 +156,10 @@ static bool input_change;
 #define CHAR_QUOTE     258     /* Character return for quoted string.  */
 
 /* Quote chars.  */
-STRING rquote;
-STRING lquote;
+string_pair curr_quote;
 
 /* Comment chars.  */
-STRING bcomm;
-STRING ecomm;
+string_pair curr_comm;
 
 #ifdef ENABLE_CHANGEWORD
 
@@ -219,13 +217,11 @@ make_text_link (struct obstack *obs, token_chain **start, 
token_chain **end)
        *start = chain;
       *end = chain;
       chain->next = NULL;
+      chain->type = CHAIN_STR;
       chain->quote_age = 0;
-      chain->str = str;
-      chain->len = len;
-      chain->level = -1;
-      chain->argv = NULL;
-      chain->index = 0;
-      chain->flatten = false;
+      chain->u.u_s.str = str;
+      chain->u.u_s.len = len;
+      chain->u.u_s.level = -1;
     }
 }
 
@@ -363,13 +359,11 @@ push_token (token_data *token, int level)
     next->u.u_c.chain = chain;
   next->u.u_c.end = chain;
   chain->next = NULL;
+  chain->type = CHAIN_STR;
   chain->quote_age = TOKEN_DATA_QUOTE_AGE (token);
-  chain->str = TOKEN_DATA_TEXT (token);
-  chain->len = TOKEN_DATA_LEN (token);
-  chain->level = level;
-  chain->argv = NULL;
-  chain->index = 0;
-  chain->flatten = false;
+  chain->u.u_s.str = TOKEN_DATA_TEXT (token);
+  chain->u.u_s.len = TOKEN_DATA_LEN (token);
+  chain->u.u_s.level = level;
   if (level >= 0)
     {
       adjust_refcount (level, true);
@@ -478,19 +472,20 @@ pop_input (bool cleanup)
       assert (!chain || !cleanup);
       while (chain)
        {
-         if (chain->str)
+         switch (chain->type)
            {
-             if (chain->len)
+           case CHAIN_STR:
+             if (chain->u.u_s.len)
                return false;
-           }
-         else
-           {
+             if (chain->u.u_s.level >= 0)
+               adjust_refcount (chain->u.u_s.level, false);
+             break;
+           case CHAIN_ARGV:
              /* TODO - peek into argv.  */
-             assert (!"implemented yet");
+           default:
+             assert (!"pop_input");
              abort ();
            }
-         if (chain->level >= 0)
-           adjust_refcount (chain->level, false);
          isp->u.u_c.chain = chain = chain->next;
        }
       break;
@@ -601,8 +596,8 @@ input_print (struct obstack *obs, const input_block *input)
       while (chain)
        {
          /* TODO support argv refs as well.  */
-         assert (chain->str);
-         if (obstack_print (obs, chain->str, chain->len, &maxlen))
+         assert (chain->type == CHAIN_STR);
+         if (obstack_print (obs, chain->u.u_s.str, chain->u.u_s.len, &maxlen))
            return;
          chain = chain->next;
        }
@@ -659,15 +654,16 @@ peek_input (void)
          chain = block->u.u_c.chain;
          while (chain)
            {
-             if (chain->str)
-               {
-                 if (chain->len)
-                   return to_uchar (chain->str[0]);
-               }
-             else
+             switch (chain->type)
                {
+               case CHAIN_STR:
+                 if (chain->u.u_s.len)
+                   return to_uchar (*chain->u.u_s.str);
+                 break;
+               case CHAIN_ARGV:
                  /* TODO - peek into argv.  */
-                 assert (!"implemented yet");
+               default:
+                 assert (!"peek_input");
                  abort ();
                }
              chain = chain->next;
@@ -760,24 +756,25 @@ next_char_1 (bool allow_quote)
            {
              if (allow_quote && chain->quote_age == current_quote_age)
                return CHAR_QUOTE;
-             if (chain->str)
+             switch (chain->type)
                {
-                 if (chain->len)
+               case CHAIN_STR:
+                 if (chain->u.u_s.len)
                    {
                      /* Partial consumption invalidates quote age.  */
                      chain->quote_age = 0;
-                     chain->len--;
-                     return to_uchar (*chain->str++);
+                     chain->u.u_s.len--;
+                     return to_uchar (*chain->u.u_s.str++);
                    }
-               }
-             else
-               {
+                 if (chain->u.u_s.level >= 0)
+                   adjust_refcount (chain->u.u_s.level, false);
+                 break;
+               case CHAIN_ARGV:
                  /* TODO - read from argv.  */
-                 assert (!"implemented yet");
+               default:
+                 assert (!"next_char_1");
                  abort ();
                }
-             if (chain->level >= 0)
-               adjust_refcount (chain->level, false);
              isp->u.u_c.chain = chain = chain->next;
            }
          break;
@@ -958,14 +955,14 @@ input_init (void)
 
   start_of_input_line = false;
 
-  lquote.string = xstrdup (DEF_LQUOTE);
-  lquote.length = strlen (lquote.string);
-  rquote.string = xstrdup (DEF_RQUOTE);
-  rquote.length = strlen (rquote.string);
-  bcomm.string = xstrdup (DEF_BCOMM);
-  bcomm.length = strlen (bcomm.string);
-  ecomm.string = xstrdup (DEF_ECOMM);
-  ecomm.length = strlen (ecomm.string);
+  curr_quote.str1 = xstrdup (DEF_LQUOTE);
+  curr_quote.len1 = strlen (curr_quote.str1);
+  curr_quote.str2 = xstrdup (DEF_RQUOTE);
+  curr_quote.len2 = strlen (curr_quote.str2);
+  curr_comm.str1 = xstrdup (DEF_BCOMM);
+  curr_comm.len1 = strlen (curr_comm.str1);
+  curr_comm.str2 = xstrdup (DEF_ECOMM);
+  curr_comm.len2 = strlen (curr_comm.str2);
 
 #ifdef ENABLE_CHANGEWORD
   set_word_regexp (NULL, user_word_regexp);
@@ -999,15 +996,15 @@ set_quotes (const char *lq, const char *rq)
   else if (!rq || (*lq && !*rq))
     rq = DEF_RQUOTE;
 
-  if (strcmp (lquote.string, lq) == 0 && strcmp (rquote.string, rq) == 0)
+  if (strcmp (curr_quote.str1, lq) == 0 && strcmp (curr_quote.str2, rq) == 0)
     return;
 
-  free (lquote.string);
-  free (rquote.string);
-  lquote.string = xstrdup (lq);
-  lquote.length = strlen (lquote.string);
-  rquote.string = xstrdup (rq);
-  rquote.length = strlen (rquote.string);
+  free (curr_quote.str1);
+  free (curr_quote.str2);
+  curr_quote.str1 = xstrdup (lq);
+  curr_quote.len1 = strlen (curr_quote.str1);
+  curr_quote.str2 = xstrdup (rq);
+  curr_quote.len2 = strlen (curr_quote.str2);
   set_quote_age ();
 }
 
@@ -1032,15 +1029,15 @@ set_comment (const char *bc, const char *ec)
   else if (!ec || (*bc && !*ec))
     ec = DEF_ECOMM;
 
-  if (strcmp (bcomm.string, bc) == 0 && strcmp (ecomm.string, ec) == 0)
+  if (strcmp (curr_comm.str1, bc) == 0 && strcmp (curr_comm.str2, ec) == 0)
     return;
 
-  free (bcomm.string);
-  free (ecomm.string);
-  bcomm.string = xstrdup (bc);
-  bcomm.length = strlen (bcomm.string);
-  ecomm.string = xstrdup (ec);
-  ecomm.length = strlen (ecomm.string);
+  free (curr_comm.str1);
+  free (curr_comm.str2);
+  curr_comm.str1 = xstrdup (bc);
+  curr_comm.len1 = strlen (curr_comm.str1);
+  curr_comm.str2 = xstrdup (ec);
+  curr_comm.len2 = strlen (curr_comm.str2);
   set_quote_age ();
 }
 
@@ -1136,14 +1133,14 @@ set_quote_age (void)
   static const char unsafe[] = Letters "_0123456789(,) \t\n\r\f\v";
 #undef Letters
 
-  if (lquote.length == 1 && rquote.length == 1
-      && strpbrk(lquote.string, unsafe) == NULL
-      && strpbrk(rquote.string, unsafe) == NULL
-      && default_word_regexp && *lquote.string != *rquote.string
-      && *bcomm.string != '(' && *bcomm.string != ','
-      && *bcomm.string != ')' && *bcomm.string != *lquote.string)
-    current_quote_age = (((*lquote.string & 0xff) << 8)
-                        | (*rquote.string & 0xff));
+  if (curr_quote.len1 == 1 && curr_quote.len2 == 1
+      && strpbrk (curr_quote.str1, unsafe) == NULL
+      && strpbrk (curr_quote.str2, unsafe) == NULL
+      && default_word_regexp && *curr_quote.str1 != *curr_quote.str2
+      && *curr_comm.str1 != '(' && *curr_comm.str1 != ','
+      && *curr_comm.str1 != ')' && *curr_comm.str1 != *curr_quote.str1)
+    current_quote_age = (((*curr_quote.str1 & 0xff) << 8)
+                        | (*curr_quote.str2 & 0xff));
   else
     current_quote_age = 0;
 }
@@ -1239,18 +1236,18 @@ next_token (token_data *td, int *line, struct obstack 
*obs, const char *caller)
   next_char (false); /* Consume character we already peeked at.  */
   file = current_file;
   *line = current_line;
-  if (MATCH (ch, bcomm.string, true))
+  if (MATCH (ch, curr_comm.str1, true))
     {
       if (obs)
        obs_td = obs;
-      obstack_grow (obs_td, bcomm.string, bcomm.length);
+      obstack_grow (obs_td, curr_comm.str1, curr_comm.len1);
       while ((ch = next_char (false)) < CHAR_EOF
-            && !MATCH (ch, ecomm.string, true))
+            && !MATCH (ch, curr_comm.str2, true))
        obstack_1grow (obs_td, ch);
       if (ch != CHAR_EOF)
        {
          assert (ch < CHAR_EOF);
-         obstack_grow (obs_td, ecomm.string, ecomm.length);
+         obstack_grow (obs_td, curr_comm.str2, curr_comm.len2);
        }
       else
        /* Current_file changed to "" if we see CHAR_EOF, use the
@@ -1306,7 +1303,7 @@ next_token (token_data *td, int *line, struct obstack 
*obs, const char *caller)
 
 #endif /* ENABLE_CHANGEWORD */
 
-  else if (!MATCH (ch, lquote.string, true))
+  else if (!MATCH (ch, curr_quote.str1, true))
     {
       switch (ch)
        {
@@ -1341,16 +1338,16 @@ next_token (token_data *td, int *line, struct obstack 
*obs, const char *caller)
 
          if (ch == CHAR_QUOTE)
            append_quote_token (obs, td);
-         else if (MATCH (ch, rquote.string, true))
+         else if (MATCH (ch, curr_quote.str2, true))
            {
              if (--quote_level == 0)
                break;
-             obstack_grow (obs_td, rquote.string, rquote.length);
+             obstack_grow (obs_td, curr_quote.str2, curr_quote.len2);
            }
-         else if (MATCH (ch, lquote.string, true))
+         else if (MATCH (ch, curr_quote.str1, true))
            {
              quote_level++;
-             obstack_grow (obs_td, lquote.string, lquote.length);
+             obstack_grow (obs_td, curr_quote.str1, curr_quote.len1);
            }
          else
            {
@@ -1392,8 +1389,24 @@ next_token (token_data *td, int *line, struct obstack 
*obs, const char *caller)
     {
       assert (TOKEN_DATA_TYPE (td) == TOKEN_COMP && type == TOKEN_STRING);
 #ifdef DEBUG_INPUT
-      xfprintf (stderr, "next_token -> %s <chain>\n",
-               token_type_string (type));
+      {
+       token_chain *chain;
+       size_t len = 0;
+       int links = 0;
+       chain = td->u.u_c.chain;
+       xfprintf (stderr, "next_token -> %s <chain> (",
+                 token_type_string (type));
+       while (chain)
+         {
+           assert (chain->type == CHAIN_STR);
+           xfprintf (stderr, "%s", chain->u.u_s.str);
+           len += chain->u.u_s.len;
+           links++;
+           chain = chain->next;
+         }
+       xfprintf (stderr, "), %d links, len %zu\n",
+                 links, len);
+      }
 #endif /* DEBUG_INPUT */
     }
   return type;
@@ -1417,7 +1430,7 @@ peek_token (void)
     {
       result = TOKEN_MACDEF;
     }
-  else if (MATCH (ch, bcomm.string, false))
+  else if (MATCH (ch, curr_comm.str1, false))
     {
       result = TOKEN_STRING;
     }
@@ -1429,7 +1442,7 @@ peek_token (void)
     {
       result = TOKEN_WORD;
     }
-  else if (MATCH (ch, lquote.string, false))
+  else if (MATCH (ch, curr_quote.str1, false))
     {
       result = TOKEN_STRING;
     }
diff --git a/src/m4.h b/src/m4.h
index 474338b..a541687 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -79,12 +79,15 @@
 
 /* Various declarations.  */
 
-struct string
+/* Describes a pair of strings, such as begin and end quotes.  */
+struct string_pair
   {
-    char *string;              /* characters of the string */
-    size_t length;             /* length of the string */
+    char *str1;
+    size_t len1;
+    char *str2;
+    size_t len2;
   };
-typedef struct string STRING;
+typedef struct string_pair string_pair;
 
 /* Memory allocation.  */
 #define obstack_chunk_alloc    xmalloc
@@ -274,17 +277,40 @@ enum token_data_type
   TOKEN_COMP   /* Composite argument, u.u_c is valid.  */
 };
 
-/* Composite tokens are built of a linked list of chains.  */
+/* A link in a chain of token data.  */
+enum token_chain_type
+{
+  CHAIN_STR,   /* Link contains a string, u.u_s is valid.  */
+  /* TODO add CHAIN_FUNC.  */
+  CHAIN_ARGV   /* Link contains a $@ reference, u.u_a is valid.  */
+};
+
+/* Composite tokens are built of a linked list of chains.  Each link
+   of the chain is either a single text reference (ie. $1), or an argv
+   reference (ie. $@).  */
 struct token_chain
 {
   token_chain *next;           /* Pointer to next link of chain.  */
+  enum token_chain_type type;  /* Type of this link.  */
   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, else 0.  */
-  int level;                   /* Expansion level of link content, or -1.  */
-  macro_arguments *argv;       /* Reference to earlier address@hidden  */
-  unsigned int index;          /* Argument index within argv.  */
-  bool flatten;                        /* True to treat builtins as text.  */
+  union
+    {
+      struct
+       {
+         const char *str;      /* Pointer to text.  */
+         size_t len;           /* Remaining length of str.  */
+         int level;            /* Expansion level of link content, or -1.  */
+       }
+      u_s;
+      struct
+       {
+         macro_arguments *argv;        /* Reference to earlier address@hidden  
*/
+         unsigned int index;           /* Argument index within argv.  */
+         bool flatten;                 /* True to treat builtins as text.  */
+       }
+      u_a;
+    }
+  u;
 };
 
 /* The content of a token or macro argument.  */
@@ -363,8 +389,8 @@ extern const char *current_file;
 extern int current_line;
 
 /* left and right quote, begin and end comment */
-extern STRING bcomm, ecomm;
-extern STRING lquote, rquote;
+extern string_pair curr_comm;
+extern string_pair curr_quote;
 
 #define DEF_LQUOTE "`"
 #define DEF_RQUOTE "\'"
@@ -465,6 +491,14 @@ void push_arg (struct obstack *, macro_arguments *, 
unsigned int);
 void push_args (struct obstack *, macro_arguments *, bool, bool);
 size_t adjust_refcount (int, bool);
 
+/* Grab the text at argv index I.  Assumes macro_argument *argv is in
+   scope, and aborts if the argument is not text.  */
+#define ARG(i) arg_text (argv, i)
+
+/* Grab the text length at argv index I.  Assumes macro_argument *argv
+   is in scope, and aborts if the argument is not text.  */
+#define ARG_LEN(i) arg_len (argv, i)
+
 
 /* File: builtin.c  --- builtins.  */
 
diff --git a/src/macro.c b/src/macro.c
index 62af398..d22226e 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -673,8 +673,9 @@ expand_macro (symbol *sym)
            chain = argv->array[i]->u.u_c.chain;
            while (chain)
              {
-               if (chain->level >= 0)
-                 adjust_refcount (chain->level, false);
+               assert (chain->type == CHAIN_STR);
+               if (chain->u.u_s.level >= 0)
+                 adjust_refcount (chain->u.u_s.level, false);
                chain = chain->next;
              }
          }
@@ -753,15 +754,17 @@ arg_token (macro_arguments *argv, unsigned int index)
        {
          token_chain *chain = token->u.u_c.chain;
          /* TODO - for now we support only a single-length $@ chain.  */
-         assert (!chain->next && !chain->str);
-         if (index < chain->argv->argc - (chain->index - 1))
+         assert (!chain->next && chain->type == CHAIN_ARGV);
+         if (index < chain->u.u_a.argv->argc - (chain->u.u_a.index - 1))
            {
-             token = arg_token (chain->argv, chain->index - 1 + index);
-             if (chain->flatten && TOKEN_DATA_TYPE (token) == TOKEN_FUNC)
+             token = arg_token (chain->u.u_a.argv,
+                                chain->u.u_a.index - 1 + index);
+             if (chain->u.u_a.flatten
+                 && TOKEN_DATA_TYPE (token) == TOKEN_FUNC)
                token = &empty_token;
              break;
            }
-         index -= chain->argv->argc - chain->index;
+         index -= chain->u.u_a.argv->argc - chain->u.u_a.index;
        }
       else if (--index == 0)
        break;
@@ -781,8 +784,8 @@ arg_mark (macro_arguments *argv)
       assert (argv->arraylen == 1
              && TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP
              && !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;
+             && argv->array[0]->u.u_c.chain->type == CHAIN_ARGV);
+      argv->array[0]->u.u_c.chain->u.u_a.argv->inuse = true;
     }
 }
 
@@ -820,7 +823,7 @@ arg_text (macro_arguments *argv, unsigned int index)
 {
   token_data *token;
   token_chain *chain;
-  struct obstack *obs;
+  struct obstack *obs; /* Scratch space; cleaned at end of macro_expand.  */
 
   if (index == 0)
     return argv->argv0;
@@ -838,8 +841,8 @@ arg_text (macro_arguments *argv, unsigned int index)
       obs = arg_scratch ();
       while (chain)
        {
-         assert (chain->str);
-         obstack_grow (obs, chain->str, chain->len);
+         assert (chain->type == CHAIN_STR);
+         obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
          chain = chain->next;
        }
       obstack_1grow (obs, '\0');
@@ -879,8 +882,9 @@ arg_equal (macro_arguments *argv, unsigned int indexa, 
unsigned int indexb)
   if (TOKEN_DATA_TYPE (ta) == TOKEN_TEXT)
     {
       tmpa.next = NULL;
-      tmpa.str = TOKEN_DATA_TEXT (ta);
-      tmpa.len = TOKEN_DATA_LEN (ta);
+      tmpa.type = CHAIN_STR;
+      tmpa.u.u_s.str = TOKEN_DATA_TEXT (ta);
+      tmpa.u.u_s.len = TOKEN_DATA_LEN (ta);
     }
   else
     {
@@ -890,8 +894,9 @@ arg_equal (macro_arguments *argv, unsigned int indexa, 
unsigned int indexb)
   if (TOKEN_DATA_TYPE (tb) == TOKEN_TEXT)
     {
       tmpb.next = NULL;
-      tmpb.str = TOKEN_DATA_TEXT (tb);
-      tmpb.len = TOKEN_DATA_LEN (tb);
+      tmpb.type = CHAIN_STR;
+      tmpb.u.u_s.str = TOKEN_DATA_TEXT (tb);
+      tmpb.u.u_s.len = TOKEN_DATA_LEN (tb);
     }
   else
     {
@@ -903,32 +908,34 @@ arg_equal (macro_arguments *argv, unsigned int indexa, 
unsigned int indexb)
   while (ca && cb)
     {
       /* TODO support comparison against $@ refs.  */
-      assert (ca->str && cb->str);
-      if (ca->len == cb->len)
+      assert (ca->type == CHAIN_STR && cb->type == CHAIN_STR);
+      if (ca->u.u_s.len == cb->u.u_s.len)
        {
-         if (memcmp (ca->str, cb->str, ca->len) != 0)
+         if (memcmp (ca->u.u_s.str, cb->u.u_s.str, ca->u.u_s.len) != 0)
            return false;
          ca = ca->next;
          cb = cb->next;
        }
-      else if (ca->len < cb->len)
+      else if (ca->u.u_s.len < cb->u.u_s.len)
        {
-         if (memcmp (ca->str, cb->str, ca->len) != 0)
+         if (memcmp (ca->u.u_s.str, cb->u.u_s.str, ca->u.u_s.len) != 0)
            return false;
          tmpb.next = cb->next;
-         tmpb.str = cb->str + ca->len;
-         tmpb.len = cb->len - ca->len;
+         tmpb.type = CHAIN_STR;
+         tmpb.u.u_s.str = cb->u.u_s.str + ca->u.u_s.len;
+         tmpb.u.u_s.len = cb->u.u_s.len - ca->u.u_s.len;
          ca = ca->next;
          cb = &tmpb;
        }
       else
        {
-         assert (ca->len > cb->len);
-         if (memcmp (ca->str, cb->str, cb->len) != 0)
+         assert (ca->u.u_s.len > cb->u.u_s.len);
+         if (memcmp (ca->u.u_s.str, cb->u.u_s.str, cb->u.u_s.len) != 0)
            return false;
          tmpa.next = ca->next;
-         tmpa.str = ca->str + cb->len;
-         tmpa.len = ca->len - cb->len;
+         tmpa.type = CHAIN_STR;
+         tmpa.u.u_s.str = ca->u.u_s.str + cb->u.u_s.len;
+         tmpa.u.u_s.len = ca->u.u_s.len - cb->u.u_s.len;
          ca = &tmpa;
          cb = cb->next;
        }
@@ -979,8 +986,8 @@ arg_len (macro_arguments *argv, unsigned int index)
       len = 0;
       while (chain)
        {
-         assert (chain->str);
-         len += chain->len;
+         assert (chain->type == CHAIN_STR);
+         len += chain->u.u_s.len;
          chain = chain->next;
        }
       assert (len);
@@ -1039,9 +1046,9 @@ make_argv_ref (macro_arguments *argv, const char *argv0, 
size_t argv0_len,
       assert (argv->arraylen == 1
              && TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP);
       chain = argv->array[0]->u.u_c.chain;
-      assert (!chain->next && !chain->str);
-      argv = chain->argv;
-      index += chain->index - 1;
+      assert (!chain->next && chain->type == CHAIN_ARGV);
+      argv = chain->u.u_a.argv;
+      index += chain->u.u_a.index - 1;
     }
   if (argv->argc <= index)
     {
@@ -1065,13 +1072,11 @@ make_argv_ref (macro_arguments *argv, const char 
*argv0, size_t argv0_len,
       TOKEN_DATA_TYPE (token) = TOKEN_COMP;
       token->u.u_c.chain = token->u.u_c.end = chain;
       chain->next = NULL;
+      chain->type = CHAIN_ARGV;
       chain->quote_age = argv->quote_age;
-      chain->str = NULL;
-      chain->len = 0;
-      chain->level = expansion_level - 1;
-      chain->argv = argv;
-      chain->index = index;
-      chain->flatten = flatten;
+      chain->u.u_a.argv = argv;
+      chain->u.u_a.index = index;
+      chain->u.u_a.flatten = flatten;
     }
   new_argv->argc = argv->argc - (index - 1);
   new_argv->inuse = false;
@@ -1111,8 +1116,8 @@ push_arg (struct obstack *obs, macro_arguments *argv, 
unsigned int index)
       token_chain *chain = token->u.u_c.chain;
       while (chain)
        {
-         assert (chain->str);
-         obstack_grow (obs, chain->str, chain->len);
+         assert (chain->type == CHAIN_STR);
+         obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
          chain = chain->next;
        }
     }
@@ -1140,22 +1145,22 @@ push_args (struct obstack *obs, macro_arguments *argv, 
bool skip, bool quote)
   if (i + 1 == argv->argc)
     {
       if (quote)
-       obstack_grow (obs, lquote.string, lquote.length);
+       obstack_grow (obs, curr_quote.str1, curr_quote.len1);
       push_arg (obs, argv, i);
       if (quote)
-       obstack_grow (obs, rquote.string, rquote.length);
+       obstack_grow (obs, curr_quote.str2, curr_quote.len2);
       return;
     }
 
   /* Compute the separator in the scratch space.  */
   if (quote)
     {
-      obstack_grow (obs, lquote.string, lquote.length);
-      obstack_grow (scratch, rquote.string, rquote.length);
+      obstack_grow (obs, curr_quote.str1, curr_quote.len1);
+      obstack_grow (scratch, curr_quote.str2, curr_quote.len2);
       obstack_1grow (scratch, ',');
-      obstack_grow0 (scratch, lquote.string, lquote.length);
+      obstack_grow0 (scratch, curr_quote.str1, curr_quote.len1);
       sep = (char *) obstack_finish (scratch);
-      sep_len += lquote.length + rquote.length;
+      sep_len += curr_quote.len1 + curr_quote.len2;
     }
   /* TODO push entire $@ reference, rather than pushing each arg.  */
   for ( ; i < argv->argc; i++)
@@ -1175,14 +1180,14 @@ push_args (struct obstack *obs, macro_arguments *argv, 
bool skip, bool quote)
          chain = token->u.u_c.chain;
          while (chain)
            {
-             assert (chain->str);
-             obstack_grow (obs, chain->str, chain->len);
+             assert (chain->type == CHAIN_STR);
+             obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
              chain = chain->next;
            }
        }
     }
   if (quote)
-    obstack_grow (obs, rquote.string, rquote.length);
+    obstack_grow (obs, curr_quote.str2, curr_quote.len2);
   if (inuse)
     arg_mark (argv);
 }
-- 
1.5.3.8


reply via email to

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