[SCM] GNU M4 source repository branch, branch-1.6, updated. v1.4.10b-26-g6b67f52

"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=6b67f52903f191b8e0b9c295182a8e6504fb4f96

The branch, branch-1.6 has been updated
       via  6b67f52903f191b8e0b9c295182a8e6504fb4f96 (commit)
      from  fb04a26fa6ac38cbe5517372b1a984af0c851876 (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 6b67f52903f191b8e0b9c295182a8e6504fb4f96
Author: Eric Blake <[email protected]>
Date:   Thu Dec 6 13:33:40 2007 -0700

    Stage 21: $@ concatenates builtins, m4wrap takes builtins.
    
    * src/m4.h (append_macro): New prototype.
    (push_macro, push_wrapup_init, arg_print, func_print): Alter
    prototypes.
    * src/input.c (INPUT_MACRO): Delete, covered by INPUT_CHAIN.
    (INPUT_EOF): New input block type, for efficiency.
    (struct input_block): Remove u.func member.
    (input_eof): New input sentinel.
    (append_macro): New function.
    (push_macro, push_wrapup_init): Add parameter.
    (push_token): Support builtin tokens.
    (init_macro_token): Always use chain for builtin tokens.
    (push_string_init, pop_input, pop_wrapup, input_print)
    (peek_input, next_char, next_char_1, input_init): Adjust callers.
    * src/macro.c (arg_equal, wrap_args): Handle builtin tokens.
    (arg_print): Add parameter.
    (collect_arguments, arg_type, arg_text): Adjust callers.
    * src/builtin.c (m4_m4wrap): Handle builtin tokens.
    (func_print): Add parameter.
    (m4_defn): Allow pushing builtin alongside other text.
    (m4_errprint): Adjust caller.
    * src/debug.c (trace_pre): Likewise.
    * doc/m4.texinfo (Defn, Ifelse, Debug Levels): Update tests to new
    behavior.
    (M4wrap): New test.
    
    (cherry picked from commit 32d4bf4d447e0e252c809897b795b57b3bfd74de)
    
    Signed-off-by: Eric Blake <[email protected]>

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

Summary of changes:
 .cvsignore     |    2 +-
 .gitignore     |    2 +-
 ChangeLog      |   35 ++++++++++
 doc/m4.texinfo |   84 +++++++++++++----------
 src/builtin.c  |   26 ++++---
 src/debug.c    |    2 +-
 src/input.c    |  203 +++++++++++++++++++++++++++-----------------------------
 src/m4.h       |   12 ++-
 src/macro.c    |   86 +++++++++++++-----------
 9 files changed, 253 insertions(+), 199 deletions(-)

diff --git a/.cvsignore b/.cvsignore
index 8fc44d6..493776c 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -17,6 +17,7 @@ configure.lineno
 COPYING
 depcomp
 gendocs.sh
+GNUmakefile
 gnupload
 INSTALL
 install-sh
@@ -30,4 +31,3 @@ stamp-h
 stamp-h1
 stamp-h.in
 tests
-GNUmakefile
diff --git a/.gitignore b/.gitignore
index b581eac..fae2fd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ COPYING
 CVS
 depcomp
 gendocs.sh
+GNUmakefile
 gnupload
 INSTALL
 install-sh
@@ -34,4 +35,3 @@ stamp-h
 stamp-h1
 stamp-h.in
 tests
-GNUmakefile
diff --git a/ChangeLog b/ChangeLog
index 8501d11..a9fc7f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2008-04-14  Eric Blake  <[email protected]>
+
+	Stage 21: $@ concatenates builtins, m4wrap takes builtins.
+	Create a new input block type, which always fails with CHAR_EOF,
+	so that remaining input routines no longer have to check for NULL
+	input block.  Improve arg_print to handle builtin tokens when
+	printing to a known chain, rather than always flattening builtins,
+	allowing m4wrap and $@ references to handle embedded builtins.
+	Memory impact: none.
+	Speed impact: noticeable improvement, from fewer conditionals.
+	* src/m4.h (append_macro): New prototype.
+	(push_macro, push_wrapup_init, arg_print, func_print): Alter
+	prototypes.
+	* src/input.c (INPUT_MACRO): Delete, covered by INPUT_CHAIN.
+	(INPUT_EOF): New input block type, for efficiency.
+	(struct input_block): Remove u.func member.
+	(input_eof): New input sentinel.
+	(append_macro): New function.
+	(push_macro, push_wrapup_init): Add parameter.
+	(push_token): Support builtin tokens.
+	(init_macro_token): Always use chain for builtin tokens.
+	(push_string_init, pop_input, pop_wrapup, input_print)
+	(peek_input, next_char, next_char_1, input_init): Adjust callers.
+	* src/macro.c (arg_equal, wrap_args): Handle builtin tokens.
+	(arg_print): Add parameter.
+	(collect_arguments, arg_type, arg_text): Adjust callers.
+	* src/builtin.c (m4_m4wrap): Handle builtin tokens.
+	(func_print): Add parameter.
+	(m4_defn): Allow pushing builtin alongside other text.
+	(m4_errprint): Adjust caller.
+	* src/debug.c (trace_pre): Likewise.
+	* doc/m4.texinfo (Defn, Ifelse, Debug Levels): Update tests to new
+	behavior.
+	(M4wrap): New test.
+
 2008-04-11  Eric Blake  <[email protected]>
 
 	Ensure --program-prefix doesn't regress.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 4a523c6..52fc77b 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -2281,10 +2281,11 @@ bar
 @result{}0
 @end example
 
-Also note that @code{defn} with multiple arguments can only join text
-macros, not builtins.  Likewise, when collecting macro arguments, a
-builtin token is preserved only when it occurs in isolation.  A future
-version of @acronym{GNU} M4 may lift these restrictions.
+Also note that as of M4 1.6, @code{defn} with multiple arguments can
+join text with builtin tokens.  However, when collecting macro
+arguments, a builtin token is preserved only when it occurs in
+isolation.  A future version of @acronym{GNU} M4 may lift this
+restriction.
 
 @example
 $ @kbd{m4 -d}
@@ -2293,13 +2294,12 @@ define(`a', `A')define(`AA', `b')
 traceon(`defn', `define')
 @result{}
 defn(`a', `divnum', `a')
-@error{}m4:stdin:3: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4trace: -1- defn(`a', `divnum', `a') -> ``A'`A''
+@error{}m4trace: -1- defn(`a', `divnum', `a') -> ``A'<divnum>`A''
 @result{}AA
 define(`mydivnum', defn(`divnum', `divnum'))mydivnum
