[SCM] GNU M4 source repository branch, branch-1.6, updated. v1.5.89a-71-g715c421

"Eric Blake" <[email protected]> Thu, 04 Dec 2008 04:31:21 +0000
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=715c42128d8d357e3e751ec605069137d693c757

The branch, branch-1.6 has been updated
       via  715c42128d8d357e3e751ec605069137d693c757 (commit)
      from  9f5e389c810aacbd70b04f2530aa52a897cd0ad9 (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 715c42128d8d357e3e751ec605069137d693c757
Author: Eric Blake <[email protected]>
Date:   Thu Jan 17 14:34:36 2008 -0700

    Stage 27: Allow embedded NUL in text processing macros.
    
    * src/m4.h (evaluate): Add parameter.
    * src/builtin.c (compile_pattern) [DEBUG_REGEX]: Support NUL in
    output messages.
    (set_macro_sequence): Likewise.
    (m4_eval): Normalize messages, and adjust caller.
    (expand_ranges, substitute): Support NUL in macro expansion.
    (m4_translit, m4_regexp, m4_patsubst): Adjust callers, to manage
    NUL bytes.
    * src/format.c (expand_format): Manage NUL bytes.
    * src/eval.c (eval_error): Add EMPTY_ARGUMENT.
    (end_text): New variable.
    (eval_init_lex): Add parameter.
    (eval_lex, evaluate): Detect NUL in macro expansion.
    * doc/m4.texinfo (Format): Update to cover new behavior.
    (Eval): Mention that result is unquoted.
    * examples/null.m4: Enhance test.
    * examples/null.err: Update expected output.
    * examples/null.out: Likewise.
    
    Signed-off-by: Eric Blake <[email protected]>
    (cherry picked from commit 948d1ed0ca4089c2db579fe3d8b3ce172b3e616f)

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

Summary of changes:
 ChangeLog         |   26 ++++++
 doc/m4.texinfo    |   15 +++-
 examples/null.err |  Bin 713 -> 1078 bytes
 examples/null.m4  |  Bin 6499 -> 6667 bytes
 examples/null.out |  Bin 510 -> 553 bytes
 src/builtin.c     |  232 ++++++++++++++++++++++++++++++++++------------------
 src/eval.c        |   33 ++++++--
 src/format.c      |   43 ++++++----
 src/m4.h          |    2 +-
 9 files changed, 241 insertions(+), 110 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2085dea..e991a8c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2008-12-03  Eric Blake  <[email protected]>
+
+	Stage 27: Allow embedded NUL in text processing macros.
+	Pass NUL through regular expressions, format, and translit, and
+	diagnose it in eval.  Improve warning capabilities of format.
+	Memory impact: none.
+	Speed impact: none noticed.
+	* src/m4.h (evaluate): Add parameter.
+	* src/builtin.c (compile_pattern) [DEBUG_REGEX]: Support NUL in
+	output messages.
+	(set_macro_sequence): Likewise.
+	(m4_eval): Normalize messages, and adjust caller.
+	(expand_ranges, substitute): Support NUL in macro expansion.
+	(m4_translit, m4_regexp, m4_patsubst): Adjust callers, to manage
+	NUL bytes.
+	* src/format.c (expand_format): Manage NUL bytes.
+	* src/eval.c (eval_error): Add EMPTY_ARGUMENT.
+	(end_text): New variable.
+	(eval_init_lex): Add parameter.
+	(eval_lex, evaluate): Detect NUL in macro expansion.
+	* doc/m4.texinfo (Format): Update to cover new behavior.
+	(Eval): Mention that result is unquoted.
+	* examples/null.m4: Enhance test.
+	* examples/null.err: Update expected output.
+	* examples/null.out: Likewise.
+
 2008-11-28  Eric Blake  <[email protected]>
 
 	Add extension to divert builtin.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 8301bb7..2fb676d 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -6448,7 +6448,7 @@ Likewise, escape sequences are not yet recognized.
 @example
 format(`%p', `0')
 @error{}m4:stdin:1: Warning: format: unrecognized specifier in `%p'
-@result{}
+@result{}p
 format(`%*d', `')
 @error{}m4:stdin:2: Warning: format: empty string treated as 0
 @error{}m4:stdin:2: Warning: format: too few arguments: 2 < 3
@@ -6734,7 +6734,9 @@ expansion.  The default radix is 10; this is also the case if
 @var{radix} is the empty string.  A warning results if the radix is
 outside the range of 1 through 36, inclusive.  The result of @code{eval}
 is always taken to be signed.  No radix prefix is output, and for
-radices greater than 10, the digits are lower case.  The @var{width}
+radices greater than 10, the digits are lower case (although some
+other implementations use upper case).  The output is unquoted, and
+subject to further macro expansion.  The @var{width}
 argument specifies the minimum output width, excluding any negative
 sign.  The result is zero-padded to extend the expansion to the
 requested width.  A warning results if the width is negative.  If
@@ -6759,14 +6761,19 @@ eval(`10', `', `0')
 eval(`10', `16')
 @result{}a
 eval(`1', `37')
-@error{}m4:stdin:9: Warning: eval: radix 37 out of range
+@error{}m4:stdin:9: Warning: eval: radix out of range: 37
 @result{}
 eval(`1', , `-1')
-@error{}m4:stdin:10: Warning: eval: negative width
+@error{}m4:stdin:10: Warning: eval: negative width: -1
 @result{}
 eval()
 @error{}m4:stdin:11: Warning: eval: empty string treated as 0
 @result{}0
+eval(` ')
+@error{}m4:stdin:12: Warning: eval: empty string treated as 0
+@result{}0
+define(`a', `hi')eval(` 10 ', `16')
+@result{}hi
 @end example
 
 @node Shell commands
diff --git a/examples/null.err b/examples/null.err
index 897ce34..977b3b7 100644
Binary files a/examples/null.err and b/examples/null.err differ
diff --git a/examples/null.m4 b/examples/null.m4
index 1823073..e60aec5 100644
Binary files a/examples/null.m4 and b/examples/null.m4 differ
diff --git a/examples/null.out b/examples/null.out
index dd83416..c2c1cb9 100644
Binary files a/examples/null.out and b/examples/null.out differ
diff --git a/src/builtin.c b/src/builtin.c
index 24f2df6..613e1d2 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -311,7 +311,11 @@ compile_pattern (const char *str, size_t len, struct re_pattern_buffer **buf,
 	regex_cache[i].count++;
 #ifdef DEBUG_REGEX
 	if (trace_file)
-	  xfprintf (trace_file, "cached:{%s}\n", str);
+	  {
+	    fputs ("cached:{", trace_file);
+	    fwrite (str, 1, len, trace_file);
+	    fputs ("}\n", trace_file);
+	  }
 #endif /* DEBUG_REGEX */
 	return NULL;
       }
@@ -321,7 +325,11 @@ compile_pattern (const char *str, size_t len, struct re_pattern_buffer **buf,
   msg = re_compile_pattern (str, len, new_buf);
 #ifdef DEBUG_REGEX
   if (trace_file)
-    xfprintf (trace_file, "compile:{%s}\n", str);
+    {
+      fputs ("compile:{", trace_file);
+      fwrite (str, 1, len, trace_file);
+      fputs ("}\n", trace_file);
+    }
 #endif /* DEBUG_REGEX */
   if (msg)
     {
@@ -356,7 +364,11 @@ compile_pattern (const char *str, size_t len, struct re_pattern_buffer **buf,
     {
 #ifdef DEBUG_REGEX
       if (trace_file)
-	xfprintf (trace_file, "flush:{%s}\n", victim->str);
+	{
+	  fputs ("flush:{", trace_file);
+	  fwrite (victim->str, 1, victim->len, trace_file);
+	  fputs ("}\n", trace_file);
+	}
 #endif /* DEBUG_REGEX */
       free (victim->str);
       regfree (victim->buf);
@@ -404,8 +416,8 @@ set_macro_sequence (const char *regexp)
   msg = re_compile_pattern (regexp, strlen (regexp), &macro_sequence_buf);
   if (msg != NULL)
     m4_error (EXIT_FAILURE, 0, NULL,
-	      _("--warn-macro-sequence: bad regular expression `%s': %s"),
-	      regexp, msg);
+	      _("--warn-macro-sequence: bad regular expression %s: %s"),
+	      quotearg_style (locale_quoting_style, regexp), msg);
   re_set_registers (&macro_sequence_buf, &macro_sequence_regs,
 		    macro_sequence_regs.num_regs,
 		    macro_sequence_regs.start, macro_sequence_regs.end);
@@ -1208,7 +1220,7 @@ m4_eval (struct obstack *obs, int argc, macro_arguments *argv)
 
   if (radix < 1 || radix > 36)
     {
-      m4_warn (0, me, _("radix %d out of range"), radix);
+      m4_warn (0, me, _("radix out of range: %d"), radix);
       return;
     }
 
@@ -1216,13 +1228,11 @@ m4_eval (struct obstack *obs, int argc, macro_arguments *argv)
     return;
   if (min < 0)
     {
-      m4_warn (0, me, _("negative width"));
+      m4_warn (0, me, _("negative width: %d"), min);
       return;
     }
 
-  if (arg_empty (argv, 1))
-    m4_warn (0, me, _("empty string treated as 0"));
-  else if (evaluate (me, ARG (1), &value))
+  if (evaluate (me, ARG (1), ARG_LEN (1), &value))
     return;
 
   if (radix == 1)
@@ -1887,34 +1897,42 @@ m4_substr (struct obstack *obs, int argc, macro_arguments *argv)
   obstack_grow (obs, ARG (1) + start, length);
 }
 
-/*------------------------------------------------------------------------.
-| For "translit", ranges are allowed in the second and third argument.	  |
-| They are expanded in the following function, and the expanded strings,  |
-| without any ranges left, are used to translate the characters of the	  |
-| first argument.  A single - (dash) can be included in the strings by	  |
-| being the first or the last character in the string.  If the first	  |
-| character in a range is after the first in the character set, the range |
-| is made backwards, thus 9-0 is the string 9876543210.			  |
-`------------------------------------------------------------------------*/
+/*------------------------------------------------------------------.
+| For "translit", ranges are allowed in the second and third        |
+| argument.  They are expanded in the following function, and the   |
+| expanded strings, without any ranges left, are used to translate  |
+| the characters of the first argument.  A single - (dash) can be   |
+| included in the strings by being the first or the last character  |
+| in the string.  If the first character in a range is after the    |
+| first in the character set, the range is made backwards, thus 9-0 |
+| is the string 9876543210.  This function expands S of length *LEN |
+| using OBS for the expansion, sets *LEN to the new length, and     |
+| returns the expansion.                                            |
+`------------------------------------------------------------------*/
 
 static const char *
-expand_ranges (const char *s, struct obstack *obs)
+expand_ranges (const char *s, size_t *len, struct obstack *obs)
 {
   unsigned char from;
   unsigned char to;
+  const char *end = s + *len;
+
+  assert (s != end);
+  from = *s++;
+  obstack_1grow (obs, from);
 
-  for (from = '\0'; *s != '\0'; from = to_uchar (*s++))
+  for ( ; s != end; from = *s++)
     {
-      if (*s == '-' && from != '\0')
+      if (*s == '-')
 	{
-	  to = to_uchar (*++s);
-	  if (to == '\0')
+	  if (++s == end)
 	    {
 	      /* trailing dash */
 	      obstack_1grow (obs, '-');
 	      break;
 	    }
-	  else if (from <= to)
+	  to = *s;
+	  if (from <= to)
 	    {
 	      while (from++ < to)
 		obstack_1grow (obs, from);
@@ -1928,7 +1946,7 @@ expand_ranges (const char *s, struct obstack *obs)
       else
 	obstack_1grow (obs, *s);
     }
-  obstack_1grow (obs, '\0');
+  *len = obstack_object_size (obs);
   return (char *) obstack_finish (obs);
 }
 
@@ -1946,25 +1964,32 @@ m4_translit (struct obstack *obs, int argc, macro_arguments *argv)
   const char *data;
   const char *from;
   const char *to;
+  size_t from_len;
+  size_t to_len;
   char map[UCHAR_MAX + 1] = {0};
   char found[UCHAR_MAX + 1] = {0};
   unsigned char ch;
 
-  if (bad_argc (arg_info (argv), argc, 2, 3))
+  enum { ASIS, REPLACE, DELETE };
+
+  if (bad_argc (arg_info (argv), argc, 2, 3) || arg_empty (argv, 1)
+      || arg_empty (argv, 2))
     {
       /* builtin(`translit') is blank, but translit(`abc') is abc.  */
-      if (argc == 2)
+      if (argc >= 2)
 	push_arg (obs, argv, 1);
       return;
     }
 
   from = ARG (2);
-  if (strchr (from, '-') != NULL)
-    from = expand_ranges (from, arg_scratch ());
+  from_len = ARG_LEN (2);
+  if (memchr (from, '-', from_len) != NULL)
+    from = expand_ranges (from, &from_len, arg_scratch ());
 
   to = ARG (3);
-  if (strchr (to, '-') != NULL)
-    to = expand_ranges (to, arg_scratch ());
+  to_len = ARG_LEN (3);
+  if (memchr (to, '-', to_len) != NULL)
+    to = expand_ranges (to, &to_len, arg_scratch ());
 
   assert (from && to);
 
@@ -1974,23 +1999,45 @@ m4_translit (struct obstack *obs, int argc, macro_arguments *argv)
      pass of data, for linear behavior.  Traditional behavior is that
      only the first instance of a character in from is consulted,
      hence the found map.  */
-  for ( ; (ch = *from) != '\0'; from++)
+  while (from_len--)
     {
-      if (!found[ch])
+      ch = *from++;
+      if (found[ch] == ASIS)
+	{
+	  if (to_len)
+	    {
+	      found[ch] = REPLACE;
+	      map[ch] = *to;
+	    }
+	  else
+	    found[ch] = DELETE;
+	}
+      if (to_len)
 	{
-	  found[ch] = 1;
-	  map[ch] = *to;
+	  to++;
+	  to_len--;
 	}
-      if (*to != '\0')
-	to++;
     }
 
-  for (data = ARG (1); (ch = *data) != '\0'; data++)
+  data = ARG (1);
+  from_len = ARG_LEN (1);
+  while (from_len--)
     {
-      if (!found[ch])
-	obstack_1grow (obs, ch);
-      else if (map[ch])
-	obstack_1grow (obs, map[ch]);
+      ch = *data++;
+      switch (found[ch])
+	{
+	case ASIS:
+	  obstack_1grow (obs, ch);
+	  break;
+	case REPLACE:
+	  obstack_1grow (obs, map[ch]);
+	  break;
+	case DELETE:
+	  break;
+	default:
+	  assert (!"m4_translit");
+	  abort ();
+	}
     }
 }
 
@@ -2020,20 +2067,27 @@ static int substitute_warned = 0;
 
 static void
 substitute (struct obstack *obs, const call_info *me, const char *victim,
-	    const char *repl, struct re_registers *regs)
+	    const char *repl, size_t repl_len, struct re_registers *regs)
 {
   int ch;
 
-  for (;;)
+  while (repl_len--)
     {
-      while ((ch = *repl++) != '\\')
+      ch = *repl++;
+      if (ch != '\\')
 	{
-	  if (ch == '\0')
-	    return;
 	  obstack_1grow (obs, ch);
+	  continue;
+	}
+      if (!repl_len)
+	{
+	  m4_warn (0, me, _("trailing \\ ignored in replacement"));
+	  return;
 	}
 
-      switch ((ch = *repl++))
+      ch = *repl++;
+      repl_len--;
+      switch (ch)
 	{
 	case '0':
 	  if (!substitute_warned)
@@ -2060,10 +2114,6 @@ substitute (struct obstack *obs, const call_info *me, const char *victim,
 			  regs->end[ch] - regs->start[ch]);
 	  break;
 
-	case '\0':
-	  m4_warn (0, me, _("trailing \\ ignored in replacement"));
-	  return;
-
 	default:
 	  obstack_1grow (obs, ch);
 	  break;
@@ -2122,26 +2172,36 @@ m4_regexp (struct obstack *obs, int argc, macro_arguments *argv)
   regexp = ARG (2);
   repl = ARG (3);
 
-  if (!*regexp)
+  if (arg_empty (argv, 2))
     {
       /* The empty regex matches everything!  */
       if (argc == 3)
 	shipout_int (obs, 0);
       else
-	substitute (obs, me, victim, repl, NULL);
+	substitute (obs, me, victim, repl, ARG_LEN (3), NULL);
       return;
     }
 
 #ifdef DEBUG_REGEX
   if (trace_file)
-    xfprintf (trace_file, "r:{%s}:%s%s%s\n", regexp,
-	      argc == 3 ? "" : "{", repl, argc == 3 ? "" : "}");
+    {
+      fputs ("r:{", trace_file);
+      fwrite (regexp, 1, ARG_LEN (2), trace_file);
+      if (argc > 3)
+	{
+	  fputs ("}:{", trace_file);
+	  fwrite (repl, 1, ARG_LEN (3), trace_file);
+	}
+      fputs ("}\n", trace_file);
+    }
 #endif /* DEBUG_REGEX */
 
   msg = compile_pattern (regexp, ARG_LEN (2), &buf, &regs);
   if (msg != NULL)
     {
-      m4_warn (0, me, _("bad regular expression: `%s': %s"), regexp, msg);
+      m4_warn (0, me, _("bad regular expression %s: %s"),
+	       quotearg_style_mem (locale_quoting_style, regexp, ARG_LEN (2)),
+	       msg);
       return;
     }
 
@@ -2151,11 +2211,12 @@ m4_regexp (struct obstack *obs, int argc, macro_arguments *argv)
 			argc == 3 ? NULL : regs);
 
   if (startpos == -2)
-    m4_warn (0, me, _("problem matching regular expression `%s'"), regexp);
+    m4_warn (0, me, _("problem matching regular expression %s"),
+	     quotearg_style_mem (locale_quoting_style, regexp, ARG_LEN (2)));
   else if (argc == 3)
     shipout_int (obs, startpos);
   else if (startpos >= 0)
-    substitute (obs, me, victim, repl, regs);
+    substitute (obs, me, victim, repl, ARG_LEN (3), regs);
 }
 
 /*------------------------------------------------------------------.
@@ -2170,16 +2231,17 @@ static void
 m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const call_info *me = arg_info (argv);
-  const char *victim;		/* first argument */
-  const char *regexp;		/* regular expression */
-  const char *repl;
-
-  struct re_pattern_buffer *buf;/* compiled regular expression */
-  struct re_registers *regs;	/* for subexpression matches */
-  const char *msg;		/* error message from re_compile_pattern */
-  int matchpos;			/* start position of match */
-  int offset;			/* current match offset */
-  int length;			/* length of first argument */
+  const char *victim;		/* First argument.  */
+  const char *regexp;		/* Regular expression.  */
+  const char *repl;		/* Replacement text.  */
+
+  struct re_pattern_buffer *buf;/* Compiled regular expression.  */
+  struct re_registers *regs;	/* For subexpression matches.  */
+  const char *msg;		/* Error message from re_compile_pattern.  */
+  int matchpos;			/* Start position of match.  */
+  int offset;			/* Current match offset.  */
+  int length;			/* Length of first argument.  */
+  size_t repl_len;		/* Length of replacement.  */
 
   if (bad_argc (me, argc, 2, 3))
     {
@@ -2189,27 +2251,36 @@ m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv)
       return;
     }
 
-  victim = ARG (1);
-  regexp = ARG (2);
-  repl = ARG (3);
-
   /* The empty regex matches everywhere, but if there is no
      replacement, we need not waste time with it.  */
-  if (!*regexp && !*repl)
+  if (arg_empty (argv, 2) && arg_empty (argv, 3))
     {
       push_arg (obs, argv, 1);
       return;
     }
 
+  victim = ARG (1);
+  regexp = ARG (2);
+  repl = ARG (3);
+  repl_len = ARG_LEN (3);
+
 #ifdef DEBUG_REGEX
   if (trace_file)
-    xfprintf (trace_file, "p:{%s}:{%s}\n", regexp, repl);
+    {
+      fputs ("p:{", trace_file);
+      fwrite (regexp, 1, ARG_LEN (2), trace_file);
+      fputs ("}:{", trace_file);
+      fwrite (repl, 1, repl_len, trace_file);
+      fputs ("}\n", trace_file);
+    }
 #endif /* DEBUG_REGEX */
 
   msg = compile_pattern (regexp, ARG_LEN (2), &buf, &regs);
   if (msg != NULL)
     {
-      m4_warn (0, me, _("bad regular expression `%s': %s"), regexp, msg);
+      m4_warn (0, me, _("bad regular expression %s: %s"),
+	       quotearg_style_mem (locale_quoting_style, regexp, ARG_LEN (2)),
+	       msg);
       return;
     }
 
@@ -2229,8 +2300,9 @@ m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv)
 	     copied verbatim.  */
 
 	  if (matchpos == -2)
-	    m4_warn (0, me, _("problem matching regular expression `%s'"),
-		     regexp);
+	    m4_warn (0, me, _("problem matching regular expression %s"),
+		     quotearg_style_mem (locale_quoting_style, regexp,
+					 ARG_LEN (2)));
 	  else if (offset < length)
 	    obstack_grow (obs, victim + offset, length - offset);
 	  break;
@@ -2243,7 +2315,7 @@ m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv)
 
       /* Handle the part of the string that was covered by the match.  */
 
-      substitute (obs, me, victim, repl, regs);
+      substitute (obs, me, victim, repl, repl_len, regs);
 
       /* Update the offset to the end of the match.  If the regexp
 	 matched a null string, advance offset one more, to avoid
diff --git a/src/eval.c b/src/eval.c
index e2e600b..1b617ed 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -58,7 +58,8 @@ typedef enum eval_error
     MISSING_RIGHT,
     UNKNOWN_INPUT,
     EXCESS_INPUT,
-    INVALID_OPERATOR
+    INVALID_OPERATOR,
+    EMPTY_ARGUMENT
   }
 eval_error;
 
@@ -87,10 +88,15 @@ static const char *eval_text;
    can back up, if we have read too much.  */
 static const char *last_text;
 
+/* Detect when to end parsing.  */
+static const char *end_text;
+
+/* Prime the lexer at the start of TEXT, with length LEN.  */
 static void
-eval_init_lex (const char *text)
+eval_init_lex (const char *text, size_t len)
 {
   eval_text = text;
+  end_text = text + len;
   last_text = NULL;
 }
 
@@ -105,12 +111,12 @@ eval_undo (void)
 static eval_token
 eval_lex (int32_t *val)
 {
-  while (isspace (to_uchar (*eval_text)))
+  while (eval_text != end_text && isspace (to_uchar (*eval_text)))
     eval_text++;
 
   last_text = eval_text;
 
-  if (*eval_text == '\0')
+  if (eval_text == end_text)
     return EOTEXT;
 
   if (isdigit (to_uchar (*eval_text)))
@@ -287,14 +293,17 @@ eval_lex (int32_t *val)
 `---------------------------------------*/
 
 bool
-evaluate (const call_info *me, const char *expr, int32_t *val)
+evaluate (const call_info *me, const char *expr, size_t len, int32_t *val)
 {
   eval_token et;
   eval_error err;
 
-  eval_init_lex (expr);
+  eval_init_lex (expr, len);
   et = eval_lex (val);
-  err = logical_or_term (me, et, val);
+  if (et == EOTEXT)
+    err = EMPTY_ARGUMENT;
+  else
+    err = logical_or_term (me, et, val);
 
   if (err == NO_ERROR && *eval_text != '\0')
     {
@@ -306,9 +315,15 @@ evaluate (const call_info *me, const char *expr, int32_t *val)
 
   switch (err)
     {
+      /* Cases where result is printed.  */
     case NO_ERROR:
-      break;
+      return false;
+
+    case EMPTY_ARGUMENT:
+      m4_warn (0, me, _("empty string treated as 0"));
+      return false;
 
+      /* Cases where error makes result meaningless.  */
     case MISSING_RIGHT:
       m4_warn (0, me, _("bad expression (missing right parenthesis): %s"),
 	       expr);
@@ -347,7 +362,7 @@ evaluate (const call_info *me, const char *expr, int32_t *val)
       abort ();
     }
 
-  return err != NO_ERROR;
+  return true;
 }
 
 /*---------------------------.
diff --git a/src/format.c b/src/format.c
index 3325853..8b2b11a 100644
--- a/src/format.c
+++ b/src/format.c
@@ -126,11 +126,12 @@ expand_format (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const call_info *me = arg_info (argv);/* Macro name.  */
   const char *f;			/* Format control string.  */
+  size_t f_len;				/* Length of f.  */
   const char *fmt;			/* Position within f.  */
   char fstart[] = "%'+- 0#*.*hhd";	/* Current format spec.  */
   char *p;				/* Position within fstart.  */
   unsigned char c;			/* A simple character.  */
-  int i = 0;				/* Index within argc used so far.  */
+  int i = 1;				/* Index within argc used so far.  */
   bool valid_format = true;		/* True if entire format string ok.  */
 
   /* Flags.  */
@@ -159,25 +160,24 @@ expand_format (struct obstack *obs, int argc, macro_arguments *argv)
   int result = 0;
   enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
 
-  f = fmt = ARG_STR (i, argc, argv);
+  f = fmt = ARG (1);
+  f_len = ARG_LEN (1);
+  assert (!f[f_len]); /* Requiring a terminating NUL makes parsing simpler.  */
   memset (ok, 0, sizeof ok);
-  while (true)
+  while (f_len--)
     {
-      while ((c = *fmt++) != '%')
+      c = *fmt++;
+      if (c != '%')
 	{
-	  if (c == '\0')
-	    {
-	      if (valid_format)
-		bad_argc (me, argc, i, i);
-	      return;
-	    }
 	  obstack_1grow (obs, c);
+	  continue;
 	}
 
       if (*fmt == '%')
 	{
 	  obstack_1grow (obs, '%');
 	  fmt++;
+	  f_len--;
 	  continue;
 	}
 
@@ -228,7 +228,7 @@ expand_format (struct obstack *obs, int argc, macro_arguments *argv)
 	      break;
 	    }
 	}
-      while (!(flags & DONE) && fmt++);
+      while (!(flags & DONE) && (f_len--, fmt++));
       if (flags & THOUSANDS)
 	*p++ = '\'';
       if (flags & PLUS)
@@ -250,12 +250,14 @@ expand_format (struct obstack *obs, int argc, macro_arguments *argv)
 	{
 	  width = ARG_INT (i, argc, argv);
 	  fmt++;
+	  f_len--;
 	}
       else
 	while (isdigit (to_uchar (*fmt)))
 	  {
 	    width = 10 * width + *fmt - '0';
 	    fmt++;
+	    f_len--;
 	  }
 
       /* Maximum precision; an explicit negative precision is the same
@@ -266,10 +268,12 @@ expand_format (struct obstack *obs, int argc, macro_arguments *argv)
       if (*fmt == '.')
 	{
 	  ok['c'] = 0;
+	  f_len--;
 	  if (*(++fmt) == '*')
 	    {
 	      prec = ARG_INT (i, argc, argv);
 	      ++fmt;
+	      f_len--;
 	    }
 	  else
 	    {
@@ -278,6 +282,7 @@ expand_format (struct obstack *obs, int argc, macro_arguments *argv)
 		{
 		  prec = 10 * prec + *fmt - '0';
 		  fmt++;
+		  f_len--;
 		}
 	    }
 	}
@@ -288,30 +293,34 @@ expand_format (struct obstack *obs, int argc, macro_arguments *argv)
 	  *p++ = 'l';
 	  lflag = 1;
 	  fmt++;
+	  f_len--;
 	  ok['c'] = ok['s'] = 0;
 	}
       else if (*fmt == 'h')
 	{
 	  *p++ = 'h';
 	  fmt++;
+	  f_len--;
 	  if (*fmt == 'h')
 	    {
 	      *p++ = 'h';
 	      fmt++;
+	      f_len--;
 	    }
 	  ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = ok['f'] = ok['F']
 	    = ok['g'] = ok['G'] = ok['s'] = 0;
 	}
 
-      c = *fmt++;
-      if (c > sizeof ok || !ok[c])
+      c = *fmt;
+      if (c > sizeof ok || !ok[c] || !f_len)
 	{
-	  m4_warn (0, me, _("unrecognized specifier in `%s'"), f);
+	  m4_warn (0, me, _("unrecognized specifier in %s"),
+		   quotearg_style_mem (locale_quoting_style, f, ARG_LEN (1)));
 	  valid_format = false;
-	  if (c == '\0')
-	    fmt--;
 	  continue;
 	}
+      fmt++;
+      f_len--;
 
       /* Specifiers.  We don't yet recognize C, S, n, or p.  */
       switch (c)
@@ -385,4 +394,6 @@ expand_format (struct obstack *obs, int argc, macro_arguments *argv)
 	 we constructed fstart, the result should not be negative.  */
       assert (0 <= result);
     }
+  if (valid_format)
+    bad_argc (me, argc, i, i);
 }
diff --git a/src/m4.h b/src/m4.h
index f643e49..76c697b 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -549,7 +549,7 @@ FILE *m4_path_search (const char *, char **);
 
 /* File: eval.c  --- expression evaluation.  */
 
-bool evaluate (const call_info *, const char *, int32_t *);
+bool evaluate (const call_info *, const char *, size_t, int32_t *);
 
 /* File: format.c  --- printf like formatting.  */
 


hooks/post-receive
--
GNU M4 source repository