head: Re: branch-1_4 off-by-one in line reporting

Eric Blake <[email protected]>
Newsgroups gmane.comp.gnu.m4.patches
Message-ID <[email protected]>
Eric Blake <ebb9 <at> byu.net> writes:

> 
> And here's the port of all three patches in this thread to head.  It's big, 
but 
> I couldn't really break it up.

This broke some of the relatively new features on head, even though it passed 
the testsuite (for example, tracing include(foo) with the 'e' flag was showing 
the wrong filename).  So the obvious course of action - beef up the testsuite, 
then fix the fallout :)

I also pulled in the branch fix for making indir and builtin grok macro 
arguments.

2006-10-13  Eric Blake  <[email protected]>

	* configure.ac (AC_CONFIG_HEADERS): Name the template config.hin,
	not config-h.in.
	* modules/gnu.c (builtin, indir): Transparently handle builtin
	tokens from defn.
	* doc/m4.texinfo (Indir, Builtin): Remerge from branch.
	* m4/input.c (m4_pop_wrapup): Display debug message when switching
	to wrapped text.
	(m4_input_init): No need to use empty file any more.
	(file_print): Display correct file name.
	* m4/utility.c (m4_error, m4_warn): Ensure we don't try to
	dereference a NULL file name.
	* m4/m4module.h (m4_pop_wrapup): New parameter.
	(m4_symbol_value_groks_macro, m4_symbol_groks_macro): New
	prototypes.
	* m4/m4private.h (m4_symbol_value_groks_macro): Faster version.
	* m4/symtab.c (m4_symbol_value_groks_macro): New method.
	* src/main.c (main): Adjust caller.
	* tests/options.at (--debugmode): Test more output from -dV.

Index: configure.ac
===================================================================
RCS file: /sources/m4/m4/configure.ac,v
retrieving revision 1.57
diff -u -r1.57 configure.ac
--- configure.ac	6 Oct 2006 18:24:31 -0000	1.57
+++ configure.ac	13 Oct 2006 16:40:29 -0000
@@ -29,7 +29,7 @@
 AC_CONFIG_MACRO_DIR([ltdl/m4])
 AC_CONFIG_LIBOBJ_DIR([gnu])
 AC_CONFIG_TESTDIR([tests])
-AC_CONFIG_HEADERS([gnu/config.h:gnu/config-h.in])
+AC_CONFIG_HEADERS([gnu/config.h:gnu/config.hin])
 AC_CONFIG_FILES([tests/m4], [chmod +x tests/m4])
 
 
@@ -57,8 +57,7 @@
 dnl Autoconf recommends that packages use lowercase for their package-specific
 dnl prefix for cache variables.  But in the case of m4, that collides with
 dnl the m4_ namespace provided by m4sugar, so we prefer M4_ as our
-dnl package-specific prefix.  FIXME - consistently use M4_ in the m4 macros
-dnl that feed the creation of configure.
+dnl package-specific prefix.
 m4_pattern_forbid([^M4_[A-Z]])
 
 AC_DEFUN([M4_DEFAULT_PRELOAD], [m4 traditional gnu])
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.63
diff -u -r1.63 m4.texinfo
--- doc/m4.texinfo	12 Oct 2006 21:14:49 -0000	1.63
+++ doc/m4.texinfo	13 Oct 2006 16:40:29 -0000
@@ -2106,6 +2106,7 @@
 used.
 
 @example