-@error{}m4:stdin:4: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4:stdin:4: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4trace: -2- defn(`divnum', `divnum')
+@error{}m4trace: -2- defn(`divnum', `divnum') -> `<divnum><divnum>'
+@error{}m4:stdin:4: Warning: define: cannot concatenate builtin `divnum'
+@error{}m4:stdin:4: Warning: define: cannot concatenate builtin `divnum'
 @error{}m4trace: -1- define(`mydivnum', `')
 @result{}
 traceoff(`defn', `define')
@@ -2317,10 +2317,10 @@ define(`mydivnum', `a'defn(`divnum'))mydivnum
 define(`q', ``$@@'')
 @result{}
 define(`foo', q(`a', defn(`divnum')))foo
-@error{}m4:stdin:10: Warning: define: cannot quote builtin
-@result{}a,
+@error{}m4:stdin:10: Warning: define: cannot concatenate builtins
+@result{}foo
 ifdef(`foo', `yes', `no')
-@result{}yes
+@result{}no
 @end example
 
 @node Pushdef
@@ -2860,8 +2860,8 @@ ifelse(`-01234567890123456789', `-'e(long)`-', `yes', `no')
 @result{}no
 @end example
 
-@comment It would be nice to pass builtin tokens through m4wrap, as well
-@comment as allowing concatenation of builtins in ifelse and user macros.
+@comment It would be nice to allow concatenation of builtins without
+@comment using $@ handling.
 @example
 define(`e', `$@@')define(`q', ``$@@'')define(`u', `$*')
 @result{}
@@ -2881,33 +2881,25 @@ cmp(`q(defn(`defn'))', `q(`<defn>')')-fixme
 cmp(`q(defn(`defn'))', ``'')-fixme
 @error{}m4:stdin:7: Warning: ifelse: cannot quote builtin
 @result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))')-fixme
-@error{}m4:stdin:8: Warning: ifelse: cannot quote builtin
-@error{}m4:stdin:8: Warning: ifelse: cannot quote builtin
-@result{}yes-fixme
-cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')')-fixme
-@error{}m4:stdin:9: Warning: ifelse: cannot quote builtin
-@result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', ```1',`2',<defn>'')-fixme
-@error{}m4:stdin:10: Warning: ifelse: cannot quote builtin
-@result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''')-fixme
-@error{}m4:stdin:11: Warning: ifelse: cannot quote builtin
-@result{}yes-fixme
+cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))')
+@result{}yes
+cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')')
+@result{}no
+cmp(`q(`1', `2', defn(`defn'))', ```1',`2',<defn>'')
+@result{}no
+cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''')
+@result{}no
 define(`cat', `$1`'ifelse(`$#', `1', `', `$0(shift($@@))')')
 @result{}
-cat(`define(`foo',', defn(`divnum'), `)foo')-fixme
-@error{}m4:stdin:13: Warning: ifelse: cannot quote builtin
-@result{}-fixme
-cat(e(`define(`bar',', defn(`divnum'), `)bar'))-fixme
-@error{}m4:stdin:14: Warning: ifelse: cannot quote builtin
-@result{}-fixme
-m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`)-fixme
+cat(`define(`foo',', defn(`divnum'), `)foo')
+@result{}0
+cat(e(`define(`bar',', defn(`divnum'), `)bar'))
+@result{}0
+m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`)
 ')
-@error{}m4:stdin:15: Warning: m4wrap: cannot quote builtin
 @result{}
 ^D
-@result{}-fixme
+@result{}0
 @end example
 @end ignore
 
@@ -3811,7 +3803,7 @@ 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: -2- defn(`change...') -> `<changequote>'
 @error{}m4trace: -1- indir(`echo', <changequote>) -> ``<changequote>''
 @result{}
 @end example
@@ -4723,6 +4715,24 @@ m4wrap(`m4wrap(`)')len(abc')
 @error{}m4:stdin:1: len: end of file in argument list
 @end example
 
+As of M4 1.6, @code{m4wrap} transparently handles builtin tokens
+generated by @code{defn} (@pxref{Defn}).  However, for portability, it
+is better to defer the evaluation of @code{defn} along with the rest of
+the wrapped text, as is done for @code{foo} in the example below, rather
+than computing the builtin token up front, as is done for @code{bar}.
+
+@example
+m4wrap(`define(`foo', defn(`divnum'))foo
+')
+@result{}
+m4wrap(`define(`bar', ')m4wrap(defn(`divnum'))m4wrap(`)bar
+')
+@result{}
+^D
+@result{}0
+@result{}0
+@end example
+
 @node File Inclusion
 @chapter File inclusion
 
diff --git a/src/builtin.c b/src/builtin.c
index e9856a8..07d2ce0 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -123,7 +123,7 @@ builtin_tab[] =
   { "indir",		true,	true,	true,	m4_indir },
   { "len",		false,	false,	true,	m4_len },
   { "m4exit",		false,	false,	false,	m4_m4exit },
-  { "m4wrap",		false,	false,	true,	m4_m4wrap },
+  { "m4wrap",		false,	true,	true,	m4_m4wrap },
   { "maketemp",		false,	false,	true,	m4_maketemp },
   { "mkstemp",		false,	false,	true,	m4_mkstemp },
   { "patsubst",		true,	false,	true,	m4_patsubst },
@@ -204,19 +204,25 @@ find_builtin_by_name (const char *name)
 
 /*------------------------------------------------------------------.
 | Print a representation of FUNC to OBS.  If FLATTEN, output QUOTES |
-| around an empty string instead.                                   |
+| around an empty string instead; else if CHAIN, append the builtin |
+| to the chain; otherwise print the name of FUNC.                   |
 `------------------------------------------------------------------*/
 void
 func_print (struct obstack *obs, const builtin *func, bool flatten,
-	    const string_pair *quotes)
+	    token_chain **chain, const string_pair *quotes)
 {
   assert (func);
-  if (flatten && quotes)
+  if (flatten)
     {
-      obstack_grow (obs, quotes->str1, quotes->len1);
-      obstack_grow (obs, quotes->str2, quotes->len2);
+      if (quotes)
+	{
+	  obstack_grow (obs, quotes->str1, quotes->len1);
+	  obstack_grow (obs, quotes->str2, quotes->len2);
+	}
     }
-  else if (!flatten)
+  else if (chain)
+    append_macro (obs, func->func, NULL, chain);
+  else
     {
       obstack_1grow (obs, '<');
       obstack_grow (obs, func->name, strlen (func->name));
@@ -1022,10 +1028,8 @@ m4_defn (struct obstack *obs, int argc, macro_arguments *argv)
 	    m4_warn (0, me,
 		     _("builtin `%s' requested by frozen file not found"),
 		     ARG (i));
-	  else if (argc != 2)
-	    m4_warn (0, me, _("cannot concatenate builtin `%s'"), ARG (i));
 	  else
-	    push_macro (b);
+	    push_macro (obs, b);
 	  break;
 
 	default:
@@ -1548,7 +1552,7 @@ m4_errprint (struct obstack *obs, int argc, macro_arguments *argv)
 
   if (bad_argc (ARG (0), argc, 1, -1))
     return;
-  arg_print (obs, argv, 1, NULL, true, " ", NULL, false);
+  arg_print (obs, argv, 1, NULL, true, NULL, " ", NULL, false);
   debug_flush_files ();
   len = obstack_object_size (obs);
   /* The close_stdin module makes it safe to skip checking the return
diff --git a/src/debug.c b/src/debug.c
index 46e1306..fde6c49 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -368,7 +368,7 @@ trace_pre (const char *name, int id, macro_arguments *argv)
       trace_format ("(");
       arg_print (&trace, argv, 1,
 		 (debug_level & DEBUG_TRACE_QUOTE) ? &curr_quote : NULL,
-		 false, ", ", &len, true);
+		 false, NULL, ", ", &len, true);
       trace_format (")");
     }
 
diff --git a/src/input.c b/src/input.c
index 86db704..3a913b7 100644
--- a/src/input.c
+++ b/src/input.c
@@ -72,10 +72,10 @@
 /* Type of an input block.  */
 enum input_type
 {
-  INPUT_STRING,		/* String resulting from macro expansion.  */
-  INPUT_FILE,		/* File from command line or include.  */
-  INPUT_MACRO,		/* Builtin resulting from defn.  */
-  INPUT_CHAIN		/* FIFO chain of separate strings and $@ refs.  */
+  INPUT_STRING,	/* String resulting from macro expansion.  */
+  INPUT_FILE,	/* File from command line or include.  */
+  INPUT_CHAIN,	/* FIFO chain of separate strings, builtins, and $@ refs.  */
+  INPUT_EOF	/* Placeholder at bottom of input stack.  */
 };
 
 typedef enum input_type input_type;
@@ -103,7 +103,6 @@ struct input_block
 	  bool_bitfield advance : 1; /* Track previous start_of_input_line.  */
 	}
 	u_f;	/* INPUT_FILE */
-      builtin_func *func;	/* INPUT_MACRO */
       struct
 	{
 	  token_chain *chain;	/* Current link in chain.  */
@@ -136,15 +135,19 @@ static struct obstack *current_input;
 /* Bottom of token_stack, for obstack_free.  */
 static void *token_bottom;
 
-/* Pointer to top of current_input.  */
+/* Pointer to top of current_input, never NULL.  */
 static input_block *isp;
 
-/* Pointer to top of wrapup_stack.  */
+/* Pointer to top of wrapup_stack, never NULL.  */
 static input_block *wsp;
 
-/* Aux. for handling split push_string ().  */
+/* Auxiliary for handling split push_string (), NULL if not pushing
+   text for rescanning.  */
 static input_block *next;
 
+/* Marker at the end of the input stack.  */
+static input_block input_eof = { NULL, INPUT_EOF, "", 0 };
+
 /* Flag for next_char () to increment current_line.  */
 static bool start_of_input_line;
 
@@ -265,33 +268,50 @@ push_file (FILE *fp, const char *title, bool close)
   isp = i;
 }
 
-/*-----------------------------------------------------------------.
-| push_macro () pushes the builtin macro FUNC on the input stack.  |
-| If next is non-NULL, this push invalidates a call to             |
-| push_string_init (), whose storage is consequently released.     |
-`-----------------------------------------------------------------*/
-
+/*------------------------------------------------------------------.
+| Given an obstack OBS, capture any unfinished text as a link, then |
+| append the builtin FUNC as the next link in the chain that starts |
+| at *START and ends at *END.  START may be NULL if *END is         |
+| non-NULL.                                                         |
+`------------------------------------------------------------------*/
 void
-push_macro (builtin_func *func)
+append_macro (struct obstack *obs, builtin_func *func, token_chain **start,
+	      token_chain **end)
 {
-  input_block *i;
-
-  if (next != NULL)
-    {
-      obstack_free (current_input, next);
-      next = NULL;
-    }
+  token_chain *chain;
 
   assert (func);
-  i = (input_block *) obstack_alloc (current_input, sizeof *i);
-  i->type = INPUT_MACRO;
-  i->file = current_file;
-  i->line = current_line;
-  input_change = true;
+  make_text_link (obs, start, end);
+  chain = (token_chain *) obstack_alloc (obs, sizeof *chain);
+  if (*end)
+    (*end)->next = chain;
+  else
+    *start = chain;
+  *end = chain;
+  chain->next = NULL;
+  chain->type = CHAIN_FUNC;
+  chain->quote_age = 0;
+  chain->u.func = func;
+}
 
-  i->u.func = func;
-  i->prev = isp;
-  isp = i;
+/*------------------------------------------------------------------.
+| push_macro () pushes the builtin FUNC onto the obstack OBS, which |
+| is either the input or wrapup stack.                              |
+`------------------------------------------------------------------*/
+
+void
+push_macro (struct obstack *obs, builtin_func *func)
+{
+  input_block *block = (obs == current_input ? next : wsp);
+  assert (block);
+  if (block->type == INPUT_STRING)
+    {
+      block->type = INPUT_CHAIN;
+      block->u.u_c.chain = block->u.u_c.end = NULL;
+    }
+  else
+    assert (block->type == INPUT_CHAIN);
+  append_macro (obs, func, &block->u.u_c.chain, &block->u.u_c.end);
 }
 
 /*--------------------------------------------------------------.
@@ -304,7 +324,7 @@ push_string_init (void)
 {
   /* Free any memory occupied by completely parsed strings.  */
   assert (next == NULL);
-  while (isp && pop_input (false));
+  while (pop_input (false));
 
   /* Reserve the next location on the obstack.  */
   next = (input_block *) obstack_alloc (current_input, sizeof *next);
@@ -361,7 +381,18 @@ push_token (token_data *token, int level, bool inuse)
 	  return false;
 	}
     }
-  else if (TOKEN_DATA_TYPE (token) != TOKEN_FUNC)
+  else if (TOKEN_DATA_TYPE (token) == TOKEN_FUNC)
+    {
+      if (next->type == INPUT_STRING)
+	{
+	  next->type = INPUT_CHAIN;
+	  next->u.u_c.chain = next->u.u_c.end = NULL;
+	}
+      append_macro (current_input, TOKEN_DATA_FUNC (token), &next->u.u_c.chain,
+		    &next->u.u_c.end);
+      return false;
+    }
+  else
     {
       /* For composite tokens, if argv is already in use, creating
 	 additional references for long text segments is more
@@ -407,23 +438,15 @@ push_token (token_data *token, int level, bool inuse)
       adjust_refcount (level, true);
       inuse = true;
     }
-  else if (TOKEN_DATA_TYPE (token) == TOKEN_FUNC)
-    {
-      chain = (token_chain *) obstack_alloc (current_input, sizeof *chain);
-      if (next->u.u_c.end)
-	next->u.u_c.end->next = chain;
-      else
-	next->u.u_c.chain = chain;
-      next->u.u_c.end = chain;
-      chain->next = NULL;
-      chain->type = CHAIN_FUNC;
-      chain->quote_age = 0;
-      chain->u.func = TOKEN_DATA_FUNC (token);
-    }
   while (src_chain)
     {
-      /* TODO support func concatenation.  */
-      assert (src_chain->type != CHAIN_FUNC);
+      if (src_chain->type == CHAIN_FUNC)
+	{
+	  append_macro (current_input, src_chain->u.func, &next->u.u_c.chain,
+			&next->u.u_c.end);
+	  src_chain = src_chain->next;
+	  continue;
+	}
       if (level == -1)
 	{
 	  /* Nothing to copy, since link already lives on obstack.  */
@@ -526,13 +549,13 @@ push_string_finish (void)
 `--------------------------------------------------------------*/
 
 struct obstack *
-push_wrapup_init (void)
+push_wrapup_init (token_chain ***end)
 {
   input_block *i;
   token_chain *chain;
 
   assert (obstack_object_size (wrapup_stack) == 0);
-  if (wsp)
+  if (wsp != &input_eof)
     {
       i = wsp;
       assert (i->type == INPUT_CHAIN && i->u.u_c.end
@@ -559,6 +582,7 @@ push_wrapup_init (void)
   chain->quote_age = 0;
   chain->u.u_l.file = current_file;
   chain->u.u_l.line = current_line;
+  *end = &i->u.u_c.end;
   return wrapup_stack;
 }
 
@@ -596,12 +620,6 @@ pop_input (bool cleanup)
 	return false;
       break;
 
-    case INPUT_MACRO:
-      assert (!isp->u.func || !cleanup);
-      if (isp->u.func)
-	return false;
-      break;
-
     case INPUT_CHAIN:
       chain = isp->u.u_c.chain;
       assert (!chain || !cleanup);
@@ -658,6 +676,9 @@ pop_input (bool cleanup)
       output_current_line = -1;
       break;
 
+    case INPUT_EOF:
+      return false;
+
     default:
       assert (!"pop_input");
       abort ();
@@ -684,7 +705,7 @@ pop_wrapup (void)
   obstack_free (current_input, NULL);
   free (current_input);
 
-  if (wsp == NULL)
+  if (wsp == &input_eof)
     {
       /* End of the program.  Free all memory even though we are about
 	 to exit, since it makes leak detection easier.  */
@@ -703,7 +724,7 @@ pop_wrapup (void)
   obstack_init (wrapup_stack);
 
   isp = wsp;
-  wsp = NULL;
+  wsp = &input_eof;
   input_change = true;
 
   return true;
@@ -730,9 +751,6 @@ input_print (struct obstack *obs, const input_block *input)
       obstack_grow (obs, input->file, strlen (input->file));
       obstack_1grow (obs, '>');
       break;
-    case INPUT_MACRO:
-      func_print (obs, find_builtin_by_addr (input->u.func), false, NULL);
-      break;
     case INPUT_CHAIN:
       chain = input->u.u_c.chain;
       while (chain)
@@ -746,14 +764,14 @@ input_print (struct obstack *obs, const input_block *input)
 	      break;
 	    case CHAIN_FUNC:
 	      func_print (obs, find_builtin_by_addr (chain->u.func), false,
-			  NULL);
+			  NULL, NULL);
 	      break;
 	    case CHAIN_ARGV:
 	      assert (!chain->u.u_a.comma);
 	      if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
 			     quote_cache (NULL, chain->quote_age,
 					  chain->u.u_a.quotes),
-			     chain->u.u_a.flatten, NULL, &maxlen, false))
+			     chain->u.u_a.flatten, NULL, NULL, &maxlen, false))
 		return;
 	      break;
 	    default:
@@ -789,9 +807,7 @@ peek_input (bool allow_argv)
 
   while (1)
     {
-      if (block == NULL)
-	return CHAR_EOF;
-
+      assert (block);
       switch (block->type)
 	{
 	case INPUT_STRING:
@@ -809,11 +825,6 @@ peek_input (bool allow_argv)
 	  block->u.u_f.end = true;
 	  break;
 
-	case INPUT_MACRO:
-	  if (block->u.func)
-	    return CHAR_MACRO;
-	  break;
-
 	case INPUT_CHAIN:
 	  chain = block->u.u_c.chain;
 	  while (chain)
@@ -863,6 +874,9 @@ peek_input (bool allow_argv)
 	    }
 	  break;
 
+	case INPUT_EOF:
+	  return CHAR_EOF;
+
 	default:
 	  assert (!"peek_input");
 	  abort ();
@@ -887,7 +901,7 @@ peek_input (bool allow_argv)
 `-------------------------------------------------------------------*/
 
 #define next_char(AQ, AA)						\
-  (isp && isp->type == INPUT_STRING && isp->u.u_s.len && !input_change	\
+  (isp->type == INPUT_STRING && isp->u.u_s.len && !input_change		\
    ? (isp->u.u_s.len--, to_uchar (*isp->u.u_s.str++))			\
    : next_char_1 (AQ, AA))
 
@@ -899,13 +913,7 @@ next_char_1 (bool allow_quote, bool allow_argv)
 
   while (1)
     {
-      if (isp == NULL)
-	{
-	  current_file = "";
-	  current_line = 0;
-	  return CHAR_EOF;
-	}
-
+      assert (isp);
       if (input_change)
 	{
 	  current_file = isp->file;
@@ -940,11 +948,6 @@ next_char_1 (bool allow_quote, bool allow_argv)
 	    }
 	  break;
 
-	case INPUT_MACRO:
-	  if (isp->u.func)
-	    return CHAR_MACRO;
-	  break;
-
 	case INPUT_CHAIN:
 	  chain = isp->u.u_c.chain;
 	  while (chain)
@@ -1013,6 +1016,9 @@ next_char_1 (bool allow_quote, bool allow_argv)
 	    }
 	  break;
 
+	case INPUT_EOF:
+	  return CHAR_EOF;
+
 	default:
 	  assert (!"next_char_1");
 	  abort ();
@@ -1064,28 +1070,15 @@ init_macro_token (token_data *td)
 {
   token_chain *chain;
 
-  if (isp->type == INPUT_MACRO)
+  assert (isp->type == INPUT_CHAIN);
+  chain = isp->u.u_c.chain;
+  assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func);
+  if (td)
     {
-      assert (isp->u.func);
-      if (td)
-	{
-	  TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
-	  TOKEN_DATA_FUNC (td) = isp->u.func;
-	}
-      isp->u.func = NULL;
-    }
-  else
-    {
-      assert (isp->type == INPUT_CHAIN);
-      chain = isp->u.u_c.chain;
-      assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func);
-      if (td)
-	{
-	  TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
-	  TOKEN_DATA_FUNC (td) = chain->u.func;
-	}
-      chain->u.func = NULL;
+      TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
+      TOKEN_DATA_FUNC (td) = chain->u.func;
     }
+  chain->u.func = NULL;
 }
 
 /*-------------------------------------------------------------------.
@@ -1282,8 +1275,8 @@ input_init (void)
   obstack_init (&token_stack);
   token_bottom = obstack_finish (&token_stack);
 
-  isp = NULL;
-  wsp = NULL;
+  isp = &input_eof;
+  wsp = &input_eof;
   next = NULL;
 
   start_of_input_line = false;
diff --git a/src/m4.h b/src/m4.h
index 54dd9da..3e1fdbb 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -393,11 +393,13 @@ void skip_line (const char *);
 /* push back input */
 void make_text_link (struct obstack *, token_chain **, token_chain **);
 void push_file (FILE *, const char *, bool);
-void push_macro (builtin_func *);
+void append_macro (struct obstack *, builtin_func *, token_chain **,
+		   token_chain **);
+void push_macro (struct obstack *, builtin_func *);
 struct obstack *push_string_init (void);
 bool push_token (token_data *, int, bool);
 const input_block *push_string_finish (void);
-struct obstack *push_wrapup_init (void);
+struct obstack *push_wrapup_init (token_chain ***);
 void push_wrapup_finish (void);
 bool pop_wrapup (void);
 void input_print (struct obstack *, const input_block *);
@@ -509,7 +511,8 @@ 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 *, bool, const char *, size_t *, bool);
+		const string_pair *, bool, token_chain **, const char *,
+		size_t *, bool);
 macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t,
 				bool, bool);
 void push_arg (struct obstack *, macro_arguments *, unsigned int);
@@ -569,7 +572,8 @@ 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 *);
+void func_print (struct obstack *, const builtin *, bool, token_chain **,
+		 const string_pair *);
 
 /* File: path.c  --- path search for include files.  */
 
diff --git a/src/macro.c b/src/macro.c
index 6a6a90c..c435644 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -582,8 +582,7 @@ collect_arguments (symbol *sym, struct obstack *arguments,
   argv->wrapper = args.wrapper;
   argv->has_ref = args.has_ref;
   argv->has_func = args.has_func;
-  /* TODO allow funcs without crippling quote age.  */
-  if (args.quote_age != quote_age () || args.has_func)
+  if (args.quote_age != quote_age ())
     argv->quote_age = 0;
   argv->arraylen = args.arraylen;
   return argv;
@@ -910,8 +909,6 @@ arg_type (macro_arguments *argv, unsigned int index)
     type = TOKEN_TEXT;
   if (type != TOKEN_TEXT)
     assert (argv->has_func);
-  /* TODO support TOKEN_COMP meaning concatenation of builtins.  */
-  assert (type != TOKEN_COMP);
   return type;
 }
 
@@ -936,7 +933,6 @@ arg_text (macro_arguments *argv, unsigned int index)
     case TOKEN_TEXT:
       return TOKEN_DATA_TEXT (token);
     case TOKEN_COMP:
-      /* TODO - concatenate functions.  */
       chain = token->u.u_c.chain;
       obs = arg_scratch ();
       while (chain)
@@ -952,7 +948,7 @@ arg_text (macro_arguments *argv, unsigned int index)
 			 quote_cache (NULL, chain->quote_age,
 				      chain->u.u_a.quotes),
 			 argv->flatten || chain->u.u_a.flatten, NULL, NULL,
-			 false);
+			 NULL, false);
 	      break;
 	    default:
 	      assert (!"arg_text");
@@ -983,6 +979,7 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb)
   token_chain tmpb;
   token_chain *ca = &tmpa;
   token_chain *cb = &tmpb;
+  token_chain *chain;
   struct obstack *obs = arg_scratch ();
 
   /* Quick tests.  */
@@ -1041,30 +1038,34 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb)
     {
       if (ca->type == CHAIN_ARGV)
 	{
-	  tmpa.next = ca->next;
+	  tmpa.next = NULL;
 	  tmpa.type = CHAIN_STR;
-	  /* TODO support $@ with funcs.  */
-	  assert (!ca->u.u_a.has_func || argv->flatten || ca->u.u_a.flatten);
+	  tmpa.u.u_s.str = NULL;
+	  tmpa.u.u_s.len = 0;
+	  chain = &tmpa;
 	  arg_print (obs, ca->u.u_a.argv, ca->u.u_a.index,
 		     quote_cache (NULL, ca->quote_age, ca->u.u_a.quotes),
-		     argv->flatten || ca->u.u_a.flatten, NULL, NULL, false);
-	  tmpa.u.u_s.len = obstack_object_size (obs);
-	  tmpa.u.u_s.str = (char *) obstack_finish (obs);
-	  ca = &tmpa;
+		     argv->flatten || ca->u.u_a.flatten, &chain, NULL, NULL,
+		     false);
+	  assert (obstack_object_size (obs) == 0 && chain != &tmpa);
+	  chain->next = ca->next;
+	  ca = tmpa.next;
 	  continue;
 	}
       if (cb->type == CHAIN_ARGV)
 	{
-	  tmpb.next = cb->next;
+	  tmpb.next = NULL;
 	  tmpb.type = CHAIN_STR;
-	  /* TODO support $@ with funcs.  */
-	  assert (!cb->u.u_a.has_func || argv->flatten || cb->u.u_a.flatten);
+	  tmpb.u.u_s.str = NULL;
+	  tmpb.u.u_s.len = 0;
+	  chain = &tmpb;
 	  arg_print (obs, cb->u.u_a.argv, cb->u.u_a.index,
 		     quote_cache (NULL, cb->quote_age, cb->u.u_a.quotes),
-		     argv->flatten || cb->u.u_a.flatten, NULL, NULL, false);
-	  tmpb.u.u_s.len = obstack_object_size (obs);
-	  tmpb.u.u_s.str = (char *) obstack_finish (obs);
-	  cb = &tmpb;
+		     argv->flatten || cb->u.u_a.flatten, &chain, NULL, NULL,
+		     false);
+	  assert (obstack_object_size (obs) == 0 && chain != &tmpb);
+	  chain->next = cb->next;
+	  cb = tmpb.next;
 	  continue;
 	}
       if (ca->type == CHAIN_FUNC)
