[SCM] GNU M4 source repository branch, branch-1_4, updated. branch-cvs-readonly-58-gd59ecd1

"Eric Blake" <[email protected]>
Newsgroups gmane.comp.gnu.m4.cvs
Message-ID <[email protected]>
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU M4 source repository".

http://git.sv.gnu.org/gitweb/?p=m4.git;a=commitdiff;h=d59ecd1edf7a7f56a4f15e2a378a7871a746bc7f

The branch, branch-1_4 has been updated
       via  d59ecd1edf7a7f56a4f15e2a378a7871a746bc7f (commit)
      from  5ca3ef321b2a9328cab37481ebcbdd80c0789e15 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit d59ecd1edf7a7f56a4f15e2a378a7871a746bc7f
Author: Eric Blake <[email protected]>
Date:   Tue Nov 13 06:55:27 2007 -0700

    Stage 16: cache quotes and improve arg_print.
    
    * src/m4.h (push_wrapup_init, push_wrapup_finish, quote_cache)
    (func_print): New prototypes.
    (arg_print): Adjust prototype.
    * src/builtin.h (func_print): New function.
    (define_user_macro): Slight cleanup.
    (dump_args): Delete, no longer used.
    (m4_errprint): Use arg_print.
    (m4_m4wrap): Handle embedded NUL.
    * src/debug.c (trace_pre): Use arg_print.
    * src/input.c (cached_quote): New variable.
    (push_wrapup): Split...
    (push_wrapup_init, push_wrapup_finish): ...into these.
    (input_print): Use arg_print.
    (quote_cache): New function.
    (pop_input, next_char_1, append_quote_token, set_quote_age):
    Adjust users.
    * src/macro.c (arg_text, make_argv_ref_token): Adjust users.
    (arg_print): Add parameters.
    * examples/null.m4: Test for NUL in m4wrap.
    * examples/null.out: Update expected output.
    * doc/m4.texinfo (Debug Levels): Test --arglength truncation.
    
    (cherry picked from commit 44740d89961c48b712562dfc650dc0cb57898aa0)
    
    Signed-off-by: Eric Blake <[email protected]>

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

Summary of changes:
 ChangeLog         |   28 +++++++++++
 doc/m4.texinfo    |   26 ++++++++++-
 examples/null.m4  |  Bin 5764 -> 5747 bytes
 examples/null.out |  Bin 400 -> 402 bytes
 src/builtin.c     |   91 +++++++++++++++++-------------------
 src/debug.c       |   38 ++-------------
 src/input.c       |  132 ++++++++++++++++++++++++++++++++++++++++------------
 src/m4.h          |    8 ++-
 src/macro.c       |   82 +++++++++++++++++++--------------
 9 files changed, 254 insertions(+), 151 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0f4e496..6578264 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,33 @@
 2008-02-21  Eric Blake  <[email protected]>
 
+	Stage 16: cache quotes and improve arg_print.
+	Cache rather than always copying quotes when pushing $@ refs; in
+	particular, reconstruct single-byte quotes on the fly.  Allow NUL
+	through m4wrap.  Improve sharing of code that prints arguments.
+	Memory impact: slight improvement, due to cached quotes.
+	Speed impact: slight improvement, due to less copying.
+	* src/m4.h (push_wrapup_init, push_wrapup_finish, quote_cache)
+	(func_print): New prototypes.
+	(arg_print): Adjust prototype.
+	* src/builtin.h (func_print): New function.
+	(define_user_macro): Slight cleanup.
+	(dump_args): Delete, no longer used.
+	(m4_errprint): Use arg_print.
+	(m4_m4wrap): Handle embedded NUL.
+	* src/debug.c (trace_pre): Use arg_print.
+	* src/input.c (cached_quote): New variable.
+	(push_wrapup): Split...
+	(push_wrapup_init, push_wrapup_finish): ...into these.
+	(input_print): Use arg_print.
+	(quote_cache): New function.
+	(pop_input, next_char_1, append_quote_token, set_quote_age):
+	Adjust users.
+	* src/macro.c (arg_text, make_argv_ref_token): Adjust users.
+	(arg_print): Add parameters.
+	* examples/null.m4: Test for NUL in m4wrap.
+	* examples/null.out: Update expected output.
+	* doc/m4.texinfo (Debug Levels): Test --arglength truncation.
+
 	Fix out-of-bounds read for sanitized macro names, from 2008-02-06.
 	* src/m4.c (m4_verror_at_line): Properly terminate the string.
 	Reported by Ralf Wildenhues.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 56445c0..2a549dd 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -3466,7 +3466,8 @@ following:
 In trace output, show the actual arguments that were collected before
 invoking the macro.  This applies to all macro calls if the @samp{t}
 flag is used, otherwise only the macros covered by calls of
