[SCM] GNU M4 source repository branch, master, updated. cvs-readonly-63-g5d083e4

"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=5d083e4726ed578b093aaf82e2a5a542f5815dcd

The branch, master has been updated
       via  5d083e4726ed578b093aaf82e2a5a542f5815dcd (commit)
      from  7f0b47b96e8872513aae3a4e3819aef6d799e6bd (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 5d083e4726ed578b093aaf82e2a5a542f5815dcd
Author: Eric Blake <[email protected]>
Date:   Fri Feb 15 22:12:03 2008 -0700

    Stage 15: return argv refs back to collect_arguments.
    
    * m4/m4private.h (CHAR_ARGV): New input engine sentinel.
    (enum m4__token_type): Add M4_TOKEN_ARGV.
    (struct m4__symbol_chain): Add skip_last member to argv link.
    (m4__next_token): Add parameter.
    * m4/input.c (peek_char, file_peek, builtin_peek, string_peek)
    (composite_peek, m4__next_token): Add new parameter.
    (composite_read, append_quote_token): Support argv in quotes.
    (init_argv_symbol): New function.
    (m4__push_symbol, match_input, consume_syntax)
    (m4__next_token_is_open, m4_print_token): Adjust callers.
    * m4/macro.c (m4_macro_expand_input, m4__arg_adjust_refcount)
    (arg_mark, m4_arg_text, make_argv_ref): Likewise.
    (expand_argument, collect_arguments): Handle new token.
    (arg_symbol): Drill through $@ reference.
    * m4/syntax.c (set_quote_age): Detect disabled comments.
    * m4/symtab.c (dump_symbol_CB) [DEBUG_SYM]: Fix debug code.
    
    Signed-off-by: Eric Blake <[email protected]>

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

Summary of changes:
 ChangeLog      |   26 +++++++
 m4/input.c     |  207 +++++++++++++++++++++++++++++++++++++++++---------------
 m4/m4private.h |    9 ++-
 m4/macro.c     |  114 ++++++++++++++++++++++++-------
 m4/symtab.c    |    2 +-
 m4/syntax.c    |    8 ++-
 6 files changed, 277 insertions(+), 89 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0e3c93e..d0ba987 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2008-02-16  Eric Blake  <[email protected]>
+
+	Stage 15: return argv refs back to collect_arguments.
+	Collect an entire $@ reference at once rather than one argument at
+	a time, outside of quotes (but inside quotes, $@ is still
+	flattened for now).  The skip_last field allows concatenation of
+	$@ with other text when collecting arguments.
+	Memory impact: noticeable improvement, due to better reuse of $@.
+	Speed impact: noticeable improvement, due to less parsing.
+	* m4/m4private.h (CHAR_ARGV): New input engine sentinel.
+	(enum m4__token_type): Add M4_TOKEN_ARGV.
+	(struct m4__symbol_chain): Add skip_last member to argv link.
+	(m4__next_token): Add parameter.
+	* m4/input.c (peek_char, file_peek, builtin_peek, string_peek)
+	(composite_peek, m4__next_token): Add new parameter.
+	(composite_read, append_quote_token): Support argv in quotes.
+	(init_argv_symbol): New function.
+	(m4__push_symbol, match_input, consume_syntax)
+	(m4__next_token_is_open, m4_print_token): Adjust callers.
+	* m4/macro.c (m4_macro_expand_input, m4__arg_adjust_refcount)
+	(arg_mark, m4_arg_text, make_argv_ref): Likewise.
+	(expand_argument, collect_arguments): Handle new token.
+	(arg_symbol): Drill through $@ reference.
+	* m4/syntax.c (set_quote_age): Detect disabled comments.
+	* m4/symtab.c (dump_symbol_CB) [DEBUG_SYM]: Fix debug code.
+
 2008-02-15  Eric Blake  <[email protected]>
 
 	* modules/gnu.c (regexp_compile): Use a fastmap for regex speed.
diff --git a/m4/input.c b/m4/input.c
index 025ae0d..b5d50a1 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -92,20 +92,20 @@
    maintains its own notion of the current file and line, so swapping
    between input blocks must update the context accordingly.  */
 
-static	int	file_peek		(m4_input_block *, m4 *);
+static	int	file_peek		(m4_input_block *, m4 *, bool);
 static	int	file_read		(m4_input_block *, m4 *, bool, bool);
 static	void	file_unget		(m4_input_block *, int);
 static	bool	file_clean		(m4_input_block *, m4 *, bool);
 static	void	file_print		(m4_input_block *, m4 *, m4_obstack *);
-static	int	builtin_peek		(m4_input_block *, m4 *);
+static	int	builtin_peek		(m4_input_block *, m4 *, bool);
 static	int	builtin_read		(m4_input_block *, m4 *, bool, bool);
 static	void	builtin_unget		(m4_input_block *, int);
 static	void	builtin_print		(m4_input_block *, m4 *, m4_obstack *);
-static	int	string_peek		(m4_input_block *, m4 *);
+static	int	string_peek		(m4_input_block *, m4 *, bool);
 static	int	string_read		(m4_input_block *, m4 *, bool, bool);
 static	void	string_unget		(m4_input_block *, int);
 static	void	string_print		(m4_input_block *, m4 *, m4_obstack *);
-static	int	composite_peek		(m4_input_block *, m4 *);
+static	int	composite_peek		(m4_input_block *, m4 *, bool);
 static	int	composite_read		(m4_input_block *, m4 *, bool, bool);
 static	void	composite_unget		(m4_input_block *, int);
 static	bool	composite_clean		(m4_input_block *, m4 *, bool);
@@ -116,12 +116,14 @@ static	void	append_quote_token	(m4 *, m4_obstack *,
 					 m4_symbol_value *);
 static	bool	match_input		(m4 *, const char *, bool);
 static	int	next_char		(m4 *, bool, bool);
-static	int	peek_char		(m4 *);
+static	int	peek_char		(m4 *, bool);
 static	bool	pop_input		(m4 *, bool);
 static	void	unget_input		(int);
 static	bool	consume_syntax		(m4 *, m4_obstack *, unsigned int);
 
 #ifdef DEBUG_INPUT
+# include "quotearg.h"
+
 static int m4_print_token (const char *, m4__token_type, m4_symbol_value *);
 #endif
 
@@ -129,8 +131,9 @@ static int m4_print_token (const char *, m4__token_type, m4_symbol_value *);
 struct input_funcs
 {
   /* Peek at input, return an unsigned char, CHAR_BUILTIN if it is a
-     builtin, or CHAR_RETRY if none available.  */
-  int	(*peek_func)	(m4_input_block *, m4 *);
+     builtin, or CHAR_RETRY if none available.  If ALLOW_ARGV, then
+     CHAR_ARGV may be returned.  */
+   int	(*peek_func)	(m4_input_block *, m4 *, bool);
 
   /* Read input, return an unsigned char, CHAR_BUILTIN if it is a
      builtin, or CHAR_RETRY if none available.  If ALLOW_QUOTE, then
@@ -254,7 +257,8 @@ static struct input_funcs composite_funcs = {
 
 /* Input files, from command line or [s]include.  */
 static int
-file_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED)
+file_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
+	   bool allow_argv M4_GNUC_UNUSED)
 {
   int ch;
 
@@ -389,7 +393,8 @@ m4_push_file (m4 *context, FILE *fp, const char *title, bool close_file)
 
 /* Handle a builtin macro token.  */
 static int
-builtin_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED)
+builtin_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
+	      bool allow_argv M4_GNUC_UNUSED)
 {
   if (me->u.u_b.read)
     return CHAR_RETRY;
@@ -474,7 +479,8 @@ m4_push_builtin (m4 *context, m4_symbol_value *token)
 
 /* Handle string expansion text.  */
 static int
-string_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED)
+string_peek (m4_input_block *me, m4 *context M4_GNUC_UNUSED,
+	     bool allow_argv M4_GNUC_UNUSED)
 {
   return me->u.u_s.len ? to_uchar (*me->u.u_s.str) : CHAR_RETRY;
 }
