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

"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=7319157ccd7cd65f72c0a456c3091252a13f558a

The branch, branch-1_4 has been updated
       via  7319157ccd7cd65f72c0a456c3091252a13f558a (commit)
      from  950807234e3ffd376ad51b2a21cd276dd8b4e59c (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 7319157ccd7cd65f72c0a456c3091252a13f558a
Author: Eric Blake <[email protected]>
Date:   Thu Nov 1 09:28:46 2007 -0600

    Stage 15: return argv refs back to collect_arguments.
    
    * src/m4.h (enum token_type): Add TOKEN_ARGV.
    (struct token_chain): Add skip_last member to argv link.
    (next_token): Update prototype.
    * src/input.c (CHAR_ARGV): New placeholder input character.
    (peek_input): Add parameter, to pass $@ at once.
    (next_char_1, append_quote_token): Handle $@ inside quotes.
    (init_argv_token): New function.
    (push_token, match_input, next_token, peek_token, lex_debug):
    Update callers.
    * src/macro.c (expand_input, collect_arguments): Likewise.
    (expand_argument): Handle incoming $@ token.
    (arg_adjust_refcount, arg_token, arg_text, make_argv_ref_token):
    Handle nested $@ refs.
    * src/symtab.c (symtab_debug): Update caller.
    * examples/null.m4: Document more tests that are needed.  Add
    tests for NUL with divert, patsubst, and regexp.
    * examples/null.out: Update for new tests.
    * doc/m4.texinfo (Syntax): Add test for m4exit and NUL.
    * checks/get-them (AWK): Give a default value.
    * checks/check-them: Allow tests to invoke child processes with
    same include path.  Perform message normalization on stderr.
    
    (cherry picked from commit 1fecefc8b990254aa667a01d12c6c7a2d716df06)
    
    Signed-off-by: Eric Blake <[email protected]>

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

Summary of changes:
 ChangeLog         |   31 +++++++++
 checks/check-them |   10 ++-
 checks/get-them   |    4 +-
 doc/m4.texinfo    |    8 ++-
 examples/null.m4  |  Bin 3671 -> 5764 bytes
 examples/null.out |  Bin 338 -> 400 bytes
 src/input.c       |  177 +++++++++++++++++++++++++++++++++++++++++------------
 src/m4.h          |    7 ++-
 src/macro.c       |  112 +++++++++++++++++++++++++--------
 src/symtab.c      |    2 +-
 10 files changed, 275 insertions(+), 76 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c455abc..0baa3c1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+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.
+	* src/m4.h (enum token_type): Add TOKEN_ARGV.
+	(struct token_chain): Add skip_last member to argv link.
+	(next_token): Update prototype.
+	* src/input.c (CHAR_ARGV): New placeholder input character.
+	(peek_input): Add parameter, to pass $@ at once.
+	(next_char_1, append_quote_token): Handle $@ inside quotes.
+	(init_argv_token): New function.
+	(push_token, match_input, next_token, peek_token, lex_debug):
+	Update callers.
+	* src/macro.c (expand_input, collect_arguments): Likewise.
+	(expand_argument): Handle incoming $@ token.
+	(arg_adjust_refcount, arg_token, arg_text, make_argv_ref_token):
+	Handle nested $@ refs.
+	* src/symtab.c (symtab_debug): Update caller.
+	* examples/null.m4: Document more tests that are needed.  Add
+	tests for NUL with divert, patsubst, and regexp.
+	* examples/null.out: Update for new tests.
+	* doc/m4.texinfo (Syntax): Add test for m4exit and NUL.
+	* checks/get-them (AWK): Give a default value.
+	* checks/check-them: Allow tests to invoke child processes with
+	same include path.  Perform message normalization on stderr.
+
 2008-02-15  Eric Blake  <[email protected]>
 
 	Use fastmaps for better regex performance.
diff --git a/checks/check-them b/checks/check-them
index daa1b00..9fca39b 100755
--- a/checks/check-them
+++ b/checks/check-them
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Check GNU m4 against examples from the manual source.
-# Copyright (C) 1992, 2006, 2007 Free Software Foundation, Inc.
+# Copyright (C) 1992, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 # Sanity check what we are testing
 m4 --version
@@ -68,7 +68,7 @@ do
   echo "Checking $file"
   options=`sed -ne '3s/^dnl @ extra options: //p;3q' "$file"`
   sed -e '/^dnl @/d' -e '/^\^D$/q' "$file" \
-    | LC_MESSAGES=C m4 -d -I "$examples" $options - >$out 2>$err
+    | LC_MESSAGES=C M4PATH=$examples m4 -d $options - >$out 2>$err
   stat=$?
 
   xstat=`sed -ne '2s/^dnl @ expected status: //p;2q' "$file"`
@@ -96,9 +96,11 @@ do
 
   xerrfile=`sed -n 's/^dnl @ expected error: //p' "$file"`
   if test -z "$xerrfile" ; then
-    sed -e '/^dnl @error{}/!d' -e 's///' -e "s|^m4:|$m4:|" "$file" > $xerr
+    sed '/^dnl @error{}/!d; s///; '"s|^m4:|$m4:|; s|\.\./examples|$examples|" \
+      "$file" > $xerr
   else
-    cp "$examples/$xerrfile" $xerr
+    sed "s|^m4:|$m4:|; s|\.\./examples|$examples|" \
+      "$examples/$xerrfile" > $xerr
   fi
 
   # For the benefit of mingw, normalize \r\n line endings
diff --git a/checks/get-them b/checks/get-them
index e034962..803f413 100755
--- a/checks/get-them
+++ b/checks/get-them
@@ -1,11 +1,13 @@
 #!/bin/sh
 # -*- AWK -*-
 # Extract all examples from the manual source.
-# Copyright (C) 1992, 2005, 2006, 2007 Free Software Foundation, Inc.
+# Copyright (C) 1992, 2005, 2006, 2007, 2008 Free Software Foundation,
+# Inc.
 
 # This script is for use with GNU awk.
 
 FILE=${1-/dev/null}
+: ${AWK=awk}
 
 $AWK '
 
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 69cfb62..32cb0a9 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -933,7 +933,13 @@ exception of the @sc{nul} character (the zero byte @samp{'\0'}).
 @comment xout: null.out
 @comment xerr: null.err
 @example
-include(`null.m4')dnl
+define(`m4exit')include(`null.m4')dnl
+@end example
+
+@comment status: 2
+@example
+include(`null.m4')
+@result{}# This file tests m4 behavior on NUL bytes.
 @end example
 @end ignore
 
diff --git a/examples/null.m4 b/examples/null.m4
index 904a6ef..2632522 100644
Binary files a/examples/null.m4 and b/examples/null.m4 differ
diff --git a/examples/null.out b/examples/null.out
index 6e8a114..c42e03c 100644
Binary files a/examples/null.out and b/examples/null.out differ
diff --git a/src/input.c b/src/input.c
index a0de36f..e320c72 100644
--- a/src/input.c
+++ b/src/input.c
@@ -154,6 +154,7 @@ static bool input_change;
 #define CHAR_EOF	256	/* Character return on EOF.  */
 #define CHAR_MACRO	257	/* Character return for MACRO token.  */
 #define CHAR_QUOTE	258	/* Character return for quoted string.  */
+#define CHAR_ARGV	259	/* Character return for $@ reference.  */
 
 /* Quote chars.  */
 string_pair curr_quote;
@@ -446,7 +447,7 @@ push_token (token_data *token, int level, bool inuse)
       next->u.u_c.end = chain;
       if (chain->type == CHAIN_ARGV)
 	{
-	  assert (!chain->u.u_a.comma);
+	  assert (!chain->u.u_a.comma && !chain->u.u_a.skip_last);
 	  inuse |= arg_adjust_refcount (chain->u.u_a.argv, true);
 	}
       else if (chain->type == CHAIN_STR && chain->u.u_s.level >= 0)
@@ -712,17 +713,18 @@ input_print (struct obstack *obs, const input_block *input)
 }
 
 
-/*-----------------------------------------------------------------.
-| Low level input is done a character at a time.  The function     |
-| peek_input () 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.  The return value is an unsigned |
-| char, or CHAR_EOF if there is no more input, or CHAR_MACRO if a  |
-| builtin token occurs next.                                       |
-`-----------------------------------------------------------------*/
+/*------------------------------------------------------------------.
+| Low level input is done a character at a time.  The function      |
+| peek_input () 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.  The return value is an unsigned  |
+| char, CHAR_EOF if there is no more input, CHAR_MACRO if a builtin |
+| token occurs next, or CHAR_ARGV if ALLOW_ARGV and the input is    |
+| visiting an argv reference with the correct quoting.              |
+`------------------------------------------------------------------*/
 
 static int
-peek_input (void)
+peek_input (bool allow_argv)
 {
   int ch;
   input_block *block = isp;
@@ -757,6 +759,7 @@ peek_input (void)
 	  chain = block->u.u_c.chain;
 	  while (chain)
 	    {
+	      unsigned int argc;
 	      switch (chain->type)
 		{
 		case CHAIN_STR:
@@ -764,11 +767,17 @@ peek_input (void)
 		    return to_uchar (*chain->u.u_s.str);
 		  break;
 		case CHAIN_ARGV:
-		  /* TODO - pass multiple arguments to macro.c at once.  */
-		  if (chain->u.u_a.index == arg_argc (chain->u.u_a.argv))
+		  argc = arg_argc (chain->u.u_a.argv);
+		  if (chain->u.u_a.index == argc)
 		    break;
 		  if (chain->u.u_a.comma)
 		    return ',';
+		  /* Only return a reference if the quoting is correct
+		     and the reference has more than one argument
+		     left.  */
+		  if (allow_argv && chain->quote_age == current_quote_age
+		      && 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.  */
@@ -778,7 +787,7 @@ peek_input (void)
 		  chain->u.u_a.index++;
 		  chain->u.u_a.comma = true;
 		  push_string_finish ();
-		  return peek_input ();
+		  return peek_input (allow_argv);
 		default:
 		  assert (!"peek_input");
 		  abort ();
@@ -871,9 +880,7 @@ next_char_1 (bool allow_quote)
 	  chain = isp->u.u_c.chain;
 	  while (chain)
 	    {
-	      /* TODO also support returning $@ as CHAR_QUOTE.  */
-	      if (allow_quote && chain->quote_age == current_quote_age
-		  && chain->type == CHAIN_STR)
+	      if (allow_quote && chain->quote_age == current_quote_age)
 		return CHAR_QUOTE;
 	      switch (chain->type)
 		{
@@ -889,7 +896,6 @@ next_char_1 (bool allow_quote)
 		    adjust_refcount (chain->u.u_s.level, false);
 		  break;
 		case CHAIN_ARGV:
-		  /* TODO - pass multiple arguments to macro.c at once.  */
 		  if (chain->u.u_a.index == arg_argc (chain->u.u_a.argv))
 		    {
 		      arg_adjust_refcount (chain->u.u_a.argv, false);
@@ -956,7 +962,6 @@ skip_line (const char *name)
   if (file != current_file || line != current_line)
     input_change = true;
 }
-
 
 /*-------------------------------------------------------------------.
 | When a MACRO token is seen, next_token () uses init_macro_token () |
@@ -983,20 +988,30 @@ append_quote_token (struct obstack *obs, token_data *td)
   token_chain *src_chain = isp->u.u_c.chain;
   token_chain *chain;
 
-  assert (isp->type == INPUT_CHAIN && obs && current_quote_age
-	  && src_chain->type == CHAIN_STR && src_chain->u.u_s.level >= 0);
+  assert (isp->type == INPUT_CHAIN && obs && current_quote_age);
   isp->u.u_c.chain = src_chain->next;
 
   /* Speed consideration - for short enough tokens, the speed and
      memory overhead of parsing another INPUT_CHAIN link outweighs the
      time to inline the token text.  */
-  if (src_chain->u.u_s.len <= INPUT_INLINE_THRESHOLD)
+  if (src_chain->type == CHAIN_STR
+      && src_chain->u.u_s.len <= INPUT_INLINE_THRESHOLD)
     {
+      assert (src_chain->u.u_s.level >= 0);
       obstack_grow (obs, src_chain->u.u_s.str, src_chain->u.u_s.len);
       adjust_refcount (src_chain->u.u_s.level, false);
       return;
     }
 
+  /* TODO preserve $@ through a quoted context.  */
+  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);
+      arg_adjust_refcount (src_chain->u.u_a.argv, false);
+      return;
+    }
+
   if (TOKEN_DATA_TYPE (td) == TOKEN_VOID)
     {
       TOKEN_DATA_TYPE (td) = TOKEN_COMP;
@@ -1013,6 +1028,65 @@ append_quote_token (struct obstack *obs, token_data *td)
   chain->next = NULL;
 }
 
+
+/*-------------------------------------------------------------------.
+| When an ARGV token is seen, convert TD to point to it via a	     |
+| composite token.  Use OBS for any additional allocations needed to |
+| store the token chain.					     |
+`-------------------------------------------------------------------*/
+static void
+init_argv_token (struct obstack *obs, token_data *td)
+{
+  token_chain *src_chain;
+  token_chain *chain;
+  int ch = next_char (true);
+
+  assert (ch == CHAR_QUOTE && TOKEN_DATA_TYPE (td) == TOKEN_VOID
+	  && isp->type == INPUT_CHAIN && isp->u.u_c.chain->type == CHAIN_ARGV
+	  && obs && obstack_object_size (obs) == 0);
+
+  src_chain = isp->u.u_c.chain;
+  isp->u.u_c.chain = src_chain->next;
+  TOKEN_DATA_TYPE (td) = TOKEN_COMP;
+  /* Clone the link, since the input will be discarded soon.  */
+  chain = (token_chain *) obstack_copy (obs, src_chain, sizeof *chain);
+  td->u.u_c.chain = td->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_1, 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 (*curr_comm.str1 != ',' && *curr_comm.str1 != ')'
+	  && *curr_comm.str1 != *curr_quote.str1);
+  ch = peek_input (false);
+  if (ch != ',' && ch != ')')
+    {
+      isp->u.u_c.chain = src_chain;
+      src_chain->u.u_a.index = arg_argc (chain->u.u_a.argv) - 1;
+      src_chain->u.u_a.comma = true;
+      chain->u.u_a.skip_last = true;
+      arg_adjust_refcount (chain->u.u_a.argv, true);
+    }
+}
+
+
 /*------------------------------------------------------------------.
 | This function is for matching a string against a prefix of the    |
 | input stream.  If the string S matches the input and CONSUME is   |
@@ -1029,7 +1103,7 @@ match_input (const char *s, bool consume)
   const char *t;
   bool result = false;
 
-  ch = peek_input ();
+  ch = peek_input (false);
   if (ch != to_uchar (*s))
     return false;			/* fail */
 
@@ -1041,7 +1115,7 @@ match_input (const char *s, bool consume)
     }
 
   next_char (false);
-  for (n = 1, t = s++; (ch = peek_input ()) == to_uchar (*s++); )
+  for (n = 1, t = s++; (ch = peek_input (false)) == to_uchar (*s++); )
     {
       next_char (false);
       n++;
@@ -1320,18 +1394,20 @@ safe_quotes (void)
 
 
 /*--------------------------------------------------------------------.
-| Parse a single token from the input stream, set TD to its           |
-| contents, and return its type.  A token is TOKEN_EOF if the         |
+| Parse a single token from the input stream, set TD to its	      |
+| contents, and return its type.  A token is TOKEN_EOF if the	      |
 | input_stack is empty; TOKEN_STRING for a quoted string or comment;  |
-| TOKEN_WORD for something that is a potential macro name; and        |
+| TOKEN_WORD for something that is a potential macro name; and	      |
 | TOKEN_SIMPLE for any single character that is not a part of any of  |
 | the previous types.  If LINE is not NULL, set *LINE to the line     |
 | where the token starts.  If OBS is not NULL, expand TOKEN_STRING    |
 | directly into OBS rather than in token_stack temporary storage      |
-| area, and TD could be a TOKEN_COMP instead of the usual             |
-| TOKEN_TEXT.  Report errors (unterminated comments or strings) on    |
-| behalf of CALLER, if non-NULL.                                      |
-|                                                                     |
+| area, and TD could be a TOKEN_COMP instead of the usual	      |
+| TOKEN_TEXT.  If ALLOW_ARGV, OBS must be non-NULL, and an entire     |
+| series of arguments can be returned as TOKEN_ARGV when a $@	      |
+| reference is encountered.  Report errors (unterminated comments or  |
+| strings) on behalf of CALLER, if non-NULL.			      |
+|								      |
 | Next_token () returns the token type, and passes back a pointer to  |
 | the token data through TD.  Non-string token text is collected on   |
 | the obstack token_stack, which never contains more than one token   |
@@ -1340,7 +1416,8 @@ safe_quotes (void)
 `--------------------------------------------------------------------*/
 
 token_type
-next_token (token_data *td, int *line, struct obstack *obs, const char *caller)
+next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv,
+	    const char *caller)
 {
   int ch;
   int quote_level;
@@ -1362,7 +1439,7 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller)
 
   /* Can't consume character until after CHAR_MACRO is handled.  */
   TOKEN_DATA_TYPE (td) = TOKEN_VOID;
-  ch = peek_input ();
+  ch = peek_input (allow_argv && current_quote_age);
   if (ch == CHAR_EOF)
     {
 #ifdef DEBUG_INPUT
@@ -1381,6 +1458,17 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller)
 #endif /* DEBUG_INPUT */
       return TOKEN_MACDEF;
     }
+  if (ch == CHAR_ARGV)
+    {
+      init_argv_token (obs, td);
+#ifdef DEBUG_INPUT
+      xfprintf (stderr, "next_token -> ARGV (%d args)\n",
+		(arg_argc (td->u.u_c.chain->u.u_a.argv)
+		 - td->u.u_c.chain->u.u_a.index
+		 - (td->u.u_c.chain->u.u_a.skip_last ? 1 : 0)));
+#endif
+      return TOKEN_ARGV;
+    }
 
   next_char (false); /* Consume character we already peeked at.  */
   file = current_file;
@@ -1409,7 +1497,8 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller)
   else if (default_word_regexp && (isalpha (ch) || ch == '_'))
     {
       obstack_1grow (&token_stack, ch);
-      while ((ch = peek_input ()) < CHAR_EOF && (isalnum (ch) || ch == '_'))
+      while ((ch = peek_input (false)) < CHAR_EOF
+	     && (isalnum (ch) || ch == '_'))
 	{
 	  obstack_1grow (&token_stack, ch);
 	  next_char (false);
@@ -1424,7 +1513,7 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller)
       obstack_1grow (&token_stack, ch);
       while (1)
 	{
-	  ch = peek_input ();
+	  ch = peek_input (false);
 	  if (ch >= CHAR_EOF)
 	    break;
 	  obstack_1grow (&token_stack, ch);
@@ -1547,9 +1636,19 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller)
 		  token_type_string (type));
 	while (chain)
 	  {
-	    assert (chain->type == CHAIN_STR);
-	    xfprintf (stderr, "%s", chain->u.u_s.str);
-	    len += chain->u.u_s.len;
+	    switch (chain->type)
+	      {
+	      case CHAIN_STR:
+		xfprintf (stderr, "%s", chain->u.u_s.str);
+		len += chain->u.u_s.len;
+		break;
+	      case CHAIN_ARGV:
+		xfprintf (stderr, "{$@}");
+		break;
+	      default:
+		assert (!"next_token");
+		abort ();
+	      }
 	    links++;
 	    chain = chain->next;
 	  }
@@ -1569,7 +1668,7 @@ token_type
 peek_token (void)
 {
   token_type result;
-  int ch = peek_input ();
+  int ch = peek_input (false);
 
   if (ch == CHAR_EOF)
     {
@@ -1684,7 +1783,7 @@ lex_debug (void)
   token_type t;
   token_data td;
 
-  while ((t = next_token (&td, NULL, NULL, "<debug>")) != TOKEN_EOF)
+  while ((t = next_token (&td, NULL, NULL, false, "<debug>")) != TOKEN_EOF)
     print_token ("lex", t, &td);
 }
 #endif /* DEBUG_INPUT */
diff --git a/src/m4.h b/src/m4.h
index 0f11366..7df29b8 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -266,7 +266,8 @@ enum token_type
   TOKEN_COMMA,	/* Active character `,', TOKEN_TEXT.  */
   TOKEN_CLOSE,	/* Active character `)', TOKEN_TEXT.  */
   TOKEN_SIMPLE,	/* Any other single character, TOKEN_TEXT.  */
-  TOKEN_MACDEF	/* A macro's definition (see "defn"), TOKEN_FUNC.  */
+  TOKEN_MACDEF,	/* A macro's definition (see "defn"), TOKEN_FUNC.  */
+  TOKEN_ARGV	/* A series of parameters, TOKEN_COMP.  */
 };
 
 /* The data for a token, a macro argument, and a macro definition.  */
@@ -309,6 +310,7 @@ struct token_chain
 	  unsigned int 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 string_pair *quotes;	/* NULL for $*, quotes for $@.  */
 	}
       u_a;
@@ -373,7 +375,8 @@ typedef enum token_data_type token_data_type;
 
 void input_init (void);
 token_type peek_token (void);
-token_type next_token (token_data *, int *, struct obstack *, const char *);
+token_type next_token (token_data *, int *, struct obstack *, bool,
+		       const char *);
 void skip_line (const char *);
 
 /* push back input */
diff --git a/src/macro.c b/src/macro.c
index d686b73..8b85cf6 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -216,7 +216,7 @@ expand_input (void)
   TOKEN_DATA_ORIG_TEXT (&empty_token) = "";
 #endif
 
-  while ((t = next_token (&td, &line, NULL, NULL)) != TOKEN_EOF)
+  while ((t = next_token (&td, &line, NULL, false, NULL)) != TOKEN_EOF)
     expand_token (NULL, t, &td, line, true);
 
   for (i = 0; i < stacks_count; i++)
@@ -364,7 +364,7 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller)
   /* Skip leading white space.  */
   do
     {
-      t = next_token (&td, NULL, obs, caller);
+      t = next_token (&td, NULL, obs, true, caller);
     }
   while (t == TOKEN_SIMPLE && isspace (to_uchar (*TOKEN_DATA_TEXT (&td))));
 
@@ -455,6 +455,20 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller)
 	    }
 	  break;
 