-@code{traceon}.
+@code{traceon}.  Arguments are subject to length truncation specified by
+the command line option @option{--arglength} (or @option{-l}).
 
 @item c
 In trace output, show several trace lines for each macro call.  A line
@@ -3477,7 +3478,9 @@ after the call has completed.
 @item e
 In trace output, show the expansion of each macro call, if it is not
 void.  This applies to all macro calls if the @samp{t} flag is used,
-otherwise only the macros covered by calls of @code{traceon}.
+otherwise only the macros covered by calls of @code{traceon}.  The
+expansion is subject to length truncation specified by the command line
+option @option{--arglength} (or @option{-l}).
 
 @item f
 In debug and trace output, include the name of the current input file in
@@ -3557,6 +3560,25 @@ foo
 @result{}FOO
 @end example
 
+The following example demonstrates the behavior of length truncation,
+when specified on the command line.  Note that each argument and the
+final result are individually truncated.  Also, the special tokens for
+builtin functions are not truncated.
+
+@comment options: -l6
+@example
+$ @kbd{m4 -d -l 6}
+define(`echo', `$@@')debugmode(`+t')
+@result{}
+echo(`1', `long string')
+@error{}m4trace: -1- echo(`1', `long s...') -> ``1',`l...'
+@result{}1,long string
+indir(`echo', defn(`changequote'))
+@error{}m4trace: -2- defn(`change...')
+@error{}m4trace: -1- indir(`echo', <changequote>) -> ``''
+@result{}
+@end example
+
 @node Debug Output
 @section Saving debugging output
 
diff --git a/examples/null.m4 b/examples/null.m4
index 2632522..79f4715 100644
Binary files a/examples/null.m4 and b/examples/null.m4 differ
diff --git a/examples/null.out b/examples/null.out
index c42e03c..aca4b78 100644
Binary files a/examples/null.out and b/examples/null.out differ
diff --git a/src/builtin.c b/src/builtin.c
index d4a0fee..09322ea 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -198,6 +198,28 @@ find_builtin_by_name (const char *name)
       return bp;
   return bp + 1;
 }
+
+/*------------------------------------------------------------------.
+| Print a representation of FUNC to OBS.  If FLATTEN, output QUOTES |
+| around an empty string instead.                                   |
+`------------------------------------------------------------------*/
+void
+func_print (struct obstack *obs, const builtin *func, bool flatten,
+	    const string_pair *quotes)
+{
+  assert (func);
+  if (flatten && quotes)
+    {
+      obstack_grow (obs, quotes->str1, quotes->len1);
+      obstack_grow (obs, quotes->str2, quotes->len2);
+    }
+  else if (!flatten)
+    {
+      obstack_1grow (obs, '<');
+      obstack_grow (obs, func->name, strlen (func->name));
+      obstack_1grow (obs, '>');
+    }
+}
 
 /*-------------------------------------------------------------------------.
 | Install a builtin macro with name NAME, bound to the C function given in |
@@ -398,14 +420,15 @@ free_regex (void)
       }
 }
 
-/*-------------------------------------------------------------------------.
-| Define a predefined or user-defined macro, with name NAME, and expansion |
-| TEXT.  MODE destinguishes between the "define" and the "pushdef" case.   |
-| It is also used from main ().						   |
-`-------------------------------------------------------------------------*/
+/*-----------------------------------------------------------------.
+| Define a predefined or user-defined macro, with name NAME of     |
+| length NAME_LEN, and expansion TEXT.  MODE is SYMBOL_INSERT for  |
+| "define" or SYMBOL_PUSHDEF for "pushdef".  This function is also |
+| used from main ().                                               |
+`-----------------------------------------------------------------*/
 
 void