@@ -662,7 +668,7 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, size_t level, bool inuse)
       next->u.u_c.end = chain;
       if (chain->type == M4__CHAIN_ARGV)
 	{
-	  assert (!chain->u.u_a.comma);
+	  assert (!chain->u.u_a.comma && !chain->u.u_a.skip_last);
 	  inuse |= m4__arg_adjust_refcount (context, chain->u.u_a.argv, true);
 	}
       else if (chain->type == M4__CHAIN_STR && chain->u.u_s.level < SIZE_MAX)
@@ -718,9 +724,11 @@ m4_push_string_finish (void)
    in FIFO order, even though the obstack allocates memory in LIFO
    order.  */
 static int
-composite_peek (m4_input_block *me, m4 *context)
+composite_peek (m4_input_block *me, m4 *context, bool allow_argv)
 {
   m4__symbol_chain *chain = me->u.u_c.chain;
+  size_t argc;
+
   while (chain)
     {
       switch (chain->type)
@@ -730,12 +738,16 @@ composite_peek (m4_input_block *me, m4 *context)
 	    return to_uchar (chain->u.u_s.str[0]);
 	  break;
 	case M4__CHAIN_ARGV:
-	  /* TODO - figure out how to pass multiple arguments to
-	     macro.c at once.  */
-	  if (chain->u.u_a.index == m4_arg_argc (chain->u.u_a.argv))
+	  argc = m4_arg_argc (chain->u.u_a.argv);
+	  if (chain->u.u_a.index == argc)
 	    break;
 	  if (chain->u.u_a.comma)
 	    return ','; /* FIXME - support M4_SYNTAX_COMMA.  */
+	  /* Only return a reference in the quoting is correct and the
+	     reference has more than one argument left.  */
+	  if (allow_argv && chain->quote_age == m4__quote_age (M4SYNTAX)
+	      && chain->u.u_a.quotes && chain->u.u_a.index + 1 < argc)
+	    return CHAR_ARGV;
 	  /* Rather than directly parse argv here, we push another
 	     input block containing the next unparsed argument from
 	     argv.  */
@@ -745,7 +757,7 @@ composite_peek (m4_input_block *me, m4 *context)
 	  chain->u.u_a.index++;
 	  chain->u.u_a.comma = true;
 	  m4_push_string_finish ();
-	  return peek_char (context);
+	  return peek_char (context, allow_argv);
 	default:
 	  assert (!"composite_peek");
 	  abort ();
@@ -761,9 +773,7 @@ composite_read (m4_input_block *me, m4 *context, bool allow_quote, bool safe)
   m4__symbol_chain *chain = me->u.u_c.chain;
   while (chain)
     {
-      /* TODO also support returning $@ as CHAR_QUOTE.  */
-      if (allow_quote && chain->quote_age == m4__quote_age (M4SYNTAX)
-	  && chain->type == M4__CHAIN_STR)
+      if (allow_quote && chain->quote_age == m4__quote_age (M4SYNTAX))
 	return CHAR_QUOTE;
       switch (chain->type)
 	{
@@ -779,8 +789,6 @@ composite_read (m4_input_block *me, m4 *context, bool allow_quote, bool safe)
 	    m4__adjust_refcount (context, chain->u.u_s.level, false);
 	  break;
 	case M4__CHAIN_ARGV:
-	  /* TODO - figure out how to pass multiple arguments to
-	     macro.c at once.  */
 	  if (chain->u.u_a.index == m4_arg_argc (chain->u.u_a.argv))
 	    {
 	      m4__arg_adjust_refcount (context, chain->u.u_a.argv, false);
@@ -996,7 +1004,7 @@ pop_input (m4 *context, bool cleanup)
   assert (isp);
   if (isp->funcs->clean_func
       ? !isp->funcs->clean_func (isp, context, cleanup)
-      : (isp->funcs->peek_func (isp, context) != CHAR_RETRY))
+      : (isp->funcs->peek_func (isp, context, true) != CHAR_RETRY))
     return false;
 
   if (tmp != NULL)
@@ -1073,18 +1081,28 @@ append_quote_token (m4 *context, m4_obstack *obs, m4_symbol_value *value)
 {
   m4__symbol_chain *src_chain = isp->u.u_c.chain;
   m4__symbol_chain *chain;
-  assert (isp->funcs == &composite_funcs && obs && m4__quote_age (M4SYNTAX)
-	  && src_chain->type == M4__CHAIN_STR
-	  && src_chain->u.u_s.level <= SIZE_MAX);
+  assert (isp->funcs == &composite_funcs && obs && m4__quote_age (M4SYNTAX));
   isp->u.u_c.chain = src_chain->next;
 
   /* Speed consideration - for short enough symbols, the speed and
      memory overhead of parsing another INPUT_CHAIN link outweighs the
      time to inline the symbol text.  */
-  if (src_chain->u.u_s.len <= INPUT_INLINE_THRESHOLD)
+  if (src_chain->type == M4__CHAIN_STR
+      && src_chain->u.u_s.len <= INPUT_INLINE_THRESHOLD)
     {
+      assert (src_chain->u.u_s.level <= SIZE_MAX);
       obstack_grow (obs, src_chain->u.u_s.str, src_chain->u.u_s.len);
       m4__adjust_refcount (context, src_chain->u.u_s.level, false);
+      return;
+    }
+
+  /* TODO preserve $@ through quotes.  */
+  if (src_chain->type == M4__CHAIN_ARGV)
+    {
+      m4_arg_print (obs, src_chain->u.u_a.argv, src_chain->u.u_a.index,
+		    src_chain->u.u_a.quotes, NULL, false);
+      m4__arg_adjust_refcount (context, src_chain->u.u_a.argv, false);
+      return;
     }
 
   if (value->type == M4_SYMBOL_VOID)
@@ -1103,6 +1121,65 @@ append_quote_token (m4 *context, m4_obstack *obs, m4_symbol_value *value)
   chain->next = NULL;
 }
 
+/* When an ARGV token is seen, convert VALUE to point to it via a
+   composite chain.  Use OBS for any additional allocations
+   needed.  */
+static void
+init_argv_symbol (m4 *context, m4_obstack *obs, m4_symbol_value *value)
+{
+  m4__symbol_chain *src_chain;
+  m4__symbol_chain *chain;
+  int ch = next_char (context, true, true);
+  const m4_string_pair *comments = m4_get_syntax_comments (M4SYNTAX);
+
+  assert (ch == CHAR_QUOTE && value->type == M4_SYMBOL_VOID
+	  && isp->funcs == &composite_funcs
+	  && isp->u.u_c.chain->type == M4__CHAIN_ARGV
+	  && obs && obstack_object_size (obs) == 0);
+
+  src_chain = isp->u.u_c.chain;
+  isp->u.u_c.chain = src_chain->next;
+  value->type = M4_SYMBOL_COMP;
+  /* Clone the link, since the input will be discarded soon.  */
+  chain = (m4__symbol_chain *) obstack_copy (obs, src_chain, sizeof *chain);
+  value->u.u_c.chain = value->u.u_c.end = chain;
+  chain->next = NULL;
+
+  /* If the next character is not ',' or ')', then unlink the last
+     argument from argv and schedule it for reparsing.  This way,
+     expand_argument never has to deal with concatenation of argv with
+     arbitrary text.  Note that the implementation of safe_quotes
+     ensures peek_input won't return CHAR_ARGV if the user is perverse
+     enough to mix comment delimiters with argument separators:
+
+       define(n,`$#')define(echo,$*)changecom(`,,',`)')n(echo(a,`,b`)'',c))
+       => 2 (not 3)
+
+     Therefore, we do not have to worry about calling MATCH, and thus
+     do not have to worry about pop_input being called and
+     invalidating the argv reference.
+
+     When the $@ ref is used unchanged, we completely bypass the
+     decrement of the argv refcount in next_char, since the ref is
+     still live via the current collect_arguments.  However, when the
+     last element of the $@ ref is reparsed, we must increase the argv
+     refcount here, to compensate for the fact that it will be
+     decreased once the final element is parsed.  */
+  assert (!comments->len1
+	  || (!m4_has_syntax (M4SYNTAX, *comments->str1,
+			      M4_SYNTAX_COMMA | M4_SYNTAX_CLOSE)
+	      && *comments->str1 != *src_chain->u.u_a.quotes->str1));
+  ch = peek_char (context, false);
+  if (!m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_COMMA | M4_SYNTAX_CLOSE))
+    {
+      isp->u.u_c.chain = src_chain;
+      src_chain->u.u_a.index = m4_arg_argc (chain->u.u_a.argv) - 1;
+      src_chain->u.u_a.comma = true;
+      chain->u.u_a.skip_last = true;
+      m4__arg_adjust_refcount (context, chain->u.u_a.argv, true);
+    }
+}
+
 
 /* Low level input is done a character at a time.  The function
    next_char () is used to read and advance the input to the next
@@ -1146,9 +1223,10 @@ next_char (m4 *context, bool allow_quote, bool retry)
 
 /* The function peek_char () is used to look at the next character in
    the input stream.  At any given time, it reads from the input_block
-   on the top of the current input stack.  */
+   on the top of the current input stack.  If ALLOW_ARGV, then return
+   CHAR_ARGV if an entire $@ reference is available for use.  */
 static int
-peek_char (m4 *context)
+peek_char (m4 *context, bool allow_argv)
 {
   int ch;
   m4_input_block *block = isp;
@@ -1159,7 +1237,8 @@ peek_char (m4 *context)
 	return CHAR_EOF;
 
       assert (block->funcs->peek_func);
-      if ((ch = block->funcs->peek_func (block, context)) != CHAR_RETRY)
+      ch = block->funcs->peek_func (block, context, allow_argv);
+      if (ch != CHAR_RETRY)
 	{
 /*	  if (IS_IGNORE (ch)) */
 /*	    return next_char (context, false, true); */
@@ -1228,7 +1307,7 @@ match_input (m4 *context, const char *s, bool consume)
   m4_obstack *st;
   bool result = false;
 
-  ch = peek_char (context);
+  ch = peek_char (context, false);
   if (ch != to_uchar (*s))
     return false;			/* fail */
 
@@ -1240,7 +1319,7 @@ match_input (m4 *context, const char *s, bool consume)
     }
 
   next_char (context, false, true);
-  for (n = 1, t = s++; (ch = peek_char (context)) == to_uchar (*s++); )
+  for (n = 1, t = s++; (ch = peek_char (context, false)) == to_uchar (*s++); )
     {
       next_char (context, false, true);
       n++;
@@ -1297,7 +1376,7 @@ consume_syntax (m4 *context, m4_obstack *obs, unsigned int syntax)
 	}
       if (ch == CHAR_RETRY || ch == CHAR_QUOTE)
 	{
-	  ch = peek_char (context);
+	  ch = peek_char (context, false);
 	  if (m4_has_syntax (M4SYNTAX, ch, syntax))
 	    {
 	      assert (ch < CHAR_EOF);
@@ -1355,8 +1434,10 @@ m4_input_exit (void)
    with a description of what TOKEN will contain.  If LINE is not
    NULL, set *LINE to the line number where the token starts.  If OBS,
    expand safe tokens (strings and comments) directly into OBS rather
-   than in a temporary staging area.  Report errors (unterminated
-   comments or strings) on behalf of CALLER, if non-NULL.
+   than in a temporary staging area.  If ALLOW_ARGV, OBS must be
+   non-NULL, and an entire series of arguments can be returned if a $@
+   reference is encountered.  Report errors (unterminated comments or
+   strings) on behalf of CALLER, if non-NULL.
 
    If OBS is NULL or the token expansion is unknown, the token text is
    collected on the obstack token_stack, which never contains more
@@ -1365,7 +1446,7 @@ m4_input_exit (void)
    m4__next_token () is called.  */
 m4__token_type
 m4__next_token (m4 *context, m4_symbol_value *token, int *line,
-		m4_obstack *obs, const char *caller)
+		m4_obstack *obs, bool allow_argv, const char *caller)
 {
   int ch;
   int quote_level;
@@ -1388,7 +1469,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line,
 
     /* Must consume an input character, but not until CHAR_BUILTIN is
        handled.  */
-    ch = peek_char (context);
+    ch = peek_char (context, allow_argv && m4__quote_age (M4SYNTAX));
     if (ch == CHAR_EOF)			/* EOF */
       {
 #ifdef DEBUG_INPUT
@@ -1407,6 +1488,14 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line,
 #endif
 	return M4_TOKEN_MACDEF;
       }
+    if (ch == CHAR_ARGV)
+      {
+	init_argv_symbol (context, obs, token);
+#ifdef DEBUG_INPUT
+	m4_print_token ("next_token", M4_TOKEN_ARGV, token);
+#endif
+	return M4_TOKEN_ARGV;
+      }
 
     /* Consume character we already peeked at.  */
     next_char (context, false, true);
@@ -1644,7 +1733,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line,
 bool
 m4__next_token_is_open (m4 *context)
 {
-  int ch = peek_char (context);
+  int ch = peek_char (context, false);
 
   if (ch == CHAR_EOF || ch == CHAR_BUILTIN
       || m4_has_syntax (M4SYNTAX, ch, (M4_SYNTAX_BCOMM | M4_SYNTAX_ESCAPE
@@ -1667,55 +1756,61 @@ m4_print_token (const char *s, m4__token_type type, m4_symbol_value *token)
   m4_obstack obs;
   size_t len;
 
-  obstack_init (&obs);
   if (!s)
     s = "m4input";
-  obstack_grow (&obs, s, strlen (s));
-  obstack_1grow (&obs, ':');
-  obstack_1grow (&obs, ' ');
+  xfprintf (stderr, "%s: ", s);
   switch (type)
     {				/* TOKSW */
     case M4_TOKEN_EOF:
-      obstack_grow (&obs, "eof", strlen ("eof"));
+      fputs ("eof", stderr);
       token = NULL;
       break;
     case M4_TOKEN_NONE:
-      obstack_grow (&obs, "none", strlen ("none"));
+      fputs ("none", stderr);
       token = NULL;
       break;
     case M4_TOKEN_STRING:
-      obstack_grow (&obs, "string\t", strlen ("string\t"));
+      fputs ("string\t", stderr);
       break;
     case M4_TOKEN_SPACE:
-      obstack_grow (&obs, "space\t", strlen ("space\t"));
+      fputs ("space\t", stderr);
       break;
     case M4_TOKEN_WORD:
-      obstack_grow (&obs, "word\t", strlen ("word\t"));
+      fputs ("word\t", stderr);
       break;
     case M4_TOKEN_OPEN:
-      obstack_grow (&obs, "open\t", strlen ("open\t"));
+      fputs ("open\t", stderr);
       break;
     case M4_TOKEN_COMMA:
-      obstack_grow (&obs, "comma\t", strlen ("comma\t"));
+      fputs ("comma\t", stderr);
       break;
     case M4_TOKEN_CLOSE:
-      obstack_grow (&obs, "close\t", strlen ("close\t"));
+      fputs ("close\t", stderr);
       break;
     case M4_TOKEN_SIMPLE:
-      obstack_grow (&obs, "simple\t", strlen ("simple\t"));
+      fputs ("simple\t", stderr);
       break;
     case M4_TOKEN_MACDEF:
-      obstack_grow (&obs, "builtin\t", strlen ("builtin\t"));
+      fputs ("builtin\t", stderr);
+      break;
+    case M4_TOKEN_ARGV:
+      fputs ("argv\t", stderr);
       break;
     default:
       abort ();
     }
   if (token)
-    m4_symbol_value_print (token, &obs, true, "\"", "\"", SIZE_MAX, NULL);
-  obstack_1grow (&obs, '\n');
-  len = obstack_object_size (&obs);
-  fwrite (obstack_finish (&obs), 1, len, stderr);
-  obstack_free (&obs, NULL);
+    {
+      obstack_init (&obs);
+      m4_symbol_value_print (token, &obs, NULL, NULL, true);
+      len = obstack_object_size (&obs);
+      xfprintf (stderr, "%s\n", quotearg_style_mem (c_maybe_quoting_style,
+						    obstack_finish (&obs),
+						    len));
+      obstack_free (&obs, NULL);
+    }
+  else
+    fputc ('\n', stderr);
   return 0;
 }
 #endif /* DEBUG_INPUT */
diff --git a/m4/m4private.h b/m4/m4private.h
index 77590f3..a2b78b8 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -221,6 +221,7 @@ struct m4__symbol_chain
       size_t index;			/* Argument index within argv.  */
       bool_bitfield flatten : 1;	/* True to treat builtins as text.  */
       bool_bitfield comma : 1;		/* True when `,' is next input.  */
+      bool_bitfield skip_last : 1;	/* True if last argument omitted.  */
       const m4_string_pair *quotes;	/* NULL for $*, quotes for $@.  */
     } u_a;			/* M4__CHAIN_ARGV.  */
   } u;
@@ -397,7 +398,8 @@ extern void m4__symtab_remove_module_references (m4_symbol_table*,
 #define CHAR_EOF	256	/* Character return on EOF.  */
 #define CHAR_BUILTIN	257	/* Character return for BUILTIN token.  */
 #define CHAR_QUOTE	258	/* Character return for quoted string.  */
-#define CHAR_RETRY	259	/* Character return for end of input block.  */
+#define CHAR_ARGV	259	/* Character return for $@ reference.  */
+#define CHAR_RETRY	260	/* Character return for end of input block.  */
 
 #define DEF_LQUOTE	"`"	/* Default left quote delimiter.  */
 #define DEF_RQUOTE	"\'"	/* Default right quote delimiter.  */
@@ -475,7 +477,8 @@ typedef enum {
   M4_TOKEN_COMMA,	/* Argument separator, M4_SYMBOL_TEXT.  */
   M4_TOKEN_CLOSE,	/* Argument list end, M4_SYMBOL_TEXT.  */
   M4_TOKEN_SIMPLE,	/* Single character, M4_SYMBOL_TEXT.  */
-  M4_TOKEN_MACDEF	/* Macro's definition (see "defn"), M4_SYMBOL_FUNC.  */
+  M4_TOKEN_MACDEF,	/* Macro's definition (see "defn"), M4_SYMBOL_FUNC.  */
+  M4_TOKEN_ARGV		/* A series of parameters, M4_SYMBOL_COMP.  */
 } m4__token_type;
 
 extern	void		m4__make_text_link (m4_obstack *, m4__symbol_chain **,
@@ -483,7 +486,7 @@ extern	void		m4__make_text_link (m4_obstack *, m4__symbol_chain **,
 extern	bool		m4__push_symbol (m4 *, m4_symbol_value *, size_t,
 					 bool);
 extern	m4__token_type	m4__next_token (m4 *, m4_symbol_value *, int *,
-					m4_obstack *, const char *);
+					m4_obstack *, bool, const char *);
 extern	bool		m4__next_token_is_open (m4 *);
 
 /* Fast macro versions of macro argv accessor functions,
diff --git a/m4/macro.c b/m4/macro.c
index 708be58..d6f81d8 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -183,7 +183,7 @@ m4_macro_expand_input (m4 *context)
   m4_set_symbol_value_text (&empty_symbol, "", 0, 0);
   VALUE_MAX_ARGS (&empty_symbol) = -1;
 
-  while ((type = m4__next_token (context, &token, &line, NULL, NULL))
+  while ((type = m4__next_token (context, &token, &line, NULL, false, NULL))
 	 != M4_TOKEN_EOF)
     expand_token (context, NULL, type, &token, line, true);
 }
@@ -311,7 +311,7 @@ expand_argument (m4 *context, m4_obstack *obs, m4_symbol_value *argp,
   /* Skip leading white space.  */
   do
     {
-      type = m4__next_token (context, &token, NULL, obs, caller);
+      type = m4__next_token (context, &token, NULL, obs, true, caller);
     }
   while (type == M4_TOKEN_SPACE);
 
@@ -389,6 +389,20 @@ expand_argument (m4 *context, m4_obstack *obs, m4_symbol_value *argp,
 	    argp->type = M4_SYMBOL_TEXT;
 	  break;
 
+	case M4_TOKEN_ARGV:
+	  assert (paren_level == 0 && argp->type == M4_SYMBOL_VOID
+		  && obstack_object_size (obs) == 0
+		  && token.u.u_c.chain == token.u.u_c.end
+		  && token.u.u_c.chain->type == M4__CHAIN_ARGV);
+	  argp->type = M4_SYMBOL_COMP;
+	  argp->u.u_c.chain = argp->u.u_c.end = token.u.u_c.chain;
+	  type = m4__next_token (context, &token, NULL, NULL, false, caller);
+	  if (argp->u.u_c.chain->u.u_a.skip_last)
+	    assert (type == M4_TOKEN_COMMA);
+	  else
+	    assert (type == M4_TOKEN_COMMA || type == M4_TOKEN_CLOSE);
+	  return type == M4_TOKEN_COMMA;
+
 	default:
 	  assert (!"expand_argument");
 	  abort ();
@@ -396,7 +410,7 @@ expand_argument (m4 *context, m4_obstack *obs, m4_symbol_value *argp,
 
       if (argp->type != M4_SYMBOL_VOID || obstack_object_size (obs))
 	first = false;
-      type = m4__next_token (context, &token, NULL, obs, caller);
+      type = m4__next_token (context, &token, NULL, obs, first, caller);
     }
 }
 
@@ -583,7 +597,7 @@ collect_arguments (m4 *context, const char *name, size_t len,
   if (m4__next_token_is_open (context))
     {
       /* Gobble parenthesis, then collect arguments.  */
-      m4__next_token (context, &token, NULL, NULL, name);
+      m4__next_token (context, &token, NULL, NULL, false, name);
       do
 	{
 	  tokenp = (m4_symbol_value *) obstack_alloc (arguments,
@@ -608,12 +622,22 @@ collect_arguments (m4 *context, const char *name, size_t len,
 	      && m4_get_symbol_value_quote_age (tokenp) != args.quote_age)
 	    args.quote_age = 0;
 	  else if (tokenp->type == M4_SYMBOL_COMP)
-	    args.has_ref = true;
+	    {
+	      args.has_ref = true;
+	      if (tokenp->u.u_c.chain->type == M4__CHAIN_ARGV)
+		{
+		  args.argc += (tokenp->u.u_c.chain->u.u_a.argv->argc
+				- tokenp->u.u_c.chain->u.u_a.index
+				- tokenp->u.u_c.chain->u.u_a.skip_last - 1);
+		  args.wrapper = true;
+		}
+	    }
 	}
       while (more_args);
     }
   argv = (m4_macro_args *) obstack_finish (argv_stack);
   argv->argc = args.argc;
+  argv->wrapper = args.wrapper;
   argv->has_ref = args.has_ref;
   if (args.quote_age != m4__quote_age (M4SYNTAX))
     argv->quote_age = 0;
@@ -981,9 +1005,22 @@ m4__arg_adjust_refcount (m4 *context, m4_macro_args *argv, bool increase)
 	  chain = argv->array[i]->u.u_c.chain;
 	  while (chain)
 	    {
-	      assert (chain->type == M4__CHAIN_STR);
-	      if (chain->u.u_s.level < SIZE_MAX)
-		m4__adjust_refcount (context, chain->u.u_s.level, increase);
+	      switch (chain->type)
+		{
+		case M4__CHAIN_STR:
+		  if (chain->u.u_s.level < SIZE_MAX)
+		    m4__adjust_refcount (context, chain->u.u_s.level,
+					 increase);
+		  break;
+		case M4__CHAIN_ARGV:
+		  assert (chain->u.u_a.argv->inuse);
+		  m4__arg_adjust_refcount (context, chain->u.u_a.argv,
+					   increase);
+		  break;
+		default:
+		  assert (!"m4__arg_adjust_refcount");
+		  abort ();
+		}
 	      chain = chain->next;
 	    }
 	}
@@ -996,15 +1033,25 @@ m4__arg_adjust_refcount (m4 *context, m4_macro_args *argv, bool increase)
 static void
 arg_mark (m4_macro_args *argv)
 {
+  size_t i;
+  m4__symbol_chain *chain;
+
+  if (argv->inuse)
+    return;
   argv->inuse = true;
   if (argv->wrapper)
     {
-      /* TODO for now we support only a single-length $@ chain.  */
-      assert (argv->arraylen == 1
-	      && argv->array[0]->type == M4_SYMBOL_COMP
-	      && !argv->array[0]->u.u_c.chain->next
-	      && argv->array[0]->u.u_c.chain->type == M4__CHAIN_ARGV);
-      argv->array[0]->u.u_c.chain->u.u_a.argv->inuse = true;
+      for (i = 0; i < argv->arraylen; i++)
+	if (argv->array[i]->type == M4_SYMBOL_COMP)
+	  {
+	    chain = argv->array[i]->u.u_c.chain;
+	    while (chain)
+	      {
+		if (chain->type == M4__CHAIN_ARGV && !chain->u.u_a.argv->inuse)
+		  arg_mark (chain->u.u_a.argv);
+		chain = chain->next;
+	      }
+	  }
     }
 }
 
@@ -1022,12 +1069,13 @@ make_argv_ref (m4_symbol_value *value, m4_obstack *obs, size_t level,
   m4__symbol_chain *chain;
 
   assert (obstack_object_size (obs) == 0);
-  if (argv->wrapper)
+  if (argv->wrapper && argv->arraylen == 1)
     {
-      /* TODO support concatenation with $@ refs.  */
-      assert (argv->arraylen == 1 && argv->array[0]->type == M4_SYMBOL_COMP);
+      /* TODO support $@ ref alongside other arguments.  */
+      assert (argv->array[0]->type == M4_SYMBOL_COMP);
       chain= argv->array[0]->u.u_c.chain;
-      assert (!chain->next && chain->type == M4__CHAIN_ARGV);
+      assert (!chain->next && chain->type == M4__CHAIN_ARGV
+	      && !chain->u.u_a.skip_last);
       argv = chain->u.u_a.argv;
       index += chain->u.u_a.index - 1;
     }
@@ -1044,6 +1092,7 @@ make_argv_ref (m4_symbol_value *value, m4_obstack *obs, size_t level,
   chain->u.u_a.index = index;
   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 changequote can
@@ -1081,12 +1130,14 @@ arg_symbol (m4_macro_args *argv, size_t index, size_t *level)
   for (i = 0; i < argv->arraylen; i++)
     {
       value = argv->array[i];
-      if (value->type == M4_SYMBOL_COMP)
+      if (value->type == M4_SYMBOL_COMP
+	  && value->u.u_c.chain->type == M4__CHAIN_ARGV)
 	{
 	  m4__symbol_chain *chain = value->u.u_c.chain;
 	  /* TODO - for now we support only a single $@ chain.  */
-	  assert (!chain->next && chain->type == M4__CHAIN_ARGV);
-	  if (index < chain->u.u_a.argv->argc - (chain->u.u_a.index - 1))
+	  assert (!chain->next);
+	  if (index <= (chain->u.u_a.argv->argc - chain->u.u_a.index
+			- chain->u.u_a.skip_last))
 	    {
 	      value = arg_symbol (chain->u.u_a.argv,
 				  chain->u.u_a.index - 1 + index, level);
@@ -1094,7 +1145,8 @@ arg_symbol (m4_macro_args *argv, size_t index, size_t *level)
 		value = &empty_symbol;
 	      break;
 	    }
-	  index -= chain->u.u_a.argv->argc - chain->u.u_a.index;
+	  index -= (chain->u.u_a.argv->argc - chain->u.u_a.index
+		    - chain->u.u_a.skip_last);
 	}
       else if (--index == 0)
 	break;
@@ -1154,15 +1206,25 @@ m4_arg_text (m4 *context, m4_macro_args *argv, size_t index)
   value = m4_arg_symbol (argv, index);
   if (m4_is_symbol_value_text (value))
     return m4_get_symbol_value_text (value);
-  /* TODO - concatenate argv refs and functions?  For now, we assume
-     all chain elements are text.  */
+  /* TODO - concatenate functions.  */
   assert (value->type == M4_SYMBOL_COMP);
   chain = value->u.u_c.chain;
   obs = m4_arg_scratch (context);
   while (chain)
     {
-      assert (chain->type == M4__CHAIN_STR);
-      obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
+      switch (chain->type)
+	{
+	case M4__CHAIN_STR:
+	  obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
+	  break;
+	case M4__CHAIN_ARGV:
+	  m4_arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
+			chain->u.u_a.quotes, NULL, false);
+	  break;
+	default:
+	  assert (!"m4_arg_text");
+	  abort ();
+	}
       chain = chain->next;
     }
   obstack_1grow (obs, '\0');
diff --git a/m4/symtab.c b/m4/symtab.c
index f302fe8..9636f9d 100644
--- a/m4/symtab.c
+++ b/m4/symtab.c
@@ -845,7 +845,7 @@ dump_symbol_CB (m4_symbol_table *symtab, const char *name,
     {
       m4_obstack obs;
       obstack_init (&obs);
-      m4_symbol_value_print (value, &obs, false, NULL, NULL, SIZE_MAX, true);
+      m4_symbol_value_print (value, &obs, NULL, NULL, true);
       xfprintf (stderr, "%s", (char *) obstack_finish (&obs));
       obstack_free (&obs, NULL);
     }
diff --git a/m4/syntax.c b/m4/syntax.c
index aff6444..8a7b0d1 100644
--- a/m4/syntax.c
+++ b/m4/syntax.c
@@ -743,9 +743,11 @@ set_quote_age (m4_syntax_table *syntax, bool reset, bool change)
 			  | M4_SYNTAX_COMMA | M4_SYNTAX_CLOSE
 			  | M4_SYNTAX_SPACE))
       && *syntax->quote.str1 != *syntax->quote.str2
-      && *syntax->comm.str1 != *syntax->quote.str2
-      && !m4_has_syntax (syntax, *syntax->comm.str1,
-			 M4_SYNTAX_OPEN | M4_SYNTAX_COMMA | M4_SYNTAX_CLOSE)
+      && (!syntax->comm.len1
+          || (*syntax->comm.str1 != *syntax->quote.str2
+              && !m4_has_syntax (syntax, *syntax->comm.str1,
+                                 (M4_SYNTAX_OPEN | M4_SYNTAX_COMMA
+                                  | M4_SYNTAX_CLOSE))))
       && m4_has_syntax (syntax, ',', M4_SYNTAX_COMMA))
     {
       syntax->quote_age = ((local_syntax_age << 16)


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.