@@ -1223,18 +1224,21 @@ 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 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.  */
+   with those quotes.  If FLATTEN, builtins are converted to empty
+   quotes; if CHAINP, *CHAINP is updated with macro tokens; otherwise,
+   builtins are represented by their name.  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.  MAX_LEN and CHAINP may not both be
+   specified.  */
 bool
 arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
-	   const string_pair *quotes, bool flatten, const char *sep,
-	   size_t *max_len, bool quote_each)
+	   const string_pair *quotes, bool flatten, token_chain **chainp,
+	   const char *sep, size_t *max_len, bool quote_each)
 {
   size_t len = max_len ? *max_len : INT_MAX;
   unsigned int i;
@@ -1245,6 +1249,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
   size_t sep_len;
   size_t *plen = quote_each ? NULL : &len;
 
+  if (chainp)
+    assert (!max_len && *chainp);
   if (!sep)
     sep = ",";
   sep_len = strlen (sep);
@@ -1287,13 +1293,13 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
 		  break;
 		case CHAIN_FUNC:
 		  func_print (obs, find_builtin_by_addr (chain->u.func),
-			      flatten, quotes);
+			      flatten, chainp, quotes);
 		  break;
 		case CHAIN_ARGV:
 		  if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
 				 quote_cache (NULL, chain->quote_age,
 					      chain->u.u_a.quotes),