-define_user_macro (const char *name, size_t len, const char *text,
+define_user_macro (const char *name, size_t name_len, const char *text,
 		   symbol_lookup mode)
 {
   symbol *s;
@@ -422,24 +445,23 @@ define_user_macro (const char *name, size_t len, const char *text,
   if (macro_sequence_inuse && text)
     {
       regoff_t offset = 0;
-      len = strlen (defn);
+      struct re_registers *regs = &macro_sequence_regs;
+      size_t len = strlen (defn);
 
       while (offset < len
 	     && (offset = re_search (&macro_sequence_buf, defn, len, offset,
-				     len - offset, &macro_sequence_regs)) >= 0)
+				     len - offset, regs)) >= 0)
 	{
 	  /* Skip empty matches.  */
-	  if (macro_sequence_regs.start[0] == macro_sequence_regs.end[0])
+	  if (regs->start[0] == regs->end[0])
 	    offset++;
 	  else
 	    {
-	      char tmp;
-	      offset = macro_sequence_regs.end[0];
-	      tmp = defn[offset];
-	      defn[offset] = '\0';
-	      m4_warn (0, NULL, _("definition of `%s' contains sequence `%s'"),
-		       name, defn + macro_sequence_regs.start[0]);
-	      defn[offset] = tmp;
+	      offset = regs->end[0];
+	      m4_warn (0, NULL,
+		       _("definition of `%s' contains sequence `%.*s'"),
+		       name, regs->end[0] - regs->start[0],
+		       defn + regs->start[0]);
 	    }
 	}
       if (offset == -2)
@@ -599,34 +621,6 @@ shipout_int (struct obstack *obs, int val)
   obstack_grow (obs, s, strlen (s));
 }
 
-/*------------------------------------------------------------------.
-| Print arguments from the table ARGV to obstack OBS, starting with |
-| START, separated by SEP, and quoted by the current quotes if	    |
-| QUOTED is true.						    |
-`------------------------------------------------------------------*/
-
-static void
-dump_args (struct obstack *obs, int start, macro_arguments *argv,
-	   const char *sep, bool quoted)
-{
-  unsigned int i;
-  bool dump_sep = false;
-  size_t len = strlen (sep);
-  unsigned int argc = arg_argc (argv);
-
-  for (i = start; i < argc; i++)
-    {
-      if (dump_sep)
-	obstack_grow (obs, sep, len);
-      else
-	dump_sep = true;
-      if (quoted)
-	obstack_grow (obs, curr_quote.str1, curr_quote.len1);
-      obstack_grow (obs, ARG (i), ARG_LEN (i));
-      if (quoted)
-	obstack_grow (obs, curr_quote.str2, curr_quote.len2);
-    }
-}
 
 /* The rest of this file is code for builtins and expansion of user
    defined macros.  All the functions for builtins have a prototype as:
@@ -1518,7 +1512,7 @@ m4_errprint (struct obstack *obs, int argc, macro_arguments *argv)
 
   if (bad_argc (ARG (0), argc, 1, -1))
     return;
-  dump_args (obs, 1, argv, " ", false);
+  arg_print (obs, argv, 1, NULL, true, " ", NULL, false);
   debug_flush_files ();
   len = obstack_object_size (obs);
   /* The close_stdin module makes it safe to skip checking the return
@@ -1599,12 +1593,13 @@ m4_m4wrap (struct obstack *obs, int argc, macro_arguments *argv)
 {
   if (bad_argc (ARG (0), argc, 1, -1))
     return;
+  obs = push_wrapup_init ();
   if (no_gnu_extensions)
     obstack_grow (obs, ARG (1), ARG_LEN (1));
   else
-    dump_args (obs, 1, argv, " ", false);
-  obstack_1grow (obs, '\0');
-  push_wrapup ((char *) obstack_finish (obs));
+    /* TODO - allow builtins, rather than always flattening.  */
+    arg_print (obs, argv, 1, NULL, true, " ", NULL, false);
+  push_wrapup_finish ();
 }
 
 /* Enable tracing of all specified macros, or all, if none is specified.
diff --git a/src/debug.c b/src/debug.c
index d6b2ddc..737ee52 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -359,44 +359,16 @@ trace_prepre (const char *name, int id)
 void
 trace_pre (const char *name, int id, macro_arguments *argv)
 {
-  int i;
-  const builtin *bp;
-  int argc = arg_argc (argv);
-
   trace_header (id);
   trace_format ("%s", name);
 
-  if (argc > 1 && (debug_level & DEBUG_TRACE_ARGS))
+  if (arg_argc (argv) > 1 && (debug_level & DEBUG_TRACE_ARGS))
     {
+      int len = max_debug_argument_length;
       trace_format ("(");
-
-      for (i = 1; i < argc; i++)
-	{
-	  if (i != 1)
-	    trace_format (", ");
-
-	  switch (arg_type (argv, i))
-	    {
-	    case TOKEN_TEXT:
-	      trace_format ("%l%S%r", ARG (i));
-	      break;
-
-	    case TOKEN_FUNC:
-	      bp = find_builtin_by_addr (arg_func (argv, i));
-	      if (bp == NULL)
-		{
-		  assert (!"trace_pre");
-		  abort ();
-		}
-	      trace_format ("<%s>", bp->name);
-	      break;
-
-	    default:
-	      assert (!"trace_pre");
-	      abort ();
-	    }
-
-	}
+      arg_print (&trace, argv, 1,
+		 (debug_level & DEBUG_TRACE_QUOTE) ? &curr_quote : NULL,
+		 false, ", ", &len, true);
       trace_format (")");
     }
 
diff --git a/src/input.c b/src/input.c
index 5c3b345..bbd50f4 100644
--- a/src/input.c
+++ b/src/input.c
@@ -42,14 +42,14 @@
    loops (e.g. "define(`f',`m4wrap(`f')')f"), without memory leaks.
 
    Pushing new input on the input stack is done by push_file (),
-   push_string (), push_wrapup () (for wrapup text), and push_macro ()
-   (for macro definitions).  Because macro expansion needs direct
-   access to the current input obstack (for optimization), push_string
-   () is split in two functions, push_string_init (), which returns a
-   pointer to the current input stack, and push_string_finish (),
-   which returns a pointer to the final text.  The input_block *next
-   is used to manage the coordination between the different push
-   routines.
+   push_string (), push_wrapup_init/push_wrapup_finish () (for wrapup
+   text), and push_macro () (for macro definitions).  Because macro
+   expansion needs direct access to the current input obstack (for
+   optimization), push_string () is split in two functions,
+   push_string_init (), which returns a pointer to the current input
+   stack, and push_string_finish (), which returns a pointer to the
+   final text.  The input_block *next is used to manage the
+   coordination between the different push routines.
 
    The current file and line number are stored in two global
    variables, for use by the error handling functions in m4.c.  Macro
@@ -185,6 +185,9 @@ static struct re_registers regs;
    context.  */
 static unsigned int current_quote_age;
 