+	case TOKEN_ARGV:
+	  assert (paren_level == 0 && TOKEN_DATA_TYPE (argp) == TOKEN_VOID
+		  && obstack_object_size (obs) == 0
+		  && td.u.u_c.chain == td.u.u_c.end
+		  && td.u.u_c.chain->type == CHAIN_ARGV);
+	  TOKEN_DATA_TYPE (argp) = TOKEN_COMP;
+	  argp->u.u_c.chain = argp->u.u_c.end = td.u.u_c.chain;
+	  t = next_token (&td, NULL, NULL, false, caller);
+	  if (argp->u.u_c.chain->u.u_a.skip_last)
+	    assert (t == TOKEN_COMMA);
+	  else
+	    assert (t == TOKEN_COMMA || t == TOKEN_CLOSE);
+	  return t == TOKEN_COMMA;
+
 	default:
 	  assert (!"expand_argument");
 	  abort ();
@@ -462,7 +476,7 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller)
 
       if (TOKEN_DATA_TYPE (argp) != TOKEN_VOID || obstack_object_size (obs))
 	first = false;
-      t = next_token (&td, NULL, obs, caller);
+      t = next_token (&td, NULL, obs, first, caller);
     }
 }
 
@@ -496,7 +510,8 @@ collect_arguments (symbol *sym, struct obstack *arguments,
 
   if (peek_token () == TOKEN_OPEN)
     {
-      next_token (&td, NULL, NULL, SYMBOL_NAME (sym)); /* gobble parenthesis */
+      /* gobble parenthesis */
+      next_token (&td, NULL, NULL, false, SYMBOL_NAME (sym));
       do
 	{
 	  tdp = (token_data *) obstack_alloc (arguments, sizeof *tdp);
@@ -519,12 +534,22 @@ collect_arguments (symbol *sym, struct obstack *arguments,
 	      && TOKEN_DATA_QUOTE_AGE (tdp) != args.quote_age)
 	    args.quote_age = 0;
 	  else if (TOKEN_DATA_TYPE (tdp) == TOKEN_COMP)
-	    args.has_ref = true;
+	    {
+	      args.has_ref = true;
+	      if (tdp->u.u_c.chain->type == CHAIN_ARGV)
+		{
+		  args.argc += (tdp->u.u_c.chain->u.u_a.argv->argc
+				- tdp->u.u_c.chain->u.u_a.index
+				- tdp->u.u_c.chain->u.u_a.skip_last - 1);
+		  args.wrapper = true;
+		}
+	    }
 	}
       while (more_args);
     }
   argv = (macro_arguments *) obstack_finish (argv_stack);
   argv->argc = args.argc;
+  argv->wrapper = args.wrapper;
   argv->has_ref = args.has_ref;
   if (args.quote_age != quote_age ())
     argv->quote_age = 0;
@@ -734,9 +759,20 @@ arg_adjust_refcount (macro_arguments *argv, bool increase)
 	  chain = argv->array[i]->u.u_c.chain;
 	  while (chain)
 	    {
-	      assert (chain->type == CHAIN_STR);
-	      if (chain->u.u_s.level >= 0)
-		adjust_refcount (chain->u.u_s.level, increase);
+	      switch (chain->type)
+		{
+		case CHAIN_STR:
+		  if (chain->u.u_s.level >= 0)
+		    adjust_refcount (chain->u.u_s.level, increase);
+		  break;
+		case CHAIN_ARGV:
+		  assert (chain->u.u_a.argv->inuse);
+		  arg_adjust_refcount (chain->u.u_a.argv, increase);
+		  break;
+		default:
+		  assert (!"arg_adjust_refcount");
+		  abort ();
+		}
 	      chain = chain->next;
 	    }
 	}
