m4-patches
[Top][All Lists]
Advanced

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

[1/18] argv_ref speedup: replace array with accessor struct


From: Eric Blake
Subject: [1/18] argv_ref speedup: replace array with accessor struct
Date: Sat, 24 Nov 2007 17:19:24 -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

I've finally ported the first of my series of 18 patches for $@ reference
speedups to the head, so I'm committing the following to branch-1_4 and
head.  On the branch, this differs slightly from the argv_ref branch, in
that it has a changelog entry and no // comments in the code.  Still 17 to
go, but I'll get there.  I welcome any review on any stage of the patch
series.

The goal of this patch is a mechanical replacement - instead of passing a
raw array of arguments to every builtin, we instead pass a pointer to a
struct that wraps the array.  No speedup from this patch and a slight
memory penalty, but it is essential for future patches, when the wrapper
struct can also point to a reference to multiple arguments in a single
array slot, without having to adjust all the clients of the struct.

2007-11-24  Eric Blake  <address@hidden>

        Stage 1: convert token_data** into new object.
        * m4/gnulib-cache.m4: Import flexmember module.
        * src/m4.h (struct macro_arguments, struct token_chain): New
        structs.
        (builtin_func): Alter signature.
        (token_data): Add new TOKEN_COMP alternative.
        * src/builtin.c: All builtins changed.
        (ARG, dump_args, define_macro, expand_user_macro): Update to use
        struct.
        * src/debug.c (trace_pre, trace_post): Likewise.
        * src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE, format):
        Likewise.
        * src/macro.c (collect_arguments): Build new struct.
        (call_macro, expand_macro): Update to use new struct.

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

iD8DBQFHSL+L84KuGfSFAYARAmW8AKDK2YiHz9n4HM8UeVUbcDeG3aeCcQCePhnu
iOnmssti6SGFqJTuI7dNCIM=
=O0RE
-----END PGP SIGNATURE-----
>From 14b2ffbb82eb22cd36b1703a5501ee87ea51c45c Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Sat, 24 Nov 2007 16:23:47 -0700
Subject: [PATCH] Stage 1: convert m4_symbol_value** into new object.

* ltdl/m4/gnulib-cache.m4: Import flexmember module.
* m4/m4module.h (m4_macro_args): New type, will be opaque to
modules later.
(m4_builtin_func, M4BUILTIN, M4BUILTIN_HANDLER, m4_dump_args)
(m4_macro_call): Alter signature to use m4_macro_args object.
(M4ARG): Redefine to access new struct.
* m4/m4private.h (M4_SYMBOL_COMP): New enumerator.
(struct m4_symbol_chain): New type.
(struct m4_symbol_value): Add chain alternative.
* m4/macro.c (collect_arguments): Build new struct.
(expand_macro, m4_macro_call, process_macro, trace_pre)
(trace_post): Adjust implementation to use it.
* m4/utility.c (m4_dump_args): Likewise.
* modules/gnu.c (builtin, indir): Likewise.
* modules/format.c (format): Likewise.
* modules/m4.h (m4_dump_symbols_func): Likewise.
* modules/m4.c (m4_dump_symbols, define, pushdef, defn, ifelse)
(shift, include, errprint, m4wrap): Likewise.
* modules/evalparse.c (m4_evaluate): Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog               |   21 ++++++++++
 ltdl/m4/gnulib-cache.m4 |    4 +-
 m4/m4module.h           |   72 ++++++++++++++++++++++------------
 m4/m4private.h          |   30 +++++++++++---
 m4/macro.c              |   97 ++++++++++++++++++++++++----------------------
 m4/utility.c            |   16 +++++---
 modules/evalparse.c     |    2 +-
 modules/format.c        |   52 ++++++++++++-------------
 modules/gnu.c           |   49 +++++++++++++++++------
 modules/m4.c            |   42 +++++++++++----------
 modules/m4.h            |    2 +-
 11 files changed, 238 insertions(+), 149 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f71a00f..2fbe4ee 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,26 @@
 2007-11-24  Eric Blake  <address@hidden>
 
+       Stage 1: convert m4_symbol_value** into new object.
+       * ltdl/m4/gnulib-cache.m4: Import flexmember module.
+       * m4/m4module.h (m4_macro_args): New type, will be opaque to
+       modules later.
+       (m4_builtin_func, M4BUILTIN, M4BUILTIN_HANDLER, m4_dump_args)
+       (m4_macro_call): Alter signature to use m4_macro_args object.
+       (M4ARG): Redefine to access new struct.
+       * m4/m4private.h (M4_SYMBOL_COMP): New enumerator.
+       (struct m4_symbol_chain): New type.
+       (struct m4_symbol_value): Add chain alternative.
+       * m4/macro.c (collect_arguments): Build new struct.
+       (expand_macro, m4_macro_call, process_macro, trace_pre)
+       (trace_post): Adjust implementation to use it.
+       * m4/utility.c (m4_dump_args): Likewise.
+       * modules/gnu.c (builtin, indir): Likewise.
+       * modules/format.c (format): Likewise.
+       * modules/m4.h (m4_dump_symbols_func): Likewise.
+       * modules/m4.c (m4_dump_symbols, define, pushdef, defn, ifelse)
+       (shift, include, errprint, m4wrap): Likewise.
+       * modules/evalparse.c (m4_evaluate): Likewise.
+
        Pass only macro name to utility functions.
        * m4/m4module.h (m4_bad_argc, m4_numeric_arg): Adjust signature.
        * m4/utility.c (m4_bad_argc, m4_numeric_arg): Adjust
diff --git a/ltdl/m4/gnulib-cache.m4 b/ltdl/m4/gnulib-cache.m4
index 9c17a78..1eefbd5 100644
--- a/ltdl/m4/gnulib-cache.m4
+++ b/ltdl/m4/gnulib-cache.m4
@@ -15,11 +15,11 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --local-dir=local --lib=libgnu 
--source-base=gnu --m4-base=ltdl/m4 --doc-base=doc --aux-dir=build-aux 
--with-tests --libtool --macro-prefix=M4 assert autobuild avltree-oset 
binary-io clean-temp cloexec close-stream closein config-h configmake dirname 
error exit fdl fflush filenamecat fopen-safer free fseeko gendocs gettext 
gnupload gpl-3.0 mkstemp obstack progname regex regexprops-generic 
sprintf-posix stdbool stdlib-safer strnlen strtol tempname unlocked-io verror 
xalloc xalloc-die xprintf-posix xstrndup xvasprintf-posix
+#   gnulib-tool --import --dir=. --local-dir=local --lib=libgnu 
--source-base=gnu --m4-base=ltdl/m4 --doc-base=doc --aux-dir=build-aux 
--with-tests --libtool --macro-prefix=M4 assert autobuild avltree-oset 
binary-io clean-temp cloexec close-stream closein config-h configmake dirname 
error exit fdl fflush filenamecat flexmember fopen-safer free fseeko gendocs 
gettext gnupload gpl-3.0 mkstemp obstack progname regex regexprops-generic 
sprintf-posix stdbool stdlib-safer strnlen strtol tempname unlocked-io verror 
xalloc xalloc-die xprintf-posix xstrndup xvasprintf-posix
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([local])
-gl_MODULES([assert autobuild avltree-oset binary-io clean-temp cloexec 
close-stream closein config-h configmake dirname error exit fdl fflush 
filenamecat fopen-safer free fseeko gendocs gettext gnupload gpl-3.0 mkstemp 
obstack progname regex regexprops-generic sprintf-posix stdbool stdlib-safer 
strnlen strtol tempname unlocked-io verror xalloc xalloc-die xprintf-posix 
xstrndup xvasprintf-posix])
+gl_MODULES([assert autobuild avltree-oset binary-io clean-temp cloexec 
close-stream closein config-h configmake dirname error exit fdl fflush 
filenamecat flexmember fopen-safer free fseeko gendocs gettext gnupload gpl-3.0 
mkstemp obstack progname regex regexprops-generic sprintf-posix stdbool 
stdlib-safer strnlen strtol tempname unlocked-io verror xalloc xalloc-die 
xprintf-posix xstrndup xvasprintf-posix])
 gl_AVOID([])
 gl_SOURCE_BASE([gnu])
 gl_M4_BASE([ltdl/m4])
diff --git a/m4/m4module.h b/m4/m4module.h
index 01514db..3f60cba 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -37,10 +37,11 @@ typedef struct m4_macro             m4_macro;
 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 obstack         m4_obstack;
 
-typedef void   m4_builtin_func  (m4 *, m4_obstack *, int, m4_symbol_value **);
+typedef void   m4_builtin_func  (m4 *, m4_obstack *, int, m4_macro_args *);
 
 /* The value of m4_builtin flags is built from these:  */
 enum {
@@ -75,38 +76,58 @@ struct m4_macro
   const char *value;
 };
 
+/* FIXME - make this struct opaque.  */
+struct m4_macro_args
+{
+  /* One more than the highest actual argument.  May be larger than
+     arraylen since the array can refer to multiple arguments via a
+     single $@ reference.  */
+  unsigned int argc;
+  /* False unless the macro expansion refers to $@; determines whether
+     this object can be freed at end of macro expansion or must wait
+     until all references have been rescanned.  */
+  bool inuse;
+  const char *argv0; /* The macro name being expanded.  */
+  size_t arraylen; /* True length of allocated elements in array.  */
+  /* Used as a variable-length array, storing information about each
+     argument.  */
+  m4_symbol_value *array[FLEXIBLE_ARRAY_MEMBER];
+};
+
 
-#define M4BUILTIN(name)                                        \
-  static void CONC(builtin_, name)                             \
-   (m4 *context, m4_obstack *obs, int argc, m4_symbol_value **argv);
+#define M4BUILTIN(name)                                                        
\
+  static void CONC (builtin_, name)                                    \
+   (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv);
 
-#define M4BUILTIN_HANDLER(name)                                \
-  static void CONC(builtin_, name)                             \
-   (m4 *context, m4_obstack *obs, int argc, m4_symbol_value **argv)
+#define M4BUILTIN_HANDLER(name)                                                
\
+  static void CONC (builtin_, name)                                    \
+   (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv)
 
-#define M4INIT_HANDLER(name)                                   \
-  void CONC(name, CONC(_LTX_, m4_init_module))                 \
-       (m4 *context, m4_module *module, m4_obstack *obs);      \
-  void CONC(name, CONC(_LTX_, m4_init_module))                 \
+#define M4INIT_HANDLER(name)                                           \
+  void CONC (name, CONC (_LTX_, m4_init_module))                       \
+       (m4 *context, m4_module *module, m4_obstack *obs);              \
+  void CONC (name, CONC (_LTX_, m4_init_module))                       \
        (m4 *context, m4_module *module, m4_obstack *obs)
 
-#define M4FINISH_HANDLER(name)                                 \
-  void CONC(name, CONC(_LTX_, m4_finish_module))               \
-       (m4 *context, m4_module *module, m4_obstack *obs);      \
-  void CONC(name, CONC(_LTX_, m4_finish_module))               \
+#define M4FINISH_HANDLER(name)                                         \
+  void CONC (name, CONC (_LTX_, m4_finish_module))                     \
+       (m4 *context, m4_module *module, m4_obstack *obs);              \
+  void CONC (name, CONC (_LTX_, m4_finish_module))                     \
        (m4 *context, m4_module *module, m4_obstack *obs)
 
-#define M4_MODULE_IMPORT(M, S)                                 \
-  CONC(S, _func) *S = (CONC(S, _func) *)                       \
-       m4_module_import (context, STR(M), STR(S), obs)
+#define M4_MODULE_IMPORT(M, S)                                         \
+  CONC (S, _func) *S = (CONC (S, _func) *)                             \
+       m4_module_import (context, STR (M), STR (S), obs)
 