+/* Cache a quote pair.  See quote_cache.  */
+static string_pair *cached_quote;
+
 static bool pop_input (bool);
 static void set_quote_age (void);
 
@@ -500,17 +503,14 @@ push_string_finish (void)
   return ret;
 }
 
-/*------------------------------------------------------------------.
-| The function push_wrapup () pushes a string on the wrapup stack.  |
-| When the normal input stack gets empty, the wrapup stack will     |
-| become the input stack, and push_string () and push_file () will  |
-| operate on wrapup_stack.  Push_wrapup should be done as           |
-| push_string (), but this will suffice, as long as arguments to    |
-| m4_m4wrap () are moderate in size.                                |
-`------------------------------------------------------------------*/
+/*--------------------------------------------------------------.
+| The function push_wrapup_init () returns an obstack ready for |
+| direct expansion of wrapup text, and should be followed by    |
+| push_wrapup_finish ().                                        |
+`--------------------------------------------------------------*/
 
-void
-push_wrapup (const char *s)
+struct obstack *
+push_wrapup_init (void)
 {
   input_block *i;
   i = (input_block *) obstack_alloc (wrapup_stack, sizeof *i);
@@ -518,9 +518,28 @@ push_wrapup (const char *s)
   i->type = INPUT_STRING;
   i->file = current_file;
   i->line = current_line;
-  i->u.u_s.len = strlen (s);
-  i->u.u_s.str = (char *) obstack_copy (wrapup_stack, s, i->u.u_s.len);
   wsp = i;
+  return wrapup_stack;
+}
+
+/*---------------------------------------------------------------.
+| After pushing wrapup text, push_wrapup_finish () completes the |
+| bookkeeping.                                                   |
+`---------------------------------------------------------------*/
+void
+push_wrapup_finish (void)
+{
+  input_block *i = wsp;
+  if (obstack_object_size (wrapup_stack) == 0)
+    {
+      wsp = i->prev;
+      obstack_free (wrapup_stack, i);
+    }
+  else
+    {
+      i->u.u_s.len = obstack_object_size (wrapup_stack);
+      i->u.u_s.str = (char *) obstack_finish (wrapup_stack);
+    }
 }
 
 