@@ -766,12 +802,14 @@ arg_token (macro_arguments *argv, unsigned int index, int *level)
   for (i = 0; i < argv->arraylen; i++)
     {
       token = argv->array[i];
-      if (TOKEN_DATA_TYPE (token) == TOKEN_COMP)
+      if (TOKEN_DATA_TYPE (token) == TOKEN_COMP
+	  && token->u.u_c.chain->type == CHAIN_ARGV)
 	{
 	  token_chain *chain = token->u.u_c.chain;
 	  /* TODO - for now we support only a single-length $@ chain.  */
-	  assert (!chain->next && chain->type == 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))
 	    {
 	      token = arg_token (chain->u.u_a.argv,
 				 chain->u.u_a.index - 1 + index, level);
@@ -780,7 +818,8 @@ arg_token (macro_arguments *argv, unsigned int index, int *level)
 		token = &empty_token;
 	      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;
@@ -793,18 +832,24 @@ arg_token (macro_arguments *argv, unsigned int index, int *level)
 static void
 arg_mark (macro_arguments *argv)
 {
+  unsigned int i;
+  token_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
-	      && TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP
-	      && !argv->array[0]->u.u_c.chain->next
-	      && argv->array[0]->u.u_c.chain->type == CHAIN_ARGV);
-      argv->array[0]->u.u_c.chain->u.u_a.argv->inuse = true;
-    }
+    for (i = 0; i < argv->arraylen; i++)
+      if (TOKEN_DATA_TYPE (argv->array[i]) == TOKEN_COMP)
+	{
+	  chain = argv->array[i]->u.u_c.chain;
+	  while (chain)
+	    {
+	      if (chain->type == CHAIN_ARGV && !chain->u.u_a.argv->inuse)
+		arg_mark (chain->u.u_a.argv);
+	      chain = chain->next;
+	    }
+	}
 }
 
 /* Given ARGV, return how many arguments it refers to.  */