-#define M4ARG(i)       (argc > (i) ? m4_get_symbol_value_text (argv[i]) : "")
+#define M4ARG(i)                                                       \
+  ((i) == 0 ? argv->argv0                                              \
+   : argv->argc > (i) ? m4_get_symbol_value_text (argv->array[(i) - 1]) : "")
 
 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 *);
 extern void    m4_dump_args       (m4 *, m4_obstack *, int,
-                                   m4_symbol_value **, const char *, bool);
+                                   m4_macro_args *, const char *, bool);
 extern bool    m4_parse_truth_arg (m4 *, const char *, const char *, bool);
 
 /* Error handling.  */
@@ -164,14 +185,14 @@ extern void               m4_delete       (m4 *);
 
 
 #define M4FIELD(type, base, field)                                     \
-       extern type CONC(m4_get_, base) (m4 *context);                  \
-       extern type CONC(m4_set_, base) (m4 *context, type value);
+       extern type CONC (m4_get_, base) (m4 *context);                 \
+       extern type CONC (m4_set_, base) (m4 *context, type value);
 m4_context_field_table
 #undef M4FIELD
 
 #define M4OPT_BIT(bit, base)                                           \
-       extern bool CONC(m4_get_, base) (m4 *context);          \
-       extern bool CONC(m4_set_, base) (m4 *context, bool value);
+  extern bool CONC (m4_get_, base) (m4 *context);                      \
+  extern bool CONC (m4_set_, base) (m4 *context, bool value);
 m4_context_opt_bit_table
 #undef M4OPT_BIT
 
@@ -287,8 +308,7 @@ extern const m4_builtin     *m4_builtin_find_by_func 
(m4_module *,
 
 extern void       m4_macro_expand_input (m4 *);
 extern void       m4_macro_call         (m4 *, m4_symbol_value *,
-                                         m4_obstack *, int,
-                                         m4_symbol_value **);
+                                         m4_obstack *, int, m4_macro_args *);
 
 
 
diff --git a/m4/m4private.h b/m4/m4private.h
index b067254..63a9bc4 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -30,10 +30,11 @@
 typedef struct m4__search_path_info m4__search_path_info;
 
 typedef enum {
-  M4_SYMBOL_VOID, /* Traced but undefined.  */
-  M4_SYMBOL_TEXT, /* Plain text.  */
-  M4_SYMBOL_FUNC, /* Builtin function.  */
-  M4_SYMBOL_PLACEHOLDER /* Placeholder for unknown builtin during -R.  */
+  M4_SYMBOL_VOID,              /* Traced but undefined, u is invalid.  */
+  M4_SYMBOL_TEXT,              /* Plain text, u.u_t is valid.  */
+  M4_SYMBOL_FUNC,              /* Builtin function, u.func is valid.  */
+  M4_SYMBOL_PLACEHOLDER,       /* Placeholder for unknown builtin from -R.  */
+  M4_SYMBOL_COMP               /* Composite symbol, u.chain is valid.  */
 } m4__symbol_type;
 
 #define BIT_TEST(flags, bit)   (((flags) & (bit)) == (bit))
@@ -171,13 +172,27 @@ extern m4_module *  m4__module_find (const char *name);
 
 /* --- SYMBOL TABLE MANAGEMENT --- */
 
+typedef struct m4_symbol_chain m4_symbol_chain;
+
 struct m4_symbol
 {
   bool         traced;
   m4_symbol_value *    value;
 };
 
-struct m4_symbol_value {
+/* Composite symbols are built of a linked list of chain objects.  */
+struct m4_symbol_chain
+{
+  m4_symbol_chain *next;/* Pointer to next link of chain.  */
+  char *str;           /* NUL-terminated string if text, else NULL.  */
+  m4_macro_args *argv; /* Reference to earlier address@hidden  */
+  unsigned int index;  /* Index within argv to start reading from.  */
+};
+
+/* A symbol value is used both for values associated with a macro
+   name, and for arguments to a macro invocation.  */
+struct m4_symbol_value
+{
   m4_symbol_value *    next;
   m4_module *          module;
   unsigned int         flags;
@@ -189,8 +204,9 @@ struct m4_symbol_value {
 
   m4__symbol_type      type;
   union {
-    const char *       text; /* Valid when type is TEXT, PLACEHOLDER.  */
-    const m4_builtin * builtin; /* Valid when type is FUNC.  */
+    const char *       text;   /* Valid when type is TEXT, PLACEHOLDER.  */
+    const m4_builtin * builtin;/* Valid when type is FUNC.  */
+    m4_symbol_chain *  chain;  /* Valid when type is COMP.  */
   } u;
 };
 
diff --git a/m4/macro.c b/m4/macro.c
index dd93152..c4ba1af 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -29,22 +29,23 @@
 
 #include "intprops.h"
 
-static void    collect_arguments (m4 *, const char *, m4_symbol *,
-                                 m4_obstack *, m4_obstack *);
+static m4_macro_args *collect_arguments (m4 *, const char *, m4_symbol *,
+                                        m4_obstack *, unsigned int,
+                                        m4_obstack *);
 static void    expand_macro      (m4 *, const char *, m4_symbol *);
 static void    expand_token      (m4 *, m4_obstack *, m4__token_type,
                                  m4_symbol_value *, int);
 static bool    expand_argument   (m4 *, m4_obstack *, m4_symbol_value *,
                                  const char *);
 static void    process_macro    (m4 *, m4_symbol_value *, m4_obstack *, int,
-                                 m4_symbol_value **);
+                                 m4_macro_args *);
 
 static void    trace_prepre     (m4 *, const char *, size_t,
                                  m4_symbol_value *);
 static void    trace_pre        (m4 *, const char *, size_t, int,
-                                 m4_symbol_value **);
+                                 m4_macro_args *);
 static void    trace_post       (m4 *, const char *, size_t, int,
-                                 m4_symbol_value **, m4_input_block *, bool);
+                                 m4_macro_args *, m4_input_block *, bool);
 
 static void    trace_format     (m4 *, const char *, ...)
   M4_GNUC_PRINTF (2, 3);
@@ -126,11 +127,11 @@ expand_token (m4 *context, m4_obstack *obs,
          ++textp;
 
        symbol = m4_symbol_lookup (M4SYMTAB, textp);
-       assert (! symbol || ! m4_is_symbol_void (symbol));
+       assert (!symbol || !m4_is_symbol_void (symbol));
        if (symbol == NULL
            || (symbol->value->type == M4_SYMBOL_FUNC
                && BIT_TEST (SYMBOL_FLAGS (symbol), VALUE_BLIND_ARGS_BIT)
-               && ! m4__next_token_is_open (context)))
+               && !m4__next_token_is_open (context)))
          {
            m4_shipout_text (context, obs, text, strlen (text), line);
          }
@@ -248,7 +249,7 @@ expand_macro (m4 *context, const char *name, m4_symbol 
*symbol)
   char *argc_base = NULL;      /* Base of argc_stack on entry.  */
   unsigned int argc_size;      /* Size of argc_stack on entry.  */
   unsigned int argv_size;      /* Size of argv_stack on entry.  */
-  m4_symbol_value **argv;
+  m4_macro_args *argv;
   int argc;
   m4_obstack *expansion;
   m4_input_block *expanded;
@@ -297,15 +298,12 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
   if (traced && m4_is_debug_bit (context, M4_DEBUG_TRACE_CALL))
     trace_prepre (context, name, my_call_id, value);
 
-  collect_arguments (context, name, symbol, &argv_stack, &argc_stack);
-
-  argc = ((obstack_object_size (&argv_stack) - argv_size)
-         / sizeof (m4_symbol_value *));
-  argv = (m4_symbol_value **) ((char *) obstack_base (&argv_stack)
-                              + argv_size);
+  argv = collect_arguments (context, name, symbol, &argv_stack, argv_size,
+                           &argc_stack);
+  argc = argv->argc;
   /* Calling collect_arguments invalidated name, but we copied it as
      argv[0].  */
-  name = m4_get_symbol_value_text (argv[0]);
+  name = argv->argv0;
 
   loc_close_file = m4_get_current_file (context);
   loc_close_line = m4_get_current_line (context);
@@ -331,32 +329,39 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
   if (BIT_TEST (VALUE_FLAGS (value), VALUE_DELETED_BIT))
     m4_symbol_value_delete (value);
 
+  /* TODO - pay attention to argv->inuse.  */
   if (0 < argc_size)
     obstack_regrow (&argc_stack, argc_base, argc_size);
   else
-    obstack_free (&argc_stack, argv[0]);
-  obstack_blank (&argv_stack, -argc * sizeof (m4_symbol_value *));
+    obstack_free (&argc_stack, (void *) name);
+  obstack_blank (&argv_stack, argv_size - obstack_object_size (&argv_stack));
 }
 
-/* Collect all the arguments to a call of the macro SYMBOL (called NAME).
-   The arguments are stored on the obstack ARGUMENTS and a table of pointers
-   to the arguments on the obstack ARGPTR.  */
-static void
+/* Collect all the arguments to a call of the macro SYMBOL (called
+   NAME).  The arguments are stored on the obstack ARGUMENTS and a
+   table of pointers to the arguments on the obstack ARGPTR.  ARGPTR
+   is an incomplete object, currently occupying ARGV_BASE bytes.
+   Return the object describing all of the macro arguments.  */
+static m4_macro_args *
 collect_arguments (m4 *context, const char *name, m4_symbol *symbol,
-                  m4_obstack *argptr, m4_obstack *arguments)
+                  m4_obstack *argptr, unsigned argv_base,
+                  m4_obstack *arguments)
 {
   m4_symbol_value token;
   m4_symbol_value *tokenp;
   bool more_args;
   bool groks_macro_args;
+  m4_macro_args args;
+  m4_macro_args *argv;
 
   groks_macro_args = BIT_TEST (SYMBOL_FLAGS (symbol), VALUE_MACRO_ARGS_BIT);
 
-  tokenp = (m4_symbol_value *) obstack_alloc (arguments, sizeof *tokenp);
-  m4_set_symbol_value_text (tokenp, (char *) obstack_copy0 (arguments, name,
-                                                           strlen (name)));
-  name = m4_get_symbol_value_text (tokenp);
-  obstack_ptr_grow (argptr, tokenp);
+  args.argc = 1;
+  args.inuse = false;
+  args.argv0 = (char *) obstack_copy0 (arguments, name, strlen (name));
+  args.arraylen = 0;
+  obstack_grow (argptr, &args, offsetof (m4_macro_args, array));
+  name = args.argv0;
 
   if (m4__next_token_is_open (context))
     {
@@ -373,9 +378,15 @@ collect_arguments (m4 *context, const char *name, 
m4_symbol *symbol,
          tokenp = (m4_symbol_value *) obstack_copy (arguments, &token,
                                                     sizeof token);
          obstack_ptr_grow (argptr, tokenp);
+         args.arraylen++;
+         args.argc++;
        }
       while (more_args);
     }
+  argv = (m4_macro_args *) ((char *) obstack_base (argptr) + argv_base);
+  argv->argc = args.argc;
+  argv->arraylen = args.arraylen;
+  return argv;
 }
 
 