@@ -607,6 +626,7 @@ pop_input (bool cleanup)
       abort ();
     }
   obstack_free (current_input, isp);
+  cached_quote = NULL;
   next = NULL;			/* might be set in push_string_init () */
 
   isp = tmp;
@@ -674,13 +694,7 @@ input_print (struct obstack *obs, const input_block *input)
       obstack_1grow (obs, '>');
       break;
     case INPUT_MACRO:
-      {
-	const builtin *bp = find_builtin_by_addr (input->u.func);
-	assert (bp);
-	obstack_1grow (obs, '<');
-	obstack_grow (obs, bp->name, strlen (bp->name));
-	obstack_1grow (obs, '>');
-      }
+      func_print (obs, find_builtin_by_addr (input->u.func), false, NULL);
       break;
     case INPUT_CHAIN:
       chain = input->u.u_c.chain;
@@ -696,7 +710,9 @@ input_print (struct obstack *obs, const input_block *input)
 	    case CHAIN_ARGV:
 	      assert (!chain->u.u_a.comma);
 	      if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
-			     chain->u.u_a.quotes, &maxlen))
+			     quote_cache (NULL, chain->quote_age,
+					  chain->u.u_a.quotes),
+			     chain->u.u_a.flatten, NULL, &maxlen, false))
 		return;
 	      break;
 	    default:
@@ -783,7 +799,9 @@ peek_input (bool allow_argv)
 		     argument from argv.  */
 		  push_string_init ();
 		  push_arg_quote (current_input, chain->u.u_a.argv,
-				  chain->u.u_a.index, chain->u.u_a.quotes);
+				  chain->u.u_a.index,
+				  quote_cache (NULL, chain->quote_age,
+					       chain->u.u_a.quotes));
 		  chain->u.u_a.index++;
 		  chain->u.u_a.comma = true;
 		  push_string_finish ();
@@ -911,7 +929,9 @@ next_char_1 (bool allow_quote)
 		     argument from argv.  */
 		  push_string_init ();
 		  push_arg_quote (current_input, chain->u.u_a.argv,
-				  chain->u.u_a.index, chain->u.u_a.quotes);
+				  chain->u.u_a.index,
+				  quote_cache (NULL, chain->quote_age,
+					       chain->u.u_a.quotes));
 		  chain->u.u_a.index++;
 		  chain->u.u_a.comma = true;
 		  push_string_finish ();
@@ -1007,7 +1027,9 @@ append_quote_token (struct obstack *obs, token_data *td)
   if (src_chain->type == CHAIN_ARGV)
     {
       arg_print (obs, src_chain->u.u_a.argv, src_chain->u.u_a.index,
-		 src_chain->u.u_a.quotes, NULL);
+		 quote_cache (NULL, src_chain->quote_age,
+			      src_chain->u.u_a.quotes),
+		 src_chain->u.u_a.flatten, NULL, NULL, false);
       arg_adjust_refcount (src_chain->u.u_a.argv, false);
       return;
     }
@@ -1366,6 +1388,7 @@ set_quote_age (void)
 			 | (*curr_quote.str2 & 0xff));
   else
     current_quote_age = 0;