-				 flatten, NULL, &len, false))
+				 flatten, chainp, NULL, &len, false))
 		    done = true;
 		  break;
 		default:
@@ -1310,7 +1316,7 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
 	  break;
 	case TOKEN_FUNC:
 	  func_print (obs, find_builtin_by_addr (TOKEN_DATA_FUNC (token)),
-		      flatten, quotes);
+		      flatten, chainp, quotes);
 	  break;
 	default:
 	  assert (!"arg_print");
@@ -1319,6 +1325,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
     }
   if (max_len)
     *max_len = len;
+  else if (chainp)
+    make_text_link (obs, NULL, chainp);
   return false;
 }
 
@@ -1524,11 +1532,12 @@ wrap_args (macro_arguments *argv)
   struct obstack *obs;
   token_data *token;
   token_chain *chain;
+  token_chain **end;
 
   if ((argv->argc == 2 || no_gnu_extensions) && arg_empty (argv, 1))
     return;
 
-  obs = push_wrapup_init ();
+  obs = push_wrapup_init (&end);
   for (i = 1; i < (no_gnu_extensions ? 2 : argv->argc); i++)
     {
       if (i != 1)
@@ -1540,8 +1549,8 @@ wrap_args (macro_arguments *argv)
 	  obstack_grow (obs, TOKEN_DATA_TEXT (token), TOKEN_DATA_LEN (token));
 	  break;
 	case TOKEN_FUNC:
-	  /* TODO allow builtins through m4wrap.  */
-	  assert (false);
+	  append_macro (obs, TOKEN_DATA_FUNC (token), NULL, end);
+	  break;
 	case TOKEN_COMP:
 	  chain = token->u.u_c.chain;
 	  while (chain)
@@ -1552,14 +1561,13 @@ wrap_args (macro_arguments *argv)
 		  obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
 		  break;
 		case CHAIN_FUNC:
-		  /* TODO allow builtins through m4wrap.  */
-		  assert (false);
+		  append_macro (obs, chain->u.func, NULL, end);
 		  break;
 		case CHAIN_ARGV:
 		  arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
 			     quote_cache (NULL, chain->quote_age,
 					  chain->u.u_a.quotes),
-			     chain->u.u_a.flatten, NULL, NULL, false);
+			     chain->u.u_a.flatten, end, NULL, NULL, false);
 		  break;
 		default:
 		  assert (!"wrap_args");


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.