+$ @kbd{m4 -d}
 define(`f', `1')
 @result{}
 f(define(`f', `2'))
@@ -2117,6 +2118,28 @@
 @result{}
 @end example
 
+When handed the result of @code{defn} (@pxref{Defn}) as one of its
+arguments, @code{indir} defers to the invoked @var{name} for whether a
+token representing a builtin is recognized or flattened to the empty
+string.
+
+@example
+$ @kbd{m4 -d}
+indir(defn(`defn'), `divnum')
+@error{}m4:stdin:1: Warning: indir: invalid macro name ignored
+@result{}
+indir(`define', defn(`defn'), `divnum')
+@error{}m4:stdin:2: Warning: define: invalid macro name ignored
+@result{}
+indir(`define', `foo', defn(`divnum'))
+@result{}
+foo
+@result{}0
+indir(`divert', defn(`foo'))
+@error{}m4:stdin:5: Warning: divert: empty string treated as 0
+@result{}
+@end example
+
 @node Builtin
 @section Indirect call of builtins
 
@@ -2147,6 +2170,10 @@
 @result{}hidden
 foo
 @result{}foo
+builtin(`define', `foo', defn(`divnum'))
+@result{}
+foo
+@result{}0
 builtin(`define', `foo', `BAR')
 @result{}
 foo
Index: m4/input.c
===================================================================
RCS file: /sources/m4/m4/m4/input.c,v
retrieving revision 1.52
diff -u -r1.52 input.c
--- m4/input.c	12 Oct 2006 23:21:34 -0000	1.52
+++ m4/input.c	13 Oct 2006 16:40:29 -0000
@@ -314,7 +314,8 @@
 static void
 file_print (m4_input_block *me, m4 *context, m4_obstack *obs)
 {
-  const char *text = m4_get_current_file (context);
+  const char *text = (me->funcs == &file_init_funcs ? me->u.u_f.name
+		      : m4_get_current_file (context));
   obstack_grow (obs, "<file: ", strlen ("<file: "));
   obstack_grow (obs, text, strlen (text));
   obstack_1grow (obs, '>');
@@ -646,8 +647,10 @@
    Since wrapup text can install new wrapup text, pop_wrapup () returns
    false when there is no wrapup text on the stack, and true otherwise.  */
 bool
-m4_pop_wrapup (void)
+m4_pop_wrapup (m4 *context)
 {
+  static size_t level = 0;
+
   next = NULL;
   obstack_free (current_input, NULL);
   free (current_input);
@@ -655,11 +658,18 @@
   if (wsp == NULL)
     {
       obstack_free (wrapup_stack, NULL);
+      m4_set_current_file (context, NULL);
+      m4_set_current_line (context, 0);
+      m4_debug_message (context, M4_DEBUG_TRACE_INPUT,
+			_("input from m4wrap exhausted"));
       current_input = NULL;
       DELETE (wrapup_stack);
       return false;
     }
 
+  m4_debug_message (context, M4_DEBUG_TRACE_INPUT,
+		    _("input from m4wrap recursion level %d"), ++level);
+
   current_input = wrapup_stack;
   wrapup_stack = (m4_obstack *) xmalloc (sizeof (m4_obstack));
   obstack_init (wrapup_stack);
@@ -897,19 +907,15 @@
 void
 m4_input_init (m4 *context)
 {
-  /* FIXME: The user should never be able to see the empty string as a
-     file name, even during m4wrap expansion.  */
-  m4_set_current_file (context, "");
+  obstack_init (&file_names);
+  m4_set_current_file (context, NULL);
   m4_set_current_line (context, 0);
 
-
   current_input = (m4_obstack *) xmalloc (sizeof (m4_obstack));
   obstack_init (current_input);
   wrapup_stack = (m4_obstack *) xmalloc (sizeof (m4_obstack));
   obstack_init (wrapup_stack);
 
-  obstack_init (&file_names);
-
   /* Allocate an object in the current chunk, so that obstack_free
      will always work even if the first token parsed spills to a new
      chunk.  */
Index: m4/m4module.h
===================================================================
RCS file: /sources/m4/m4/m4/m4module.h,v
retrieving revision 1.94
diff -u -r1.94 m4module.h
--- m4/m4module.h	12 Oct 2006 21:14:50 -0000	1.94
+++ m4/m4module.h	13 Oct 2006 16:40:29 -0000
@@ -229,6 +229,7 @@
 extern void	m4_symbol_print		(m4_symbol *, m4_obstack *, bool,
 					 const char *, const char *, bool,
 					 size_t, bool);
+extern bool	m4_symbol_value_groks_macro	(m4_symbol_value *);
 
 #define m4_is_symbol_void(symbol)					\
 	(m4_is_symbol_value_void (m4_get_symbol_value (symbol)))
@@ -244,6 +245,8 @@
 	(m4_get_symbol_value_func (m4_get_symbol_value (symbol)))
 #define m4_get_symbol_placeholder(symbol)				\
 	(m4_get_symbol_value_placeholder (m4_get_symbol_value (symbol)))
+#define m4_symbol_groks_macro(symbol)					\
+	(m4_symbol_value_groks_macro (m4_get_symbol_value (symbol)))
 
 extern m4_symbol_value *m4_symbol_value_create	  (void);
 extern void		m4_symbol_value_delete	  (m4_symbol_value *);
@@ -401,7 +404,7 @@
 extern	m4_obstack	*m4_push_string_init	(m4 *);
 extern	m4_input_block	*m4_push_string_finish	(void);
 extern	void	m4_push_wrapup	(m4 *, const char *);
-extern	bool	m4_pop_wrapup	(void);
+extern	bool	m4_pop_wrapup	(m4 *);
 extern	void	m4_input_print	(m4 *, m4_obstack *, m4_input_block *);
 
 
Index: m4/m4private.h
===================================================================
RCS file: /sources/m4/m4/m4/m4private.h,v
retrieving revision 1.66
diff -u -r1.66 m4private.h
--- m4/m4private.h	12 Oct 2006 21:14:50 -0000	1.66
+++ m4/m4private.h	13 Oct 2006 16:40:29 -0000
@@ -220,6 +220,8 @@
 #  define m4_get_symbol_value_func(V)	((V)->u.func)
 #  define m4_get_symbol_value_placeholder(V)				\
 					((V)->u.text)
+#  define m4_symbol_value_groks_macro(V) (BIT_TEST ((V)->flags,	
	\
+                                                    VALUE_MACRO_ARGS_BIT))
 
 #  define m4_set_symbol_value_text(V, T)				\
 	((V)->type = M4_SYMBOL_TEXT, (V)->u.text = (T))
Index: m4/symtab.c
===================================================================
RCS file: /sources/m4/m4/m4/symtab.c,v
retrieving revision 1.63
diff -u -r1.63 symtab.c
--- m4/symtab.c	28 Sep 2006 04:22:33 -0000	1.63
+++ m4/symtab.c	13 Oct 2006 16:40:29 -0000
@@ -580,6 +580,14 @@
   return xzalloc (sizeof (m4_symbol_value));
 }
 
+#undef m4_symbol_groks_macro
+bool
+m4_symbol_value_groks_macro (m4_symbol_value *value)
+{
+  assert (value);
+  return BIT_TEST (value->flags, VALUE_MACRO_ARGS_BIT);
+}
+
 #undef m4_get_symbol_value
 m4_symbol_value *
 m4_get_symbol_value (m4_symbol *symbol)
Index: m4/utility.c
===================================================================
RCS file: /sources/m4/m4/m4/utility.c,v
retrieving revision 1.53
diff -u -r1.53 utility.c
--- m4/utility.c	12 Oct 2006 21:14:50 -0000	1.53
+++ m4/utility.c	13 Oct 2006 16:40:29 -0000
@@ -127,6 +127,7 @@
 {
   va_list args;
   int line = m4_get_current_line (context);
+  assert (m4_get_current_file (context) || ! line);
   va_start (args, format);
   if (status == EXIT_SUCCESS && m4_get_fatal_warnings_opt (context))
     status = EXIT_FAILURE;
@@ -170,6 +171,8 @@
       int status = EXIT_SUCCESS;
       int line = m4_get_current_line (context);
       char *full_format = xasprintf(_("Warning: %s"), format);
+
+      assert (m4_get_current_file (context) || ! line);
       va_start (args, format);
       if (m4_get_fatal_warnings_opt (context))
 	status = EXIT_FAILURE;
Index: modules/gnu.c
===================================================================
RCS file: /sources/m4/m4/modules/gnu.c,v
retrieving revision 1.63
diff -u -r1.63 gnu.c
--- modules/gnu.c	9 Oct 2006 21:00:32 -0000	1.63
+++ modules/gnu.c	13 Oct 2006 16:40:29 -0000
@@ -46,7 +46,7 @@
   BUILTIN (__file__,	false,	false,	false,	0,	0 )	\
   BUILTIN (__line__,	false,	false,	false,	0,	0  )	\
   BUILTIN (__program__,	false,	false,	false,	0,	0  )	\
-  BUILTIN (builtin,	false,	true,	false,	1,	-1 )	\
+  BUILTIN (builtin,	true,	true,	false,	1,	-1 )	\
   BUILTIN (changeresyntax,false,true,	false,	1,	1  )	\
   BUILTIN (changesyntax,false,	true,	false,	1,	-1 )	\
   BUILTIN (debugfile,	false,	false,	false,	0,	1  )	\
@@ -54,7 +54,7 @@
   BUILTIN (debugmode,	false,	false,	false,	0,	1  )	\
   BUILTIN (esyscmd,	false,	true,	true,	1,	1  )	\
   BUILTIN (format,	false,	true,	false,	1,	-1 )	\
-  BUILTIN (indir,	false,	true,	false,	1,	-1 )	\
+  BUILTIN (indir,	true,	true,	false,	1,	-1 )	\
   BUILTIN (patsubst,	false,	true,	true,	2,	4  )	\
   BUILTIN (regexp,	false,	true,	true,	2,	4  )	\
   BUILTIN (renamesyms,	false,	true,	false,	2,	3  )	\
@@ -329,17 +329,27 @@
  **/
 M4BUILTIN_HANDLER (builtin)
 {
-  const m4_builtin *bp = NULL;
-  const char *name = M4ARG (1);
-
-  bp = m4_builtin_find_by_name (NULL, name);
+  if (! m4_is_symbol_value_text (argv[1]))
+    m4_warn (context, 0, _("%s: invalid macro name ignored"), M4ARG (0));
+  else
+    {
+      const char *name = M4ARG (1);
+      const m4_builtin *bp = m4_builtin_find_by_name (NULL, name);
 
-  if (bp == NULL)
-    m4_warn (context, 0, _("%s: undefined builtin `%s'"), M4ARG (0), name);
-  else if (!m4_bad_argc (context, argc - 1, argv + 1,
-			 bp->min_args, bp->max_args,
-			 (bp->flags & M4_BUILTIN_SIDE_EFFECT) != 0))
-    bp->func (context, obs, argc - 1, argv + 1);
+      if (bp == NULL)
+	m4_warn (context, 0, _("%s: undefined builtin `%s'"), M4ARG (0), name);
+      else if (!m4_bad_argc (context, argc - 1, argv + 1,
+			     bp->min_args, bp->max_args,
+			     (bp->flags & M4_BUILTIN_SIDE_EFFECT) != 0))
+	{
+	  int i;
+	  if ((bp->flags & M4_BUILTIN_GROKS_MACRO) == 0)
+	    for (i = 2; i < argc; i++)
+	      if (! m4_is_symbol_value_text (argv[i]))
+		m4_set_symbol_value_text (argv[i], "");
+	  bp->func (context, obs, argc - 1, argv + 1);
+	}
+    }
 }
 
 
@@ -537,14 +547,26 @@
  **/
 M4BUILTIN_HANDLER (indir)
 {
-  const char * name   = M4ARG (1);
-  m4_symbol *  symbol = m4_symbol_lookup (M4SYMTAB, name);
-
-  if (symbol == NULL)
-    m4_warn (context, 0, _("%s: undefined macro `%s'"), M4ARG (0), name);
+  if (! m4_is_symbol_value_text (argv[1]))
+    m4_warn (context, 0, _("%s: invalid macro name ignored"), M4ARG (0));
   else
-    m4_macro_call (context, m4_get_symbol_value (symbol), obs,
-		   argc - 1, argv + 1);
+    {
+      const char *name = M4ARG (1);
+      m4_symbol *symbol = m4_symbol_lookup (M4SYMTAB, name);
+
+      if (symbol == NULL)
+	m4_warn (context, 0, _("%s: undefined macro `%s'"), M4ARG (0), name);
+      else
+	{
+	  int i;
+	  if (! m4_symbol_groks_macro (symbol))
+	    for (i = 2; i < argc; i++)
+	      if (! m4_is_symbol_value_text (argv[i]))
+		m4_set_symbol_value_text (argv[i], "");
+	  m4_macro_call (context, m4_get_symbol_value (symbol), obs,
+			 argc - 1, argv + 1);
+	}
+    }
 }
 
 
Index: src/main.c
===================================================================
RCS file: /sources/m4/m4/src/main.c,v
retrieving revision 1.96
diff -u -r1.96 main.c
--- src/main.c	12 Oct 2006 02:44:27 -0000	1.96
+++ src/main.c	13 Oct 2006 16:40:29 -0000
@@ -652,7 +652,7 @@
       }
 
   /* Now handle wrapup text.  */
-  while (m4_pop_wrapup ())
+  while (m4_pop_wrapup (context))
     m4_macro_expand_input (context);
 
   if (frozen_file_to_write)
Index: tests/options.at
===================================================================
RCS file: /sources/m4/m4/tests/options.at,v
retrieving revision 1.20
diff -u -r1.20 options.at
--- tests/options.at	12 Oct 2006 21:14:50 -0000	1.20
+++ tests/options.at	13 Oct 2006 16:40:29 -0000
@@ -243,19 +243,24 @@
 AT_SETUP([--debugmode])
 
 AT_DATA([[in]],
-[[divnum
+[[include(`nested')dnl
 len(`abc')
 ]])
 
+AT_DATA([[nested]],
+[[m4wrap(`divnum
+')dnl
+]])
+
 dnl AT_CHECK_M4 starts life with -d.  Make sure it looks like -daeq.
-AT_CHECK_M4([-tlen in], [0], [[0
-3
+AT_CHECK_M4([-tlen in], [0], [[3
+0
 ]], [[m4trace: -1- len(`abc') -> `3'
 ]])
 
 dnl Test all flags.
-AT_CHECK_M4([-dV in], [0], [[0
-3
+AT_CHECK_M4([-dV in], [0], [[3
+0
 ]], [[m4debug: module m4: opening file
 m4debug: module m4: init hook called
 m4debug: module m4: opened
@@ -266,11 +271,25 @@
 m4debug: module gnu: macros loaded
 m4debug: path search for `in' found `in'
 m4debug: input read from in
-m4trace:in:1: -1- id 1: divnum ... = <divnum>{m4}
-m4trace:in:1: -1- id 1: divnum -> `0'
-m4trace:in:2: -1- id 2: len ... = <len>{m4}
-m4trace:in:2: -1- id 2: len(`abc') -> `3'
+m4trace:in:1: -1- id 1: include ... = <include>{m4}
+m4debug:in:1: path search for `nested' found `nested'
+m4debug:in:1: input read from nested
+m4trace:in:1: -1- id 1: include(`nested') -> <file: nested>
+m4trace:nested:1: -1- id 2: m4wrap ... = <m4wrap>{m4}
+m4trace:nested:1: -1- id 2: m4wrap(`divnum
+') -> `'
+m4trace:nested:2: -1- id 3: dnl ... = <dnl>{m4}
+m4trace:nested:2: -1- id 3: dnl -> `'
+m4debug:nested:3: input reverted to in, line 1
+m4trace:in:1: -1- id 4: dnl ... = <dnl>{m4}
+m4trace:in:1: -1- id 4: dnl -> `'
+m4trace:in:2: -1- id 5: len ... = <len>{m4}
+m4trace:in:2: -1- id 5: len(`abc') -> `3'
 m4debug:in:3: input exhausted
+m4debug: input from m4wrap recursion level 1
+m4trace:nested:1: -1- id 6: divnum ... = <divnum>{m4}
+m4trace:nested:1: -1- id 6: divnum -> `0'
+m4debug: input from m4wrap exhausted
 m4debug: module gnu: symbols unloaded
 m4debug: module gnu: finish hook called
 m4debug: module gnu: closed
@@ -279,10 +298,15 @@
 ]])
 
 dnl Test addition and subtraction of flags.
-AT_CHECK_M4([--debug=-e --debugmode=+xt in], [0], [[0
-3
-]], [[m4trace: -1- id 1: divnum
-m4trace: -1- id 2: len(`abc')
+AT_CHECK_M4([--debug=-e --debugmode=+xt in], [0], [[3
+0
+]], [[m4trace: -1- id 1: include(`nested')
+m4trace: -1- id 2: m4wrap(`divnum
+')
+m4trace: -1- id 3: dnl
+m4trace: -1- id 4: dnl
+m4trace: -1- id 5: len(`abc')
+m4trace: -1- id 6: divnum
 ]])
 
 dnl Test that shorter prefix is ambiguous.
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.