+  cached_quote = NULL;
 }
 
 /* Return the current quote age.  Each non-trivial changequote alters
@@ -1391,6 +1414,53 @@ safe_quotes (void)
 {
   return current_quote_age != 0;
 }
+
+/* Interface for caching frequently used quote pairs, using AGE for
+   optimization.  If QUOTES is NULL, don't use quoting.  If OBS is
+   non-NULL, AGE should be the current quote age, and QUOTES should be
+   &curr_quote; the return value will be a cached quote pair, where
+   the pointer is valid at least as long as OBS is not reset, but
+   whose contents are only guaranteed until the next changequote or
+   quote_cache.  Otherwise, OBS is NULL, AGE should be the same as
+   before, and QUOTES should be a previously returned cache value;
+   used to refresh the contents of the result.  */
+const string_pair *
+quote_cache (struct obstack *obs, unsigned int age, const string_pair *quotes)
+{
+  static char lquote[2];
+  static char rquote[2];
+  static string_pair simple = {lquote, 1, rquote, 1};
+
+  /* Implementation - if AGE is non-zero, then the implementation of
+     set_quote_age guarantees that we can recreate the return value on
+     the fly; so we use static storage, and the contents must be used
+     immediately.  If AGE is zero, then we must copy QUOTES onto OBS
+     (since changequote will invalidate the original), but we might as
+     well cache that copy (in case the current expansion contains more
+     than one instance of $@).  */
+  if (!quotes)
+    return NULL;
+  if (age)
+    {
+      *lquote = (age >> 8) & 0xff;
+      *rquote = age & 0xff;
+      return &simple;
+    }
+  if (!obs)
+    return quotes;
+  assert (next && quotes == &curr_quote);
+  if (!cached_quote)
+    {
+      assert (obs == current_input && obstack_object_size (obs) == 0);
+      cached_quote = (string_pair *) obstack_copy (obs, quotes,
+						   sizeof *quotes);
+      cached_quote->str1 = (char *) obstack_copy0 (obs, quotes->str1,
+						   quotes->len1);
+      cached_quote->str2 = (char *) obstack_copy0 (obs, quotes->str2,
+						   quotes->len2);
+    }
+  return cached_quote;
+}
 
 
 /*--------------------------------------------------------------------.
diff --git a/src/m4.h b/src/m4.h
index e1da7a7..0c2a8c8 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -386,7 +386,8 @@ void push_macro (builtin_func *);
 struct obstack *push_string_init (void);
 bool push_token (token_data *, int, bool);
 const input_block *push_string_finish (void);
-void push_wrapup (const char *);
+struct obstack *push_wrapup_init (void);
+void push_wrapup_finish (void);
 bool pop_wrapup (void);
 void input_print (struct obstack *, const input_block *);
 
@@ -410,6 +411,8 @@ void set_word_regexp (const char *, const char *);
 #endif
 unsigned int quote_age (void);
 bool safe_quotes (void);
+const string_pair *quote_cache (struct obstack *, unsigned int,
+				const string_pair *);
 
 /* File: output.c --- output functions.  */
 extern int current_diversion;
@@ -494,7 +497,7 @@ size_t arg_len (macro_arguments *, unsigned int);
 builtin_func *arg_func (macro_arguments *, unsigned int);
 struct obstack *arg_scratch (void);
 bool arg_print (struct obstack *, macro_arguments *, unsigned int,
-		const string_pair *, int *);
+		const string_pair *, bool, const char *, int *, bool);
 macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t,
 				bool, bool);
 void push_arg (struct obstack *, macro_arguments *, unsigned int);
@@ -553,6 +556,7 @@ const char *ntoa (int32_t, int);
 
 const builtin *find_builtin_by_addr (builtin_func *);
 const builtin *find_builtin_by_name (const char *);
+void func_print (struct obstack *, const builtin *, bool, const string_pair *);
 
 /* File: path.c  --- path search for include files.  */
 
diff --git a/src/macro.c b/src/macro.c
index 8b85cf6..8b7e303 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -911,7 +911,9 @@ arg_text (macro_arguments *argv, unsigned int index)
 	      break;
 	    case CHAIN_ARGV:
 	      arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
-			 chain->u.u_a.quotes, NULL);
+			 quote_cache (NULL, chain->quote_age,
+				      chain->u.u_a.quotes),
+			 chain->u.u_a.flatten, NULL, NULL, false);
 	      break;
 	    default:
 	      assert (!"arg_text");
@@ -1097,50 +1099,70 @@ arg_scratch (void)
 
 /* Dump a representation of ARGV to the obstack OBS, starting with
    argument INDEX.  If QUOTES is non-NULL, each argument is displayed
-   with those quotes.  If MAX_LEN is non-NULL, truncate the output
-   after *MAX_LEN bytes are output and return true; otherwise, return
-   false, and reduce *MAX_LEN by the number of bytes output.  */
+   with those quotes.  If FLATTEN, builtins are ignored.  Separate
+   arguments with SEP, which defaults to a comma.  If MAX_LEN is
+   non-NULL, truncate the output after *MAX_LEN bytes are output and
+   return true; otherwise, return false, and reduce *MAX_LEN by the
+   number of bytes output.  If QUOTE_EACH, the truncation length is
+   reset for each argument, quotes do not count against length, and
+   all arguments are printed; otherwise, quotes count against the
+   length and trailing arguments may be discarded.  */
 bool
 arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