@@ -387,30 +398,24 @@ collect_arguments (m4 *context, const char *name, 
m4_symbol *symbol,
    the obstack EXPANSION.  Macro tracing is also handled here.  */
 void
 m4_macro_call (m4 *context, m4_symbol_value *value, m4_obstack *expansion,
-              int argc, m4_symbol_value **argv)
+              int argc, m4_macro_args *argv)
 {
-  if (m4_bad_argc (context, argc, m4_get_symbol_value_text (argv[0]),
+  if (m4_bad_argc (context, argc, argv->argv0,
                   VALUE_MIN_ARGS (value), VALUE_MAX_ARGS (value),
                   BIT_TEST (VALUE_FLAGS (value),
                             VALUE_SIDE_EFFECT_ARGS_BIT)))
     return;
   if (m4_is_symbol_value_text (value))
-    {
-      process_macro (context, value, expansion, argc, argv);
-    }
+    process_macro (context, value, expansion, argc, argv);
   else if (m4_is_symbol_value_func (value))
-    {
-      (*m4_get_symbol_value_func (value)) (context, expansion, argc, argv);
-    }
+    m4_get_symbol_value_func (value) (context, expansion, argc, argv);
   else if (m4_is_symbol_value_placeholder (value))
-    {
-      m4_warn (context, 0, M4ARG (0),
-              _("builtin `%s' requested by frozen file not found"),
-              m4_get_symbol_value_placeholder (value));
-    }
+    m4_warn (context, 0, M4ARG (0),
+            _("builtin `%s' requested by frozen file not found"),
+            m4_get_symbol_value_placeholder (value));
   else
     {
-      assert (!"INTERNAL ERROR: bad symbol type in call_macro ()");
+      assert (!"INTERNAL ERROR: bad symbol type in m4_macro_call ()");
       abort ();
     }
 }
@@ -422,7 +427,7 @@ m4_macro_call (m4 *context, m4_symbol_value *value, 
m4_obstack *expansion,
    as usual.  */
 static void
 process_macro (m4 *context, m4_symbol_value *value, m4_obstack *obs,
-              int argc, m4_symbol_value **argv)
+              int argc, m4_macro_args *argv)
 {
   const char *text;
   int i;
@@ -467,7 +472,7 @@ process_macro (m4 *context, m4_symbol_value *value, 
m4_obstack *obs,
 
        case '*':               /* all arguments */
        case '@':               /* ... same, but quoted */
-         m4_dump_args (context, obs, argc, argv, ",", *text == '@');
+         m4_dump_args (context, obs, 1, argv, ",", *text == '@');
          text++;
          break;
 
@@ -649,7 +654,7 @@ trace_prepre (m4 *context, const char *name, size_t id, 
m4_symbol_value *value)
    actually expanded.  Used from expand_macro ().  */
 static void
 trace_pre (m4 *context, const char *name, size_t id,
-          int argc, m4_symbol_value **argv)
+          int argc, m4_macro_args *argv)
 {
   int i;
 
@@ -670,7 +675,7 @@ trace_pre (m4 *context, const char *name, size_t id,
          if (i != 1)
            trace_format (context, ", ");
 
-         m4_symbol_value_print (argv[i], &context->trace_messages,
+         m4_symbol_value_print (argv->array[i - 1], &context->trace_messages,
                                 quote, lquote, rquote, arg_length, module);
        }
       trace_format (context, ")");
@@ -681,7 +686,7 @@ trace_pre (m4 *context, const char *name, size_t id,
    expand_macro ().  */
 static void
 trace_post (m4 *context, const char *name, size_t id,
-           int argc, m4_symbol_value **argv, m4_input_block *expanded,
+           int argc, m4_macro_args *argv, m4_input_block *expanded,
            bool trace_expansion)
 {
   if (trace_expansion)
diff --git a/m4/utility.c b/m4/utility.c
index a8873f3..851da31 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -95,19 +95,23 @@ m4_numeric_arg (m4 *context, const char *caller, const char 
*arg, int *valuep)
 }
 
 
-/* Print ARGC arguments from the table ARGV to obstack OBS, separated by
-   SEP, and quoted by the current quotes, if QUOTED is true.  */
+/* Print arguments from the table ARGV to obstack OBS, starting at
+   index START, separated by SEP, and quoted by the current quotes, if
+   QUOTED is true.  */
 void
-m4_dump_args (m4 *context, m4_obstack *obs, int argc,
-             m4_symbol_value **argv, const char *sep, bool quoted)
+m4_dump_args (m4 *context, m4_obstack *obs, int start,
+             m4_macro_args *argv, const char *sep, bool quoted)
 {
   int i;
   size_t len = strlen (sep);
+  bool need_sep = false;
 
-  for (i = 1; i < argc; i++)
+  for (i = start; i < argv->argc; i++)
     {
-      if (i > 1)
+      if (need_sep)
        obstack_grow (obs, sep, len);
+      else
+       need_sep = true;
 
       m4_shipout_string (context, obs, M4ARG (i), 0, quoted);
     }
diff --git a/modules/evalparse.c b/modules/evalparse.c
index 260ee1a..31e1e4b 100644
--- a/modules/evalparse.c
+++ b/modules/evalparse.c
@@ -885,7 +885,7 @@ simple_term (m4 *context, eval_token et, number *v1)
 
 /* Main entry point, called from "eval" and "mpeval" builtins.  */
 void
-m4_evaluate (m4 *context, m4_obstack *obs, int argc, m4_symbol_value **argv)
+m4_evaluate (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv)
 {
   const char * me      = M4ARG (0);
   const char * str     = M4ARG (1);
diff --git a/modules/format.c b/modules/format.c
index 8e5f670..ced3924 100644
--- a/modules/format.c
+++ b/modules/format.c
@@ -30,21 +30,17 @@
    TODO - use xstrtoimax, not atoi, to catch overflow, non-numeric
    arguments, etc.  */
 
-#define ARG_INT(argc, argv) \
-       ((argc == 0) ? 0 : \
-        (--argc, argv++, atoi (M4ARG (-1))))
+#define ARG_INT(i, argc, argv)                 \
+  ((argc <= i++) ? 0 : atoi (M4ARG (i - 1)))
 
-#define ARG_LONG(argc, argv) \
-       ((argc == 0) ? 0 : \
-        (--argc, argv++, atol (M4ARG (-1))))
+#define ARG_LONG(i, argc, argv)                        \
+  ((argc <= i++) ? 0L : atol (M4ARG (i - 1)))
 
-#define ARG_STR(argc, argv) \
-       ((argc == 0) ? "" : \
-        (--argc, argv++, M4ARG (-1)))
+#define ARG_STR(i, argc, argv)                 \
+  ((argc <= i++) ? "" : M4ARG (i - 1))
 
-#define ARG_DOUBLE(argc, argv) \
-       ((argc == 0) ? 0 : \
-        (--argc, argv++, atof (M4ARG (-1))))
+#define ARG_DOUBLE(i, argc, argv)              \
+  ((argc <= i++) ? 0.0 : atof (M4ARG (i - 1)))
 
 
 /* The main formatting function.  Output is placed on the obstack OBS,
@@ -54,14 +50,15 @@
    format.  */
 
 static void
-format (m4 *context, m4_obstack *obs, int argc, m4_symbol_value **argv)
+format (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv)
 {
-  const char *name = ARG_STR (argc, argv);     /* macro name */
-  const char *f;                       /* format control string */
-  const char *fmt;                     /* position within f */
-  char fstart[] = "%'+- 0#*.*hhd";     /* current format spec */
-  char *p;                             /* position within fstart */
-  unsigned char c;                     /* a simple character */
+  const char *name = M4ARG (0);                /* Macro name.  */
+  const char *f;                       /* Format control string.  */
+  const char *fmt;                     /* Position within f.  */
+  char fstart[] = "%'+- 0#*.*hhd";     /* Current format spec.  */
+  char *p;                             /* Position within fstart.  */
+  unsigned char c;                     /* A simple character.  */
+  int index = 1;                       /* Index within argc used so far.  */
 
   /* Flags.  */
   char flags;                          /* flags to use in fstart */
@@ -89,7 +86,7 @@ format (m4 *context, m4_obstack *obs, int argc, 
m4_symbol_value **argv)
   char *str;                   /* malloc'd buffer of formatted text */
   enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
 
-  f = fmt = ARG_STR (argc, argv);
+  f = fmt = ARG_STR (index, argc, argv);
   memset (ok, 0, sizeof ok);
   for (;;)
     {
@@ -174,7 +171,7 @@ format (m4 *context, m4_obstack *obs, int argc, 
m4_symbol_value **argv)
       *p++ = '*';
       if (*fmt == '*')
        {
-         width = ARG_INT (argc, argv);
+         width = ARG_INT (index, argc, argv);
          fmt++;
        }
       else
@@ -194,7 +191,7 @@ format (m4 *context, m4_obstack *obs, int argc, 
m4_symbol_value **argv)
          ok['c'] = 0;
          if (*(++fmt) == '*')
            {
-             prec = ARG_INT (argc, argv);
+             prec = ARG_INT (index, argc, argv);
              ++fmt;
            }
          else
@@ -279,23 +276,24 @@ format (m4 *context, m4_obstack *obs, int argc, 
m4_symbol_value **argv)
       switch (datatype)
        {
        case CHAR:
-         str = xasprintf (fstart, width, ARG_INT (argc, argv));
+         str = xasprintf (fstart, width, ARG_INT (index, argc, argv));
          break;
 
        case INT:
-         str = xasprintf (fstart, width, prec, ARG_INT (argc, argv));
+         str = xasprintf (fstart, width, prec, ARG_INT (index, argc, argv));
          break;
 
        case LONG:
-         str = xasprintf (fstart, width, prec, ARG_LONG (argc, argv));
+         str = xasprintf (fstart, width, prec, ARG_LONG (index, argc, argv));
          break;
 
        case DOUBLE:
-         str = xasprintf (fstart, width, prec, ARG_DOUBLE (argc, argv));
+         str = xasprintf (fstart, width, prec,
+                          ARG_DOUBLE (index, argc, argv));
          break;
 
        case STR:
-         str = xasprintf (fstart, width, prec, ARG_STR (argc, argv));
+         str = xasprintf (fstart, width, prec, ARG_STR (index, argc, argv));
          break;
 
        default:
diff --git a/modules/gnu.c b/modules/gnu.c
index e857632..6fd6623 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -409,14 +409,14 @@ M4BUILTIN_HANDLER (builtin)
   const char *name;
   m4_symbol_value *value;
 
-  if (!m4_is_symbol_value_text (argv[1]))
+  if (!m4_is_symbol_value_text (argv->array[1 - 1]))
     {
-      if (m4_is_symbol_value_func (argv[1])
-         && m4_get_symbol_value_func (argv[1]) == builtin_builtin)
+      if (m4_is_symbol_value_func (argv->array[1 - 1])
+         && m4_get_symbol_value_func (argv->array[1 - 1]) == builtin_builtin)
        {
          if (m4_bad_argc (context, argc, me, 2, 2, false))
            return;
-         if (!m4_is_symbol_value_text (argv[2]))
+         if (!m4_is_symbol_value_text (argv->array[2 - 1]))
            {
              m4_warn (context, 0, me, _("invalid macro name ignored"));
              return;
@@ -448,11 +448,22 @@ M4BUILTIN_HANDLER (builtin)
                            (bp->flags & M4_BUILTIN_SIDE_EFFECT) != 0))
            {
              int i;
+             /* TODO - make use of $@ reference */
+             m4_macro_args *new_argv;
+             new_argv = xmalloc (offsetof (m4_macro_args, array)
+                                 + ((argc - 2) * sizeof (m4_symbol_value *)));
+             new_argv->argc = argc - 1;
+             new_argv->inuse = false;
+             new_argv->argv0 = name;
+             new_argv->arraylen = argc - 2;
+             memcpy (&new_argv->array[0], &argv->array[1],
+                     (argc - 2) * sizeof (m4_symbol_value *));
              if ((bp->flags & M4_BUILTIN_GROKS_MACRO) == 0)
                for (i = 2; i < argc; i++)
-                 if (!m4_is_symbol_value_text (argv[i]))
-                   m4_set_symbol_value_text (argv[i], "");
-             bp->func (context, obs, argc - 1, argv + 1);
+                 if (!m4_is_symbol_value_text (argv->array[i - 1]))
+                   m4_set_symbol_value_text (new_argv->array[i - 2], "");
+             bp->func (context, obs, argc - 1, new_argv);
+             free (new_argv);
            }
          free (value);
        }
@@ -667,24 +678,36 @@ M4BUILTIN_HANDLER (format)
  **/
 M4BUILTIN_HANDLER (indir)
 {
-  if (!m4_is_symbol_value_text (argv[1]))
-    m4_warn (context, 0, M4ARG (0), _("invalid macro name ignored"));
+  const char *me = M4ARG (0);
+  if (!m4_is_symbol_value_text (argv->array[1 - 1]))
+    m4_warn (context, 0, me, _("invalid macro name ignored"));
   else
     {
       const char *name = M4ARG (1);
       m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, name);
 
       if (symbol == NULL)
-       m4_warn (context, 0, M4ARG (0), _("undefined macro `%s'"), name);
+       m4_warn (context, 0, me, _("undefined macro `%s'"), name);
       else
        {
          int i;
+         /* TODO - make use of $@ reference */
+         m4_macro_args *new_argv;
+         new_argv = xmalloc (offsetof (m4_macro_args, array)
+                             + ((argc - 2) * sizeof (m4_symbol_value *)));
+         new_argv->argc = argc - 1;
+         new_argv->inuse = false;
+         new_argv->argv0 = name;
+         new_argv->arraylen = argc - 2;
+         memcpy (&new_argv->array[0], &argv->array[1],
+                 (argc - 2) * sizeof (m4_symbol_value *));
          if (!m4_symbol_groks_macro (symbol))
            for (i = 2; i < argc; i++)
-             if (!m4_is_symbol_value_text (argv[i]))
-               m4_set_symbol_value_text (argv[i], "");
+             if (!m4_is_symbol_value_text (argv->array[i - 1]))
+               m4_set_symbol_value_text (new_argv->array[i - 2], "");
          m4_macro_call (context, m4_get_symbol_value (symbol), obs,
-                        argc - 1, argv + 1);
+                        argc - 1, new_argv);
+         free (new_argv);
        }
     }
 }
diff --git a/modules/m4.c b/modules/m4.c
index bffb274..7c0d8ed 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -50,7 +50,7 @@
 extern void m4_set_sysval    (int value);
 extern void m4_sysval_flush  (m4 *context, bool report);
 extern void m4_dump_symbols  (m4 *context, m4_dump_symbol_data *data, int argc,
-                             m4_symbol_value **argv, bool complain);
+                             m4_macro_args *argv, bool complain);
 extern const char *m4_expand_ranges (const char *s, m4_obstack *obs);
 extern void m4_make_temp     (m4 *context, m4_obstack *obs, const char *macro,
                              const char *name, bool dir);
@@ -102,7 +102,7 @@ extern void m4_make_temp     (m4 *context, m4_obstack *obs, 
const char *macro,
 typedef intmax_t number;
 typedef uintmax_t unumber;
 
-static void    include         (m4 *context, int argc, m4_symbol_value **argv,
+static void    include         (m4 *context, int argc, m4_macro_args *argv,
                                 bool silent);
 static int     dumpdef_cmp_CB  (const void *s1, const void *s2);
 static void *  dump_symbol_CB  (m4_symbol_table *ignored, const char *name,
@@ -113,7 +113,7 @@ static void numb_obstack    (m4_obstack *obs, number value,
 
 
 /* Generate prototypes for each builtin handler function. */
-#define BUILTIN(handler, macros,  blind, side, min, max) M4BUILTIN(handler)
+#define BUILTIN(handler, macros,  blind, side, min, max) M4BUILTIN (handler)
   builtin_functions
 #undef BUILTIN
 
@@ -122,7 +122,7 @@ static void numb_obstack    (m4_obstack *obs, number value,
 m4_builtin m4_builtin_table[] =
 {
 #define BUILTIN(handler, macros, blind, side, min, max)        \
-  { CONC(builtin_, handler), STR(handler),             \
+  { CONC (builtin_, handler), STR (handler),           \
     ((macros ? M4_BUILTIN_GROKS_MACRO : 0)             \
      | (blind ? M4_BUILTIN_BLIND : 0)                  \
      | (side ? M4_BUILTIN_SIDE_EFFECT : 0)),           \
@@ -160,14 +160,14 @@ M4INIT_HANDLER (m4)
 
 M4BUILTIN_HANDLER (define)
 {
-  if (m4_is_symbol_value_text (argv[1]))
+  if (m4_is_symbol_value_text (argv->array[1 - 1]))
     {
       m4_symbol_value *value = m4_symbol_value_create ();
 
       if (argc == 2)
        m4_set_symbol_value_text (value, xstrdup (""));
       else
-       m4_symbol_value_copy (value, argv[2]);
+       m4_symbol_value_copy (value, argv->array[2 - 1]);
 
       m4_symbol_define (M4SYMTAB, M4ARG (1), value);
     }
@@ -191,14 +191,14 @@ M4BUILTIN_HANDLER (undefine)
 
 M4BUILTIN_HANDLER (pushdef)
 {
-  if (m4_is_symbol_value_text (argv[1]))
+  if (m4_is_symbol_value_text (argv->array[1 - 1]))
     {
       m4_symbol_value *value = m4_symbol_value_create ();
 
       if (argc == 2)
        m4_set_symbol_value_text (value, xstrdup (""));
       else
-       m4_symbol_value_copy (value, argv[2]);
+       m4_symbol_value_copy (value, argv->array[2 - 1]);
 
       m4_symbol_pushdef (M4SYMTAB, M4ARG (1), value);
     }
@@ -248,6 +248,7 @@ M4BUILTIN_HANDLER (ifelse)
 {
   const char *me = M4ARG (0);
   const char *result;
+  int index;
 
   /* The valid ranges of argc for ifelse is discontinuous, we cannot
      rely on the regular mechanisms.  */
@@ -260,14 +261,14 @@ M4BUILTIN_HANDLER (ifelse)
     /* Diagnose excess arguments if 5, 8, 11, etc., actual arguments.  */
     m4_bad_argc (context, argc, me, 0, argc - 2, false);
 
-  argv++;
+  index = 1;
   argc--;
 
   result = NULL;
   while (result == NULL)
 
-    if (strcmp (M4ARG (0), M4ARG (1)) == 0)
-      result = M4ARG (2);
+    if (strcmp (M4ARG (index), M4ARG (index + 1)) == 0)
+      result = M4ARG (index + 2);
 
     else
       switch (argc)
@@ -277,12 +278,12 @@ M4BUILTIN_HANDLER (ifelse)
 
        case 4:
        case 5:
-         result = M4ARG (3);
+         result = M4ARG (index + 3);
          break;
 
        default:
          argc -= 3;
-         argv += 3;
+         index += 3;
        }
 
   obstack_grow (obs, result, strlen (result));
@@ -327,7 +328,7 @@ dump_symbol_CB (m4_symbol_table *ignored, const char *name, 
m4_symbol *symbol,
    symbols, otherwise, only the specified symbols.  */
 void
 m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, int argc,
-                m4_symbol_value **argv, bool complain)
+                m4_macro_args *argv, bool complain)
 {
   assert (obstack_object_size (data->obs) == 0);
   data->size = obstack_room (data->obs) / sizeof (const char *);
@@ -394,15 +395,16 @@ M4BUILTIN_HANDLER (dumpdef)
    macro-definition token on the input stack.  */
 M4BUILTIN_HANDLER (defn)
 {
+  const char *me = M4ARG (0);
   int i;
 
   for (i = 1; i < argc; i++)
     {
-      const char *name = m4_get_symbol_value_text (argv[i]);
+      const char *name = M4ARG (i);
       m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, name);
 
       if (!symbol)
-       m4_warn (context, 0, M4ARG (0), _("undefined macro `%s'"), name);
+       m4_warn (context, 0, me, _("undefined macro `%s'"), name);
       else if (m4_is_symbol_text (symbol))
        m4_shipout_string (context, obs, m4_get_symbol_text (symbol), 0, true);
       else if (m4_is_symbol_func (symbol))
@@ -647,7 +649,7 @@ M4BUILTIN_HANDLER (dnl)
    output argument is quoted with the current quotes.  */
 M4BUILTIN_HANDLER (shift)
 {
-  m4_dump_args (context, obs, argc - 1, argv + 1, ",", true);
+  m4_dump_args (context, obs, 2, argv, ",", true);
 }
 
 /* Change the current quotes.  The function set_quotes () lives in
@@ -676,7 +678,7 @@ M4BUILTIN_HANDLER (changecom)
 /* Generic include function.  Include the file given by the first argument,
    if it exists.  Complain about inaccesible files iff SILENT is false.  */
 static void
-include (m4 *context, int argc, m4_symbol_value **argv, bool silent)
+include (m4 *context, int argc, m4_macro_args *argv, bool silent)
 {
   FILE *fp;
   char *name = NULL;
@@ -806,7 +808,7 @@ M4BUILTIN_HANDLER (mkstemp)
 M4BUILTIN_HANDLER (errprint)
 {
   assert (obstack_object_size (obs) == 0);
-  m4_dump_args (context, obs, argc, argv, " ", false);
+  m4_dump_args (context, obs, 1, argv, " ", false);
   obstack_1grow (obs, '\0');
   m4_sysval_flush (context, false);
   fputs ((char *) obstack_finish (obs), stderr);
@@ -861,7 +863,7 @@ M4BUILTIN_HANDLER (m4wrap)
   if (m4_get_posixly_correct_opt (context))
     m4_shipout_string (context, obs, M4ARG (1), 0, false);
   else
-    m4_dump_args (context, obs, argc, argv, " ", false);
+    m4_dump_args (context, obs, 1, argv, " ", false);
   obstack_1grow (obs, '\0');
   m4_push_wrapup (context, obstack_finish (obs));
 }
diff --git a/modules/m4.h b/modules/m4.h
index 5c14e25..4214aed 100644
--- a/modules/m4.h
+++ b/modules/m4.h
@@ -39,7 +39,7 @@ typedef struct
 typedef void m4_sysval_flush_func (m4 *context, bool report);
 typedef void m4_set_sysval_func (int value);
 typedef void m4_dump_symbols_func (m4 *context, m4_dump_symbol_data *data,
-                                  int argc, m4_symbol_value **argv,
+                                  int argc, m4_macro_args *argv,
                                   bool complain);
 typedef const char *m4_expand_ranges_func (const char *s, m4_obstack *obs);
 typedef void m4_make_temp_func (m4 *context, m4_obstack *obs,
-- 
1.5.3.5

>From ac8845562f0066ef07d74a1f5b0e749eceb19b89 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Fri, 19 Oct 2007 07:43:34 -0600
Subject: [PATCH] Stage 1: convert token_data** into new object.

* m4/gnulib-cache.m4: Import flexmember module.
* src/m4.h (struct macro_arguments, struct token_chain): New
structs.
(builtin_func): Alter signature.
(token_data): Add new TOKEN_COMP alternative.
* src/builtin.c: All builtins changed.
(ARG, dump_args, define_macro, expand_user_macro): Update to use
struct.
* src/debug.c (trace_pre, trace_post): Likewise.
* src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE, format):
Likewise.
* src/macro.c (collect_arguments): Build new struct.
(call_macro, expand_macro): Update to use new struct.

(cherry picked from commit 44f5da7de32ac8f71f26d9e441316fa563db30d6)

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog          |   17 +++++
 m4/gnulib-cache.m4 |    4 +-
 src/builtin.c      |  201 ++++++++++++++++++++++++++++++----------------------
 src/debug.c        |   10 ++--
 src/format.c       |   45 +++++-------
 src/m4.h           |   71 ++++++++++++++-----
 src/macro.c        |   51 ++++++++------
 7 files changed, 243 insertions(+), 156 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 9bbf726..14deb9f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2007-11-24  Eric Blake  <address@hidden>
+
+       Stage 1: convert token_data** into new object.
+       * m4/gnulib-cache.m4: Import flexmember module.
+       * src/m4.h (struct macro_arguments, struct token_chain): New
+       structs.
+       (builtin_func): Alter signature.
+       (token_data): Add new TOKEN_COMP alternative.
+       * src/builtin.c: All builtins changed.
+       (ARG, dump_args, define_macro, expand_user_macro): Update to use
+       struct.
+       * src/debug.c (trace_pre, trace_post): Likewise.
+       * src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE, format):
+       Likewise.
+       * src/macro.c (collect_arguments): Build new struct.
+       (call_macro, expand_macro): Update to use new struct.
+
 2007-11-22  Eric Blake  <address@hidden>
 
        More error messages tied to macro names.
diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4
index 4d1727d..a89650c 100644
--- a/m4/gnulib-cache.m4
+++ b/m4/gnulib-cache.m4
@@ -15,11 +15,11 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --local-dir=local --lib=libm4 
--source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests 
--no-libtool --macro-prefix=M4 assert avltree-oset binary-io clean-temp cloexec 
close-stream closein config-h error fdl fflush fopen-safer free fseeko gendocs 
getopt gnupload gpl-3.0 mkstemp obstack regex stdbool stdint stdlib-safer 
strtol unlocked-io verror version-etc version-etc-fsf xalloc xprintf 
xvasprintf-posix
+#   gnulib-tool --import --dir=. --local-dir=local --lib=libm4 
--source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests 
--no-libtool --macro-prefix=M4 assert avltree-oset binary-io clean-temp cloexec 
close-stream closein config-h error fdl fflush flexmember fopen-safer free 
fseeko gendocs getopt gnupload gpl-3.0 mkstemp obstack regex stdbool stdint 
stdlib-safer strtol unlocked-io verror version-etc version-etc-fsf xalloc 
xprintf xvasprintf-posix
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([local])
-gl_MODULES([assert avltree-oset binary-io clean-temp cloexec close-stream 
closein config-h error fdl fflush fopen-safer free fseeko gendocs getopt 
gnupload gpl-3.0 mkstemp obstack regex stdbool stdint stdlib-safer strtol 
unlocked-io verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix])
+gl_MODULES([assert avltree-oset binary-io clean-temp cloexec close-stream 
closein config-h error fdl fflush flexmember fopen-safer free fseeko gendocs 
getopt gnupload gpl-3.0 mkstemp obstack regex stdbool stdint stdlib-safer 
strtol unlocked-io verror version-etc version-etc-fsf xalloc xprintf 
xvasprintf-posix])
 gl_AVOID([])
 gl_SOURCE_BASE([lib])
 gl_M4_BASE([m4])
diff --git a/src/builtin.c b/src/builtin.c
index cc4e469..eb66465 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -30,14 +30,16 @@
 # include <sys/wait.h>
 #endif
 
-#define ARG(i) (argc > (i) ? TOKEN_DATA_TEXT (argv[i]) : "")
+#define ARG(i)                                                         \
+  ((i) == 0 ? argv->argv0                                              \
+   : argv->argc > (i) ? TOKEN_DATA_TEXT (argv->array[(i) - 1]) : "")
 
 /* Initialization of builtin and predefined macros.  The table
    "builtin_tab" is both used for initialization, and by the "builtin"
    builtin.  */
 
 #define DECLARE(name) \
-  static void name (struct obstack *, int, token_data **)
+  static void name (struct obstack *, int, macro_arguments *)
 
 DECLARE (m4___file__);
 DECLARE (m4___line__);
@@ -602,16 +604,19 @@ shipout_int (struct obstack *obs, int val)
 `----------------------------------------------------------------------*/
 
 static void
-dump_args (struct obstack *obs, int argc, token_data **argv,
+dump_args (struct obstack *obs, int start, macro_arguments *argv,
           const char *sep, bool quoted)
 {
   int i;
+  bool dump_sep = false;
   size_t len = strlen (sep);
 
-  for (i = 1; i < argc; i++)
+  for (i = start; i < argv->argc; i++)
     {
-      if (i > 1)
+      if (dump_sep)
        obstack_grow (obs, sep, len);
+      else
+       dump_sep = true;
       if (quoted)
        obstack_grow (obs, lquote.string, lquote.length);
       obstack_grow (obs, ARG (i), strlen (ARG (i)));
@@ -623,14 +628,15 @@ dump_args (struct obstack *obs, int argc, token_data 
**argv,
 /* The rest of this file is code for builtins and expansion of user
    defined macros.  All the functions for builtins have a prototype as:
 
-       void m4_MACRONAME (struct obstack *obs, int argc, char *argv[]);
+     void m4_MACRONAME (struct obstack *obs, int argc, macro_arguments *argv);
 
-   The function are expected to leave their expansion on the obstack OBS,
-   as an unfinished object.  ARGV is a table of ARGC pointers to the
-   individual arguments to the macro.  Please note that in general
-   argv[argc] != NULL.  */
+   The functions are expected to leave their expansion on the obstack OBS,
+   as an unfinished object.  ARGV is an object representing ARGC pointers
+   to the individual arguments to the macro; the object may be compressed
+   due to references to $@ expansions, so accessors should be used.  Please
+   note that in general argv[argc] != NULL.  */
 
-/* The first section are macros for definining, undefining, examining,
+/* The first section are macros for defining, undefining, examining,
    changing, ... other macros.  */
 
 /*-------------------------------------------------------------------------.
@@ -641,7 +647,7 @@ dump_args (struct obstack *obs, int argc, token_data **argv,
 `-------------------------------------------------------------------------*/
 
 static void
-define_macro (int argc, token_data **argv, symbol_lookup mode)
+define_macro (int argc, macro_arguments *argv, symbol_lookup mode)
 {
   const builtin *bp;
   const char *me = ARG (0);
@@ -649,7 +655,7 @@ define_macro (int argc, token_data **argv, symbol_lookup 
mode)
   if (bad_argc (me, argc, 1, 2))
     return;
 
-  if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
+  if (TOKEN_DATA_TYPE (argv->array[0]) != TOKEN_TEXT)
     {
       m4_warn (0, me, _("invalid macro name ignored"));
       return;
@@ -661,14 +667,14 @@ define_macro (int argc, token_data **argv, symbol_lookup 
mode)
       return;
     }
 
-  switch (TOKEN_DATA_TYPE (argv[2]))
+  switch (TOKEN_DATA_TYPE (argv->array[1]))
     {
     case TOKEN_TEXT:
       define_user_macro (ARG (1), ARG (2), mode);
       break;
 
     case TOKEN_FUNC:
-      bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[2]));
+      bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv->array[1]));
       if (bp == NULL)
        return;
       else
@@ -682,13 +688,13 @@ define_macro (int argc, token_data **argv, symbol_lookup 
mode)
 }
 
 static void
-m4_define (struct obstack *obs, int argc, token_data **argv)
+m4_define (struct obstack *obs, int argc, macro_arguments *argv)
 {
   define_macro (argc, argv, SYMBOL_INSERT);
 }
 
 static void
-m4_undefine (struct obstack *obs, int argc, token_data **argv)
+m4_undefine (struct obstack *obs, int argc, macro_arguments *argv)
 {
   int i;
   if (bad_argc (ARG (0), argc, 1, -1))
@@ -698,13 +704,13 @@ m4_undefine (struct obstack *obs, int argc, token_data 
**argv)
 }
 
 static void
-m4_pushdef (struct obstack *obs, int argc, token_data **argv)
+m4_pushdef (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  define_macro (argc, argv,  SYMBOL_PUSHDEF);
+  define_macro (argc, argv, SYMBOL_PUSHDEF);
 }
 
 static void
-m4_popdef (struct obstack *obs, int argc, token_data **argv)
+m4_popdef (struct obstack *obs, int argc, macro_arguments *argv)
 {
   int i;
   if (bad_argc (ARG (0), argc, 1, -1))
@@ -718,7 +724,7 @@ m4_popdef (struct obstack *obs, int argc, token_data **argv)
 `---------------------*/
 
 static void
-m4_ifdef (struct obstack *obs, int argc, token_data **argv)
+m4_ifdef (struct obstack *obs, int argc, macro_arguments *argv)
 {
   symbol *s;
   const char *result;
@@ -739,10 +745,11 @@ m4_ifdef (struct obstack *obs, int argc, token_data 
**argv)
 }
 
 static void
-m4_ifelse (struct obstack *obs, int argc, token_data **argv)
+m4_ifelse (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *result;
   const char *me;
+  int index;
 
   if (argc == 2)
     return;
@@ -754,14 +761,14 @@ m4_ifelse (struct obstack *obs, int argc, token_data 
**argv)
     /* Diagnose excess arguments if 5, 8, 11, etc., actual arguments.  */
     bad_argc (me, argc, 0, argc - 2);
 
-  argv++;
+  index = 1;
   argc--;
 
   result = NULL;
   while (result == NULL)
 
-    if (strcmp (ARG (0), ARG (1)) == 0)
-      result = ARG (2);
+    if (strcmp (ARG (index), ARG (index + 1)) == 0)
+      result = ARG (index + 2);
 
     else
       switch (argc)
@@ -771,12 +778,12 @@ m4_ifelse (struct obstack *obs, int argc, token_data 
**argv)
 
        case 4:
        case 5:
-         result = ARG (3);
+         result = ARG (index + 3);
          break;
 
        default:
          argc -= 3;
-         argv += 3;
+         index += 3;
        }
 
   obstack_grow (obs, result, strlen (result));
@@ -826,7 +833,7 @@ dumpdef_cmp (const void *s1, const void *s2)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_dumpdef (struct obstack *obs, int argc, token_data **argv)
+m4_dumpdef (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   symbol *s;
@@ -900,7 +907,7 @@ m4_dumpdef (struct obstack *obs, int argc, token_data 
**argv)
 `---------------------------------------------------------------------*/
 
 static void
-m4_builtin (struct obstack *obs, int argc, token_data **argv)
+m4_builtin (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   const builtin *bp;
@@ -908,7 +915,7 @@ m4_builtin (struct obstack *obs, int argc, token_data 
**argv)
 
   if (bad_argc (me, argc, 1, -1))
     return;
-  if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
+  if (TOKEN_DATA_TYPE (argv->array[0]) != TOKEN_TEXT)
     {
       m4_warn (0, me, _("invalid macro name ignored"));
       return;
@@ -921,14 +928,25 @@ m4_builtin (struct obstack *obs, int argc, token_data 
**argv)
   else
     {
       int i;
+      /* TODO make use of $@ reference, instead of copying argv.  */
+      macro_arguments *new_argv = xmalloc (offsetof (macro_arguments, array)
+                                          + ((argc - 2)
+                                             * sizeof (token_data *)));
+      new_argv->argc = argc - 1;
+      new_argv->inuse = false;
+      new_argv->argv0 = name;
+      new_argv->arraylen = argc - 2;
+      memcpy (&new_argv->array[0], &argv->array[1],
+             (argc - 2) * sizeof (token_data *));
       if (!bp->groks_macro_args)
        for (i = 2; i < argc; i++)
-         if (TOKEN_DATA_TYPE (argv[i]) != TOKEN_TEXT)
+         if (TOKEN_DATA_TYPE (new_argv->array[i - 2]) != TOKEN_TEXT)
            {
-             TOKEN_DATA_TYPE (argv[i]) = TOKEN_TEXT;
-             TOKEN_DATA_TEXT (argv[i]) = (char *) "";
+             TOKEN_DATA_TYPE (new_argv->array[i - 2]) = TOKEN_TEXT;
+             TOKEN_DATA_TEXT (new_argv->array[i - 2]) = (char *) "";
            }
-      bp->func (obs, argc - 1, argv + 1);
+      bp->func (obs, argc - 1, new_argv);
+      free (new_argv);
     }
 }
 
@@ -940,7 +958,7 @@ m4_builtin (struct obstack *obs, int argc, token_data 
**argv)
 `------------------------------------------------------------------------*/
 
 static void
-m4_indir (struct obstack *obs, int argc, token_data **argv)
+m4_indir (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   symbol *s;
@@ -948,7 +966,7 @@ m4_indir (struct obstack *obs, int argc, token_data **argv)
 
   if (bad_argc (me, argc, 1, -1))
     return;
-  if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
+  if (TOKEN_DATA_TYPE (argv->array[0]) != TOKEN_TEXT)
     {
       m4_warn (0, me, _("invalid macro name ignored"));
       return;
@@ -961,14 +979,25 @@ m4_indir (struct obstack *obs, int argc, token_data 
**argv)
   else
     {
       int i;
+      /* TODO make use of $@ reference, instead of copying argv.  */
+      macro_arguments *new_argv = xmalloc (offsetof (macro_arguments, array)
+                                          + ((argc - 2)
+                                             * sizeof (token_data *)));
+      new_argv->argc = argc - 1;
+      new_argv->inuse = false;
+      new_argv->argv0 = name;
+      new_argv->arraylen = argc - 2;
+      memcpy (&new_argv->array[0], &argv->array[1],
+             (argc - 2) * sizeof (token_data *));
       if (!SYMBOL_MACRO_ARGS (s))
        for (i = 2; i < argc; i++)
-         if (TOKEN_DATA_TYPE (argv[i]) != TOKEN_TEXT)
+         if (TOKEN_DATA_TYPE (new_argv->array[i - 2]) != TOKEN_TEXT)
            {
-             TOKEN_DATA_TYPE (argv[i]) = TOKEN_TEXT;
-             TOKEN_DATA_TEXT (argv[i]) = (char *) "";
+             TOKEN_DATA_TYPE (new_argv->array[i - 2]) = TOKEN_TEXT;
+             TOKEN_DATA_TEXT (new_argv->array[i - 2]) = (char *) "";
            }
-      call_macro (s, argc - 1, argv + 1, obs);
+      call_macro (s, argc - 1, new_argv, obs);
+      free (new_argv);
     }
 }
 
@@ -979,7 +1008,7 @@ m4_indir (struct obstack *obs, int argc, token_data **argv)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_defn (struct obstack *obs, int argc, token_data **argv)
+m4_defn (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   symbol *s;
@@ -1060,7 +1089,7 @@ m4_defn (struct obstack *obs, int argc, token_data **argv)
 static int sysval;
 
 static void
-m4_syscmd (struct obstack *obs, int argc, token_data **argv)
+m4_syscmd (struct obstack *obs, int argc, macro_arguments *argv)
 {
   if (bad_argc (ARG (0), argc, 1, 1))
     {
@@ -1084,7 +1113,7 @@ m4_syscmd (struct obstack *obs, int argc, token_data 
**argv)
 }
 
 static void
-m4_esyscmd (struct obstack *obs, int argc, token_data **argv)
+m4_esyscmd (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   FILE *pin;
@@ -1114,7 +1143,7 @@ m4_esyscmd (struct obstack *obs, int argc, token_data 
**argv)
 }
 
 static void
-m4_sysval (struct obstack *obs, int argc, token_data **argv)
+m4_sysval (struct obstack *obs, int argc, macro_arguments *argv)
 {
   shipout_int (obs, (sysval == -1 ? 127
                     : (M4SYSVAL_EXITBITS (sysval)
@@ -1127,7 +1156,7 @@ m4_sysval (struct obstack *obs, int argc, token_data 
**argv)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_eval (struct obstack *obs, int argc, token_data **argv)
+m4_eval (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   int32_t value = 0;
@@ -1190,7 +1219,7 @@ m4_eval (struct obstack *obs, int argc, token_data **argv)
 }
 
 static void
-m4_incr (struct obstack *obs, int argc, token_data **argv)
+m4_incr (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   int value;
@@ -1205,7 +1234,7 @@ m4_incr (struct obstack *obs, int argc, token_data **argv)
 }
 
 static void
-m4_decr (struct obstack *obs, int argc, token_data **argv)
+m4_decr (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   int value;
@@ -1228,7 +1257,7 @@ m4_decr (struct obstack *obs, int argc, token_data **argv)
 `-----------------------------------------------------------------------*/
 
 static void
-m4_divert (struct obstack *obs, int argc, token_data **argv)
+m4_divert (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   int i = 0;
@@ -1245,7 +1274,7 @@ m4_divert (struct obstack *obs, int argc, token_data 
**argv)
 `-----------------------------------------------------*/
 
 static void
-m4_divnum (struct obstack *obs, int argc, token_data **argv)
+m4_divnum (struct obstack *obs, int argc, macro_arguments *argv)
 {
   bad_argc (ARG (0), argc, 0, 0);
   shipout_int (obs, current_diversion);
@@ -1259,7 +1288,7 @@ m4_divnum (struct obstack *obs, int argc, token_data 
**argv)
 `-----------------------------------------------------------------------*/
 
 static void
-m4_undivert (struct obstack *obs, int argc, token_data **argv)
+m4_undivert (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   int i;
@@ -1303,7 +1332,7 @@ m4_undivert (struct obstack *obs, int argc, token_data 
**argv)
 `------------------------------------------------------------------------*/
 
 static void
-m4_dnl (struct obstack *obs, int argc, token_data **argv)
+m4_dnl (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
 
@@ -1317,11 +1346,12 @@ m4_dnl (struct obstack *obs, int argc, token_data 
**argv)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_shift (struct obstack *obs, int argc, token_data **argv)
+m4_shift (struct obstack *obs, int argc, macro_arguments *argv)
 {
   if (bad_argc (ARG (0), argc, 1, -1))
     return;
-  dump_args (obs, argc - 1, argv + 1, ",", true);
+  /* TODO push a $@ reference.  */
+  dump_args (obs, 2, argv, ",", true);
 }
 
 /*--------------------------------------------------------------------------.
@@ -1329,13 +1359,13 @@ m4_shift (struct obstack *obs, int argc, token_data 
**argv)
 `--------------------------------------------------------------------------*/
 
 static void
-m4_changequote (struct obstack *obs, int argc, token_data **argv)
+m4_changequote (struct obstack *obs, int argc, macro_arguments *argv)
 {
   bad_argc (ARG (0), argc, 0, 2);
 
   /* Explicit NULL distinguishes between empty and missing argument.  */
   set_quotes ((argc >= 2) ? ARG (1) : NULL,
-            (argc >= 3) ? ARG (2) : NULL);
+             (argc >= 3) ? ARG (2) : NULL);
 }
 
 /*--------------------------------------------------------------------.
@@ -1344,7 +1374,7 @@ m4_changequote (struct obstack *obs, int argc, token_data 
**argv)
 `--------------------------------------------------------------------*/
 
 static void
-m4_changecom (struct obstack *obs, int argc, token_data **argv)
+m4_changecom (struct obstack *obs, int argc, macro_arguments *argv)
 {
   bad_argc (ARG (0), argc, 0, 2);
 
@@ -1361,7 +1391,7 @@ m4_changecom (struct obstack *obs, int argc, token_data 
**argv)
 `-----------------------------------------------------------------------*/
 
 static void
-m4_changeword (struct obstack *obs, int argc, token_data **argv)
+m4_changeword (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
 
@@ -1382,7 +1412,7 @@ m4_changeword (struct obstack *obs, int argc, token_data 
**argv)
 `-------------------------------------------------------------------------*/
 
 static void
-include (int argc, token_data **argv, bool silent)
+include (int argc, macro_arguments *argv, bool silent)
 {
   const char *me = ARG (0);
   FILE *fp;
@@ -1408,7 +1438,7 @@ include (int argc, token_data **argv, bool silent)
 `------------------------------------------------*/
 
 static void
-m4_include (struct obstack *obs, int argc, token_data **argv)
+m4_include (struct obstack *obs, int argc, macro_arguments *argv)
 {
   include (argc, argv, false);
 }
@@ -1418,7 +1448,7 @@ m4_include (struct obstack *obs, int argc, token_data 
**argv)
 `----------------------------------*/
 
 static void
-m4_sinclude (struct obstack *obs, int argc, token_data **argv)
+m4_sinclude (struct obstack *obs, int argc, macro_arguments *argv)
 {
   include (argc, argv, true);
 }
@@ -1462,7 +1492,7 @@ mkstemp_helper (struct obstack *obs, const char *me, 
const char *name)
 }
 
 static void
-m4_maketemp (struct obstack *obs, int argc, token_data **argv)
+m4_maketemp (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
 
@@ -1507,7 +1537,7 @@ m4_maketemp (struct obstack *obs, int argc, token_data 
**argv)
 }
 
 static void
-m4_mkstemp (struct obstack *obs, int argc, token_data **argv)
+m4_mkstemp (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
 
@@ -1521,11 +1551,11 @@ m4_mkstemp (struct obstack *obs, int argc, token_data 
**argv)
 `----------------------------------------*/
 
 static void
-m4_errprint (struct obstack *obs, int argc, token_data **argv)
+m4_errprint (struct obstack *obs, int argc, macro_arguments *argv)
 {
   if (bad_argc (ARG (0), argc, 1, -1))
     return;
-  dump_args (obs, argc, argv, " ", false);
+  dump_args (obs, 1, argv, " ", false);
   obstack_1grow (obs, '\0');
   debug_flush_files ();
   xfprintf (stderr, "%s", (char *) obstack_finish (obs));
@@ -1533,7 +1563,7 @@ m4_errprint (struct obstack *obs, int argc, token_data 
**argv)
 }
 
 static void
-m4___file__ (struct obstack *obs, int argc, token_data **argv)
+m4___file__ (struct obstack *obs, int argc, macro_arguments *argv)
 {
   bad_argc (ARG (0), argc, 0, 0);
   obstack_grow (obs, lquote.string, lquote.length);
@@ -1542,14 +1572,14 @@ m4___file__ (struct obstack *obs, int argc, token_data 
**argv)
 }
 
 static void
-m4___line__ (struct obstack *obs, int argc, token_data **argv)
+m4___line__ (struct obstack *obs, int argc, macro_arguments *argv)
 {
   bad_argc (ARG (0), argc, 0, 0);
   shipout_int (obs, current_line);
 }
 
 static void
-m4___program__ (struct obstack *obs, int argc, token_data **argv)
+m4___program__ (struct obstack *obs, int argc, macro_arguments *argv)
 {
   bad_argc (ARG (0), argc, 0, 0);
   obstack_grow (obs, lquote.string, lquote.length);
@@ -1567,7 +1597,7 @@ m4___program__ (struct obstack *obs, int argc, token_data 
**argv)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_m4exit (struct obstack *obs, int argc, token_data **argv)
+m4_m4exit (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   int exit_code = EXIT_SUCCESS;
@@ -1600,14 +1630,14 @@ m4_m4exit (struct obstack *obs, int argc, token_data 
**argv)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_m4wrap (struct obstack *obs, int argc, token_data **argv)
+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), strlen (ARG (1)));
   else
-    dump_args (obs, argc, argv, " ", false);
+    dump_args (obs, 1, argv, " ", false);
   obstack_1grow (obs, '\0');
   push_wrapup ((char *) obstack_finish (obs));
 }
@@ -1632,7 +1662,7 @@ set_trace (symbol *sym, void *data)
 }
 
 static void
-m4_traceon (struct obstack *obs, int argc, token_data **argv)
+m4_traceon (struct obstack *obs, int argc, macro_arguments *argv)
 {
   symbol *s;
   int i;
@@ -1652,7 +1682,7 @@ m4_traceon (struct obstack *obs, int argc, token_data 
**argv)
 `------------------------------------------------------------------------*/
 
 static void
-m4_traceoff (struct obstack *obs, int argc, token_data **argv)
+m4_traceoff (struct obstack *obs, int argc, macro_arguments *argv)
 {
   symbol *s;
   int i;
@@ -1675,7 +1705,7 @@ m4_traceoff (struct obstack *obs, int argc, token_data 
**argv)
 `----------------------------------------------------------------------*/
 
 static void
-m4_debugmode (struct obstack *obs, int argc, token_data **argv)
+m4_debugmode (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   const char *str = ARG (1);
@@ -1727,7 +1757,7 @@ m4_debugmode (struct obstack *obs, int argc, token_data 
**argv)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_debugfile (struct obstack *obs, int argc, token_data **argv)
+m4_debugfile (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
 
@@ -1748,7 +1778,7 @@ m4_debugfile (struct obstack *obs, int argc, token_data 
**argv)
 `---------------------------------------------*/
 
 static void
-m4_len (struct obstack *obs, int argc, token_data **argv)
+m4_len (struct obstack *obs, int argc, macro_arguments *argv)
 {
   if (bad_argc (ARG (0), argc, 1, 1))
     return;
@@ -1761,7 +1791,7 @@ m4_len (struct obstack *obs, int argc, token_data **argv)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_index (struct obstack *obs, int argc, token_data **argv)
+m4_index (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *haystack;
   const char *needle;
@@ -1801,7 +1831,7 @@ m4_index (struct obstack *obs, int argc, token_data 
**argv)
 `-------------------------------------------------------------------------*/
 
 static void
-m4_substr (struct obstack *obs, int argc, token_data **argv)
+m4_substr (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   int start = 0;
@@ -1885,7 +1915,7 @@ expand_ranges (const char *s, struct obstack *obs)
 `----------------------------------------------------------------------*/
 
 static void
-m4_translit (struct obstack *obs, int argc, token_data **argv)
+m4_translit (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *data;
   const char *from;
@@ -1950,7 +1980,7 @@ m4_translit (struct obstack *obs, int argc, token_data 
**argv)
 `--------------------------------------------------------------*/
 
 static void
-m4_format (struct obstack *obs, int argc, token_data **argv)
+m4_format (struct obstack *obs, int argc, macro_arguments *argv)
 {
   if (bad_argc (ARG (0), argc, 1, -1))
     return;
@@ -2047,7 +2077,7 @@ init_pattern_buffer (struct re_pattern_buffer *buf, 
struct re_registers *regs)
 `------------------------------------------------------------------*/
 
 static void
-m4_regexp (struct obstack *obs, int argc, token_data **argv)
+m4_regexp (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   const char *victim;          /* first argument */
@@ -2117,7 +2147,7 @@ m4_regexp (struct obstack *obs, int argc, token_data 
**argv)
 `------------------------------------------------------------------*/
 
 static void
-m4_patsubst (struct obstack *obs, int argc, token_data **argv)
+m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const char *me = ARG (0);
   const char *victim;          /* first argument */
@@ -2222,7 +2252,7 @@ m4_patsubst (struct obstack *obs, int argc, token_data 
**argv)
 `--------------------------------------------------------------------*/
 
 void
-m4_placeholder (struct obstack *obs, int argc, token_data **argv)
+m4_placeholder (struct obstack *obs, int argc, macro_arguments *argv)
 {
   m4_warn (0, NULL, _("builtin `%s' requested by frozen file not found"),
           ARG (0));
@@ -2238,7 +2268,7 @@ m4_placeholder (struct obstack *obs, int argc, token_data 
**argv)
 
 void
 expand_user_macro (struct obstack *obs, symbol *sym,
-                  int argc, token_data **argv)
+                  int argc, macro_arguments *argv)
 {
   const char *text;
   int i;
@@ -2263,7 +2293,7 @@ expand_user_macro (struct obstack *obs, symbol *sym,
          else
            {
              for (i = 0; isdigit (to_uchar (*text)); text++)
-               i = i*10 + (*text - '0');
+               i = i * 10 + (*text - '0');
            }
          if (i < argc)
            obstack_grow (obs, ARG (i), strlen (ARG (i)));
@@ -2276,7 +2306,8 @@ expand_user_macro (struct obstack *obs, symbol *sym,
 
        case '*':               /* all arguments */
        case '@':               /* ... same, but quoted */
-         dump_args (obs, argc, argv, ",", *text == '@');
+         /* TODO push a $@ reference.  */
+         dump_args (obs, 1, argv, ",", *text == '@');
          text++;
          break;
 
diff --git a/src/debug.c b/src/debug.c
index c22a482..e5bd280 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -362,7 +362,7 @@ trace_prepre (const char *name, int id)
 `-----------------------------------------------------------------------*/
 
 void
-trace_pre (const char *name, int id, int argc, token_data **argv)
+trace_pre (const char *name, int id, int argc, macro_arguments *argv)
 {
   int i;
   const builtin *bp;
@@ -379,14 +379,14 @@ trace_pre (const char *name, int id, int argc, token_data 
**argv)
          if (i != 1)
            trace_format (", ");
 
-         switch (TOKEN_DATA_TYPE (argv[i]))
+         switch (TOKEN_DATA_TYPE (argv->array[i - 1]))
            {
            case TOKEN_TEXT:
-             trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv[i]));
+             trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv->array[i - 1]));
              break;
 
            case TOKEN_FUNC:
-             bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[i]));
+             bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv->array[i - 1]));
              if (bp == NULL)
                {
                  assert (!"trace_pre");
@@ -417,7 +417,7 @@ trace_pre (const char *name, int id, int argc, token_data 
**argv)
 `-------------------------------------------------------------------*/
 
 void
-trace_post (const char *name, int id, int argc, token_data **argv,
+trace_post (const char *name, int id, int argc, macro_arguments *argv,
            const char *expanded)
 {
   if (debug_level & DEBUG_TRACE_CALL)
diff --git a/src/format.c b/src/format.c
index 96ac562..4c2b60a 100644
--- a/src/format.c
+++ b/src/format.c
@@ -27,21 +27,17 @@
 /* Simple varargs substitute.  We assume int and unsigned int are the
    same size; likewise for long and unsigned long.  */
 
-#define ARG_INT(argc, argv) \
-       ((argc == 0) ? 0 : \
-        (--argc, argv++, atoi (TOKEN_DATA_TEXT (argv[-1]))))
+#define ARG_INT(i, argc, argv)                                         \
+       ((i == argc) ? 0 : atoi (TOKEN_DATA_TEXT (argv->array[i++ - 1])))
 
-#define ARG_LONG(argc, argv) \
-       ((argc == 0) ? 0 : \
-        (--argc, argv++, atol (TOKEN_DATA_TEXT (argv[-1]))))
+#define ARG_LONG(i, argc, argv)                                                
\
+       ((i == argc) ? 0L : atol (TOKEN_DATA_TEXT (argv->array[i++ - 1])))
 
-#define ARG_STR(argc, argv) \
-       ((argc == 0) ? "" : \
-        (--argc, argv++, TOKEN_DATA_TEXT (argv[-1])))
+#define ARG_STR(i, argc, argv)                                         \
+       ((i == argc) ? "" : TOKEN_DATA_TEXT (argv->array[i++ - 1]))
 
-#define ARG_DOUBLE(argc, argv) \
-       ((argc == 0) ? 0 : \
-        (--argc, argv++, atof (TOKEN_DATA_TEXT (argv[-1]))))
+#define ARG_DOUBLE(i, argc, argv)                                      \
+       ((i == argc) ? 0.0 : atof (TOKEN_DATA_TEXT (argv->array[i++ - 1])))
 
 
 /*------------------------------------------------------------------.
@@ -53,14 +49,15 @@
 `------------------------------------------------------------------*/
 
 void
-format (struct obstack *obs, int argc, token_data **argv)
+format (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  const char *me = TOKEN_DATA_TEXT (argv[0]);
+  const char *me = argv->argv0;
   const char *f;                       /* format control string */
   const char *fmt;                     /* position within f */
   char fstart[] = "%'+- 0#*.*hhd";     /* current format spec */
   char *p;                             /* position within fstart */
   unsigned char c;                     /* a simple character */
+  int index = 1;                       /* index within argc used so far */
 
   /* Flags.  */
   char flags;                          /* flags to use in fstart */
@@ -88,9 +85,7 @@ format (struct obstack *obs, int argc, token_data **argv)
   char *str;                   /* malloc'd buffer of formatted text */
   enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
 
-  argv++;
-  argc--;
-  f = fmt = ARG_STR (argc, argv);
+  f = fmt = ARG_STR (index, argc, argv);
   memset (ok, 0, sizeof ok);
   for (;;)
     {
@@ -175,7 +170,7 @@ format (struct obstack *obs, int argc, token_data **argv)
       *p++ = '*';
       if (*fmt == '*')
        {
-         width = ARG_INT (argc, argv);
+         width = ARG_INT (index, argc, argv);
          fmt++;
        }
       else
@@ -195,7 +190,7 @@ format (struct obstack *obs, int argc, token_data **argv)
          ok['c'] = 0;
          if (*(++fmt) == '*')
            {
-             prec = ARG_INT (argc, argv);
+             prec = ARG_INT (index, argc, argv);
              ++fmt;
            }
          else
@@ -280,27 +275,27 @@ format (struct obstack *obs, int argc, token_data **argv)
       switch (datatype)
        {
        case CHAR:
-         str = xasprintf (fstart, width, ARG_INT(argc, argv));
+         str = xasprintf (fstart, width, ARG_INT (index, argc, argv));
          break;
 
        case INT:
-         str = xasprintf (fstart, width, prec, ARG_INT(argc, argv));
+         str = xasprintf (fstart, width, prec, ARG_INT (index, argc, argv));
          break;
 
        case LONG:
-         str = xasprintf (fstart, width, prec, ARG_LONG(argc, argv));
+         str = xasprintf (fstart, width, prec, ARG_LONG (index, argc, argv));
          break;
 
        case DOUBLE:
-         str = xasprintf (fstart, width, prec, ARG_DOUBLE(argc, argv));
+         str = xasprintf (fstart, width, prec, ARG_DOUBLE (index, argc, argv));
          break;
 
        case STR:
-         str = xasprintf (fstart, width, prec, ARG_STR(argc, argv));
+         str = xasprintf (fstart, width, prec, ARG_STR (index, argc, argv));
          break;
 
        default:
-         abort();
+         abort ();
        }
 
       /* NULL was returned on failure, such as invalid format string.
diff --git a/src/m4.h b/src/m4.h
index 4f1fa1f..94276e9 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -89,7 +89,8 @@ typedef struct string STRING;
 
 /* Those must come first.  */
 typedef struct token_data token_data;
-typedef void builtin_func (struct obstack *, int, token_data **);
+typedef struct macro_arguments macro_arguments;
+typedef void builtin_func (struct obstack *, int, macro_arguments *);
 
 /* Gnulib's stdbool doesn't work with bool bitfields.  For nicer
    debugging, use bool when we know it works, but use the more
@@ -103,14 +104,14 @@ typedef unsigned int bool_bitfield;
 /* Take advantage of GNU C compiler source level optimization hints,
    using portable macros.  */
 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 6)
-#  define M4_GNUC_ATTRIBUTE(args)      __attribute__(args)
+#  define M4_GNUC_ATTRIBUTE(args)      __attribute__ (args)
 #else
 #  define M4_GNUC_ATTRIBUTE(args)
 #endif  /* __GNUC__ */
 
-#define M4_GNUC_UNUSED         M4_GNUC_ATTRIBUTE((__unused__))
+#define M4_GNUC_UNUSED         M4_GNUC_ATTRIBUTE ((__unused__))
 #define M4_GNUC_PRINTF(fmt, arg)                       \
-  M4_GNUC_ATTRIBUTE((__format__ (__printf__, fmt, arg)))
+  M4_GNUC_ATTRIBUTE ((__format__ (__printf__, fmt, arg)))
 
 /* File: m4.c  --- global definitions.  */
 
@@ -132,12 +133,13 @@ extern const char *user_word_regexp;      /* -W */
 extern int retcode;
 extern const char *program_name;
 
-void m4_error (int, int, const char *, const char *, ...) M4_GNUC_PRINTF(4, 5);
+void m4_error (int, int, const char *, const char *, ...)
+  M4_GNUC_PRINTF (4, 5);
 void m4_error_at_line (int, int, const char *, int, const char *,
-                      const char *, ...) M4_GNUC_PRINTF(6, 7);
-void m4_warn (int, const char *, const char *, ...) M4_GNUC_PRINTF(3, 4);
+                      const char *, ...) M4_GNUC_PRINTF (6, 7);
+void m4_warn (int, const char *, const char *, ...) M4_GNUC_PRINTF (3, 4);
 void m4_warn_at_line (int, const char *, int, const char *,
-                     const char *, ...) M4_GNUC_PRINTF(5, 6);
+                     const char *, ...) M4_GNUC_PRINTF (5, 6);
 
 #ifdef USE_STACKOVF
 void setup_stackovf_trap (char *const *, char *const *,
@@ -235,11 +237,13 @@ bool debug_set_output (const char *, const char *);
 void debug_message_prefix (void);
 
 void trace_prepre (const char *, int);
-void trace_pre (const char *, int, int, token_data **);
-void trace_post (const char *, int, int, token_data **, const char *);
+void trace_pre (const char *, int, int, macro_arguments *);
+void trace_post (const char *, int, int, macro_arguments *, const char *);
 
 /* File: input.c  --- lexical definitions.  */
 
+typedef struct token_chain token_chain;
+
 /* Various different token types.  */
 enum token_type
 {
@@ -256,9 +260,19 @@ enum token_type
 /* The data for a token, a macro argument, and a macro definition.  */
 enum token_data_type
 {
-  TOKEN_VOID,
-  TOKEN_TEXT,
-  TOKEN_FUNC
+  TOKEN_VOID, /* Token still being constructed, u is invalid.  */
+  TOKEN_TEXT, /* Straight text, u.u_t is valid.  */
+  TOKEN_FUNC, /* Builtin function definition, u.func is valid.  */
+  TOKEN_COMP  /* Composite argument, u.chain is valid.  */
+};
+
+/* Composite tokens are built of a linked list of chains.  */
+struct token_chain
+{
+  token_chain *next; /* Pointer to next link of chain.  */
+  char *str; /* NUL-terminated string if text, else NULL.  */
+  macro_arguments *argv; /* Reference to earlier address@hidden  */
+  unsigned int index; /* Index within argv to start reading from.  */
 };
 
 struct token_data
@@ -275,10 +289,31 @@ struct token_data
        }
       u_t;
       builtin_func *func;
+
+      /* Composite text: a linked list of straight text and $@
+        placeholders.  */
+      token_chain *chain;
     }
   u;
 };
 
+struct macro_arguments
+{
+  /* Number of arguments owned by this object, may be larger than
+     arraylen since the array can refer to multiple arguments via a
+     single $@ reference.  */
+  unsigned int argc;
+  /* False unless the macro expansion refers to $@, determines whether
+     this object can be freed at end of macro expansion or must wait
+     until next byte read from file.  */
+  bool inuse;
+  const char *argv0; /* The macro name being expanded.  */
+  size_t arraylen; /* True length of allocated elements in array.  */
+  /* Used as a variable-length array, storing information about each
+     argument.  */
+  token_data *array[FLEXIBLE_ARRAY_MEMBER];
+};
+
 #define TOKEN_DATA_TYPE(Td)            ((Td)->type)
 #define TOKEN_DATA_TEXT(Td)            ((Td)->u.u_t.text)
 #ifdef ENABLE_CHANGEWORD
@@ -358,7 +393,7 @@ struct symbol
   int pending_expansions;
 
   char *name;
-  token_data data;
+  token_data data;  /* Type should be only TOKEN_TEXT or TOKEN_FUNC.  */
 };
 
 #define SYMBOL_NEXT(S)         ((S)->next)
@@ -391,7 +426,7 @@ void hack_all_symbols (hack_symbol *, void *);
 extern int expansion_level;
 
 void expand_input (void);
-void call_macro (symbol *, int, token_data **, struct obstack *);
+void call_macro (symbol *, int, macro_arguments *, struct obstack *);
 
 /* File: builtin.c  --- builtins.  */
 
@@ -427,8 +462,8 @@ void set_macro_sequence (const char *);
 void free_regex (void);
 void define_user_macro (const char *, const char *, symbol_lookup);
 void undivert_all (void);
-void expand_user_macro (struct obstack *, symbol *, int, token_data **);
-void m4_placeholder (struct obstack *, int, token_data **);
+void expand_user_macro (struct obstack *, symbol *, int, macro_arguments *);
+void m4_placeholder (struct obstack *, int, macro_arguments *);
 void init_pattern_buffer (struct re_pattern_buffer *, struct re_registers *);
 const char *ntoa (int32_t, int);
 
@@ -448,7 +483,7 @@ bool evaluate (const char *, const char *, int32_t *);
 
 /* File: format.c  --- printf like formatting.  */
 
-void format (struct obstack *, int, token_data **);
+void format (struct obstack *, int, macro_arguments *);
 
 /* File: freeze.c --- frozen state files.  */
 
diff --git a/src/macro.c b/src/macro.c
index 8a678d4..d2f2cb7 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -241,19 +241,22 @@ expand_argument (struct obstack *obs, token_data *argp, 
const char *caller)
 | on the obstack ARGPTR.                                                  |
 `-------------------------------------------------------------------------*/
 
-static void
-collect_arguments (symbol *sym, struct obstack *argptr,
+static macro_arguments *
+collect_arguments (symbol *sym, struct obstack *argptr, unsigned argv_base,
                   struct obstack *arguments)
 {
   token_data td;
   token_data *tdp;
   bool more_args;
   bool groks_macro_args = SYMBOL_MACRO_ARGS (sym);
+  macro_arguments args;
+  macro_arguments *argv;
 
-  TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
-  TOKEN_DATA_TEXT (&td) = SYMBOL_NAME (sym);
-  tdp = (token_data *) obstack_copy (arguments, &td, sizeof td);
-  obstack_ptr_grow (argptr, tdp);
+  args.argc = 1;
+  args.inuse = false;
+  args.argv0 = SYMBOL_NAME (sym);
+  args.arraylen = 0;
+  obstack_grow (argptr, &args, offsetof (macro_arguments, array));
 
   if (peek_token () == TOKEN_OPEN)
     {
@@ -269,9 +272,15 @@ collect_arguments (symbol *sym, struct obstack *argptr,
            }
          tdp = (token_data *) obstack_copy (arguments, &td, sizeof td);
          obstack_ptr_grow (argptr, tdp);
+         args.arraylen++;
+         args.argc++;
        }
       while (more_args);
     }
+  argv = (macro_arguments *) ((char *) obstack_base (argptr) + argv_base);
+  argv->argc = args.argc;
+  argv->arraylen = args.arraylen;
+  return argv;
 }
 
 
@@ -285,13 +294,13 @@ collect_arguments (symbol *sym, struct obstack *argptr,
 `------------------------------------------------------------------------*/
 
 void
-call_macro (symbol *sym, int argc, token_data **argv,
-                struct obstack *expansion)
+call_macro (symbol *sym, int argc, macro_arguments *argv,
+           struct obstack *expansion)
 {
   switch (SYMBOL_TYPE (sym))
     {
     case TOKEN_FUNC:
-      (*SYMBOL_FUNC (sym)) (expansion, argc, argv);
+      SYMBOL_FUNC (sym) (expansion, argc, argv);
       break;
 
     case TOKEN_TEXT:
@@ -319,8 +328,8 @@ expand_macro (symbol *sym)
 {
   struct obstack arguments;    /* Alternate obstack if argc_stack is busy.  */
   unsigned argv_base;          /* Size of argv_stack on entry.  */
-  bool use_argc_stack = true;  /* Whether argc_stack is safe.  */
-  token_data **argv;
+  void *argc_start;            /* Start of argc_stack, else NULL if unsafe.  */
+  macro_arguments *argv;
   int argc;
   struct obstack *expansion;
   const char *expanded;
@@ -357,18 +366,17 @@ expand_macro (symbol *sym)
         outer invocation has an unfinished argument being
         collected.  */
       obstack_init (&arguments);
-      use_argc_stack = false;
+      argc_start = NULL;
     }
+  else
+    argc_start = obstack_finish (&argc_stack);
 
   if (traced && (debug_level & DEBUG_TRACE_CALL))
     trace_prepre (SYMBOL_NAME (sym), my_call_id);
 
-  collect_arguments (sym, &argv_stack,
-                    use_argc_stack ? &argc_stack : &arguments);
-
-  argc = ((obstack_object_size (&argv_stack) - argv_base)
-         / sizeof (token_data *));
-  argv = (token_data **) ((char *) obstack_base (&argv_stack) + argv_base);
+  argv = collect_arguments (sym, &argv_stack, argv_base,
+                           argc_start ? &argc_stack : &arguments);
+  argc = argv->argc;
 
   loc_close_file = current_file;
   loc_close_line = current_line;
@@ -394,9 +402,10 @@ expand_macro (symbol *sym)
   if (SYMBOL_DELETED (sym))
     free_symbol (sym);
 
-  if (use_argc_stack)
-    obstack_free (&argc_stack, argv[0]);
+  /* TODO pay attention to argv->inuse, in case someone is depending on 
address@hidden  */
+  if (argc_start)
+    obstack_free (&argc_stack, argc_start);
   else
     obstack_free (&arguments, NULL);
-  obstack_blank (&argv_stack, -argc * sizeof (token_data *));
+  obstack_blank (&argv_stack, argv_base - obstack_object_size (&argv_stack));
 }
-- 
1.5.3.5


reply via email to

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