@@ -854,14 +899,24 @@ arg_text (macro_arguments *argv, unsigned int index)
     case TOKEN_TEXT:
       return TOKEN_DATA_TEXT (token);
     case TOKEN_COMP:
-      /* TODO - concatenate multiple arguments?  For now, we assume
-	 all elements are text.  */
+      /* TODO - concatenate functions.  */
       chain = token->u.u_c.chain;
       obs = arg_scratch ();
       while (chain)
 	{
-	  assert (chain->type == CHAIN_STR);
-	  obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
+	  switch (chain->type)
+	    {
+	    case CHAIN_STR:
+	      obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
+	      break;
+	    case CHAIN_ARGV:
+	      arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
+			 chain->u.u_a.quotes, NULL);
+	      break;
+	    default:
+	      assert (!"arg_text");
+	      abort ();
+	    }
 	  chain = chain->next;
 	}
       obstack_1grow (obs, '\0');
@@ -1122,13 +1177,13 @@ make_argv_ref_token (token_data *token, struct obstack *obs, int level,
   token_chain *chain;
 
   assert (obstack_object_size (obs) == 0);
-  if (argv->wrapper)
+  if (argv->wrapper && argv->arraylen == 1)
     {
       /* TODO for now we support only a single-length $@ chain.  */
-      assert (argv->arraylen == 1
-	      && TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP);
+      assert (TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP);
       chain = argv->array[0]->u.u_c.chain;
-      assert (!chain->next && chain->type == CHAIN_ARGV);
+      assert (!chain->next && chain->type == CHAIN_ARGV
+	      && !chain->u.u_a.skip_last);
       argv = chain->u.u_a.argv;
       index += chain->u.u_a.index - 1;
     }
@@ -1145,6 +1200,7 @@ make_argv_ref_token (token_data *token, struct obstack *obs, int 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 a subsequent
diff --git a/src/symtab.c b/src/symtab.c
index 277a79f..dac49d7 100644
--- a/src/symtab.c
+++ b/src/symtab.c
@@ -350,7 +350,7 @@ symtab_debug (void)
   int delete;
   static int i;
 
-  while (next_token (&td, NULL, NULL, "<debug>") == TOKEN_WORD)
+  while (next_token (&td, NULL, NULL, false, "<debug>") == TOKEN_WORD)
     {
       text = TOKEN_DATA_TEXT (&td);
       if (*text == '_')


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.