-	   const string_pair *quotes, int *max_len)
+	   const string_pair *quotes, bool flatten, const char *sep,
+	   int *max_len, bool quote_each)
 {
   int len = max_len ? *max_len : INT_MAX;
   unsigned int i;
   token_data *token;
   token_chain *chain;
-  bool comma = false;
-
+  bool use_sep = false;
+  bool done;
+  size_t sep_len;
+  size_t *plen = quote_each ? NULL : &len;
+
+  if (!sep)
+    sep = ",";
+  sep_len = strlen (sep);
   for (i = index; i < argv->argc; i++)
     {
-      if (comma && obstack_print (obs, ",", 1, &len))
+      if (quote_each && max_len)
+	len = *max_len;
+      if (use_sep && obstack_print (obs, sep, sep_len, plen))
 	return true;
-      else
-	comma = true;
+      use_sep = true;
       token = arg_token (argv, i, NULL);
-      if (quotes && obstack_print (obs, quotes->str1, quotes->len1, &len))
-	return true;
       switch (TOKEN_DATA_TYPE (token))
 	{
 	case TOKEN_TEXT:
+	  if (quotes && obstack_print (obs, quotes->str1, quotes->len1, plen))
+	    return true;
 	  if (obstack_print (obs, TOKEN_DATA_TEXT (token),
-			     TOKEN_DATA_LEN (token), &len))
+			     TOKEN_DATA_LEN (token), &len) && !quote_each)
+	    return true;
+	  if (quotes && obstack_print (obs, quotes->str2, quotes->len2, plen))
 	    return true;
 	  break;
 	case TOKEN_COMP:
+	  if (quotes && obstack_print (obs, quotes->str1, quotes->len1, plen))
+	    return true;
 	  chain = token->u.u_c.chain;
-	  while (chain)
+	  done = false;
+	  while (chain && !done)
 	    {
 	      switch (chain->type)
 		{
 		case CHAIN_STR:
 		  if (obstack_print (obs, chain->u.u_s.str, chain->u.u_s.len,
 				     &len))
-		    return true;
+		    done = true;
 		  break;
 		case CHAIN_ARGV:
 		  if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
-				 chain->u.u_a.quotes, &len))
-		    return true;
+				 quote_cache (NULL, chain->quote_age,
+					      chain->u.u_a.quotes),
+				 flatten, NULL, &len, false))
+		    done = true;
 		  break;
 		default:
 		  assert (!"arg_print");
@@ -1148,16 +1170,19 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
 		}
 	      chain = chain->next;
 	    }
+	  if (done && !quote_each)
+	    return true;
+	  if (quotes && obstack_print (obs, quotes->str2, quotes->len2, plen))
+	    return true;
 	  break;
 	case TOKEN_FUNC:
-	  /* TODO - support func.  */
+	  func_print (obs, find_builtin_by_addr (TOKEN_DATA_FUNC (token)),
+		      flatten, quotes);
+	  break;
 	default:
 	  assert (!"arg_print");
 	  abort ();
 	}
-      if (quotes && obstack_print (obs, quotes->str2, quotes->len2,
-				   &len))
-	return true;
     }
   if (max_len)
     *max_len = len;
@@ -1201,20 +1226,7 @@ make_argv_ref_token (token_data *token, struct obstack *obs, int level,
   chain->u.u_a.flatten = flatten;
   chain->u.u_a.comma = false;
   chain->u.u_a.skip_last = false;
-  if (quotes)
-    {
-      /* Clone the quotes into the obstack, since a subsequent
-	 changequote may take effect before the $@ ref is
-	 rescanned.  */
-      /* TODO - optimize when quote_age is nonzero.  */
-      string_pair *tmp = (string_pair *) obstack_copy (obs, quotes,
-						       sizeof *quotes);
-      tmp->str1 = (char *) obstack_copy0 (obs, quotes->str1, quotes->len1);
-      tmp->str2 = (char *) obstack_copy0 (obs, quotes->str2, quotes->len2);
-      chain->u.u_a.quotes = tmp;
-    }
-  else
-    chain->u.u_a.quotes = NULL;
+  chain->u.u_a.quotes = quote_cache (obs, chain->quote_age, quotes);
   return token;
 }
 


hooks/post-receive
--
GNU M4 source repository
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.