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

Eric Blake <[email protected]>
Newsgroups gmane.comp.gnu.m4.patches
Message-ID <[email protected]>
-----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  <[email protected]>

	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             [email protected]
-----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-----

_______________________________________________
M4-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/m4-patches
m4.patch297 (text/plain, 77.9 KB)
From 14b2ffbb82eb22cd36b1703a5501ee87ea51c45c Mon Sep 17 00:00:00 2001
From: Eric Blake <[email protected]>
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 <[email protected]>
---
 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  <[email protected]>
 
+	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 $@.  */
+  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 <[email protected]>
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 <[email protected]>
---
 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  <[email protected]>
+
+	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  <[email protected]>
 
 	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 $@.  */
+  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 $@.  */
+  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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.