m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

argv_ref patch 28: handle NUL in warning messages


From: Eric Blake
Subject: argv_ref patch 28: handle NUL in warning messages
Date: Wed, 11 Feb 2009 20:27:40 -0700
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.19) Gecko/20081209 Thunderbird/2.0.0.19 Mnenhy/0.7.6.666

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I'm still going rather slowly at porting the argv_ref branch into master.
 This patch rounds out the handling of embedded NUL in all of the builtins
(in most of these cases, the presence of an embedded NUL is a user bug,
but the warning message should not be truncated at the NUL).  I still need
to beef up the NEWS and manual about the fact that embedded NUL support is
now present in m4.  On the master branch, I subdivided the patch into
three roughly equal parts.  Also, working on this patch uncovered a hole
in branch-1.6 handling of warning on popdef(undef), which I patched
earlier today.  No real change in memory usage or speed.

        Stage 28: Allow embedded NUL in warning messages.
        Issue graceful error messages for a variety of places where
        embedded NUL is otherwise unhandled.  For example, numeric parsing
        rejects embedded NUL, while file-related commands warn about
        truncation at NUL, and frozen files detect unexpected NUL.
        Memory impact: none.
        Speed impact: none noticed.
        * src/m4.h (debug_decode): Add argument.
        * src/m4.c (main): Adjust caller.
        * src/format.c (arg_int, arg_long, arg_double): Likewise.
        (arg_string): New function.
        (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE): Support embedded NUL.
        * src/freeze.c (reload_frozen_state): Likewise.
        * src/debug.c (debug_decode): Likewise.
        * src/builtin.c (numeric_arg): Add argument.
        (m4_syscmd, m4_esyscmd, m4_eval, m4_incr, m4_decr, m4_divert)
        (m4_undivert, include, mkstemp_helper, m4_m4exit, m4_debugmode)
        (m4_debugfile, m4_index, m4_substr): Detect embedded NUL.
        (m4_defn, m4_maketemp, m4_placeholder): Improve warning message.
        * src/path.c (m4_path_search): Likewise.
        * doc/m4.texinfo (Syntax, Mkstemp, Using frozen files): Adjust
        tests.
        * examples/null.m4: Likewise.
        * examples/null.out: Likewise.
        * examples/null.err: Likewise.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkmTlywACgkQ84KuGfSFAYBadwCgnNHWJdUxeGRE+uGPZxa9VGTk
GngAoK0Zk3cA5jlccjK3ixr4HPpuX9/i
=J55f
-----END PGP SIGNATURE-----
From 185b10ba33786707f6478b8315e9527151f60902 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 5 Feb 2009 07:47:32 -0700
Subject: [PATCH 1/3] Stage 28a: Warn on embedded NUL in numeric arguments.

* m4/m4module.h (m4_numeric_arg): Adjust prototype.
* m4/utility.c (m4_numeric_arg): Add parameter.
* modules/gnu.c (debuglen): Adjust callers.
* modules/m4.c (incr, decr, divert, undivert, m4exit, substr)
(index): Likewise.
* modules/evalparse.c (m4_evaluate): Likewise.
* modules/stdlib.c (setenv, getpwuid, srand): Likewise.
* modules/time.c (ctime, gmtime, localtime, mktime, strftime):
Likewise.
* doc/m4.texinfo (Changesyntax): Fix typo.
* tests/others.at (nul character): Adjust test.
* tests/null.m4: Likewise.
* tests/null.out: Likewise.
* tests/null.err: Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog           |   22 ++++++++++++++++++++++
 doc/m4.texinfo      |    2 +-
 m4/m4module.h       |    4 ++--
 m4/utility.c        |   24 +++++++++++++++---------
 modules/evalparse.c |    7 ++++---
 modules/gnu.c       |    7 ++++---
 modules/m4.c        |   29 +++++++++++++++++++----------
 modules/stdlib.c    |   13 ++++++++-----
 modules/time.c      |   31 ++++++++++++++++++-------------
 tests/null.err      |   43 +++++++++++++++++++++++++++++++------------
 tests/null.m4       |   43 ++++++++++++++++++++++++++++++++++---------
 tests/null.out      |    2 +-
 tests/others.at     |    8 ++++++--
 13 files changed, 165 insertions(+), 70 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index acbdeec..def85f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2009-02-11  Eric Blake  <address@hidden>

+       Stage 28a: Warn on embedded NUL in numeric arguments.
+       Quote warning messages related to numeric parsing in order to
+       handle embedded NUL.
+       Memory impact: none.
+       Speed impact: none noticed.
+       * m4/m4module.h (m4_numeric_arg): Adjust prototype.
+       * m4/utility.c (m4_numeric_arg): Add parameter.
+       * modules/gnu.c (debuglen): Adjust callers.
+       * modules/m4.c (incr, decr, divert, undivert, m4exit, substr)
+       (index): Likewise.
+       * modules/evalparse.c (m4_evaluate): Likewise.
+       * modules/stdlib.c (setenv, getpwuid, srand): Likewise.
+       * modules/time.c (ctime, gmtime, localtime, mktime, strftime):
+       Likewise.
+       * doc/m4.texinfo (Changesyntax): Fix typo.
+       * tests/others.at (nul character): Adjust test.
+       * tests/null.m4: Likewise.
+       * tests/null.out: Likewise.
+       * tests/null.err: Likewise.
+
+2009-02-11  Eric Blake  <address@hidden>
+
        Avoid regression in popdef(undef).
        * doc/m4.texinfo (Trace): Enhance test, to cover regression
        recently fixed on the branch.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index f972b48..62fed45 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -5751,7 +5751,7 @@ Changesyntax
 @example
 changesyntax(`(@{<', `)@}>', `,;:', `O(,)')
 @result{}
address@hidden; 2 : 8>
address@hidden; 2: 8>
 @result{}00001111
 @end example

diff --git a/m4/m4module.h b/m4/m4module.h
index d5ad513..84c203e 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1999, 2000, 2003,
-   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -170,7 +170,7 @@ struct m4_string_pair
 extern bool    m4_bad_argc        (m4 *, size_t, const m4_call_info *, size_t,
                                    size_t, bool);
 extern bool    m4_numeric_arg     (m4 *, const m4_call_info *, const char *,
-                                   int *);
+                                   size_t, int *);
 extern bool    m4_parse_truth_arg (m4 *, const m4_call_info *, const char *,
                                    bool);
 extern m4_symbol *m4_symbol_value_lookup (m4 *, m4_macro_args *, size_t, bool);
diff --git a/m4/utility.c b/m4/utility.c
index 31c1705..6d19526 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1998, 1999, 2003,
-   2006, 2007, 2008 Free Software Foundation, Inc.
+   2006, 2007, 2008, 2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -68,30 +68,36 @@ skip_space (m4 *context, const char *arg)
   return arg;
 }

-/* The function m4_numeric_arg () converts ARG to an int pointed to by
-   VALUEP. If the conversion fails, print error message for CALLER.
-   Return true iff conversion succeeds.  */
+/* The function m4_numeric_arg () converts ARG of length LEN to an int
+   pointed to by VALUEP. If the conversion fails, print error message
+   for CALLER.  Return true iff conversion succeeds.  */
 /* FIXME: Convert this to use gnulib's xstrtoimax, xstrtoumax.
    Otherwise, we are arbitrarily limiting integer values.  */
 bool
 m4_numeric_arg (m4 *context, const m4_call_info *caller, const char *arg,
-               int *valuep)
+               size_t len, int *valuep)
 {
   char *endp;

-  if (*arg == '\0')
+  if (!len)
     {
       *valuep = 0;
       m4_warn (context, 0, caller, _("empty string treated as 0"));
     }
   else
     {
-      *valuep = strtol (skip_space (context, arg), &endp, 10);
-      if (*skip_space (context, endp) != 0)
+      const char *str = skip_space (context, arg);
+      *valuep = strtol (str, &endp, 10);
+      if (endp - arg != len)
        {
-         m4_warn (context, 0, caller, _("non-numeric argument `%s'"), arg);
+         m4_warn (context, 0, caller, _("non-numeric argument %s"),
+                  quotearg_style_mem (locale_quoting_style, arg, len));
          return false;
        }
+      if (str != arg)
+       m4_warn (context, 0, caller, _("leading whitespace ignored"));
+      else if (errno == ERANGE)
+       m4_warn (context, 0, caller, _("numeric overflow detected"));
     }
   return true;
 }
diff --git a/modules/evalparse.c b/modules/evalparse.c
index ac30cfe..87a318e 100644
--- a/modules/evalparse.c
+++ b/modules/evalparse.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007,
-   2008 Free Software Foundation, Inc.
+   2008, 2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -901,7 +901,7 @@ m4_evaluate (m4 *context, m4_obstack *obs, size_t argc, 
m4_macro_args *argv)
   eval_error   err     = NO_ERROR;

   if (!m4_arg_empty (argv, 2)
-      && !m4_numeric_arg (context, me, M4ARG (2), &radix))
+      && !m4_numeric_arg (context, me, M4ARG (2), M4ARGLEN (2), &radix))
     return;

   if (radix < 1 || radix > 36)
@@ -910,7 +910,8 @@ m4_evaluate (m4 *context, m4_obstack *obs, size_t argc, 
m4_macro_args *argv)
       return;
     }

-  if (argc >= 4 && !m4_numeric_arg (context, me, M4ARG (3), &min))
+  if (argc >= 4 && !m4_numeric_arg (context, me, M4ARG (3), M4ARGLEN (3),
+                                   &min))
     return;

   if (min < 0)
diff --git a/modules/gnu.c b/modules/gnu.c
index 8ad1722..ce14532 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 2000, 2004, 2005, 2006, 2007, 2008 Free Software
-   Foundation, Inc.
+   Copyright (C) 2000, 2004, 2005, 2006, 2007, 2008, 2009 Free
+   Software Foundation, Inc.

    This file is part of GNU M4.

@@ -599,7 +599,8 @@ M4BUILTIN_HANDLER (debuglen)
 {
   int i;
   size_t s;
-  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &i))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+                      &i))
     return;
   /* FIXME - make m4_numeric_arg more powerful - we want to accept
      suffixes, and limit the result to size_t.  */
diff --git a/modules/m4.c b/modules/m4.c
index 177590d..44e8a0a 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -536,7 +536,8 @@ M4BUILTIN_HANDLER (incr)
 {
   int value;

-  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &value))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+                      &value))
     return;

   m4_shipout_int (obs, value + 1);
@@ -546,7 +547,8 @@ M4BUILTIN_HANDLER (decr)
 {
   int value;

-  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &value))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+                      &value))
     return;

   m4_shipout_int (obs, value - 1);
@@ -563,7 +565,7 @@ M4BUILTIN_HANDLER (divert)
   int i = 0;

   if (argc >= 2 && !m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1),
-                                   &i))
+                                   M4ARGLEN (1), &i))
     return;
   m4_make_diversion (context, i);
   m4_divert_text (context, NULL, M4ARG (2), M4ARGLEN (2),
@@ -592,12 +594,17 @@ M4BUILTIN_HANDLER (undivert)
     for (i = 1; i < argc; i++)
       {
        const char *str = M4ARG (i);
+       size_t len = M4ARGLEN (i);
        char *endp;
        int diversion = strtol (str, &endp, 10);
-       if (*endp == '\0' && !isspace ((unsigned char) *str))
+       if (endp - str == len && !isspace ((unsigned char) *str))
          m4_insert_diversion (context, diversion);
        else if (m4_get_posixly_correct_opt (context))
-         m4_numeric_arg (context, me, str, &diversion);
+         m4_warn (context, 0, me, _("non-numeric argument %s"),
+                  quotearg_style_mem (locale_quoting_style, str, len));
+       else if (strlen (str) != len)
+         m4_warn (context, 0, me, _("invalid file name %s"),
+                  quotearg_style_mem (locale_quoting_style, str, len));
        else
          {
            FILE *fp = m4_path_search (context, str, NULL);
@@ -609,7 +616,7 @@ M4BUILTIN_HANDLER (undivert)
                            quotearg_style (locale_quoting_style, str));
              }
            else
-             m4_error (context, 0, errno, me, _("cannot undivert `%s'"),
+             m4_error (context, 0, errno, me, _("cannot undivert %s"),
                        quotearg_style (locale_quoting_style, str));
          }
       }
@@ -824,7 +831,8 @@ M4BUILTIN_HANDLER (m4exit)
   int exit_code = EXIT_SUCCESS;

   /* Warn on bad arguments, but still exit.  */
-  if (argc >= 2 && !m4_numeric_arg (context, me, M4ARG (1), &exit_code))
+  if (argc >= 2 && !m4_numeric_arg (context, me, M4ARG (1), M4ARGLEN (1),
+                                   &exit_code))
     exit_code = EXIT_FAILURE;
   if (exit_code < 0 || exit_code > 255)
     {
@@ -919,7 +927,8 @@ M4BUILTIN_HANDLER (index)
   int retval = -1;

   if (!m4_arg_empty (argv, 3) && !m4_numeric_arg (context, m4_arg_info (argv),
-                                                 M4ARG (3), &offset))
+                                                 M4ARG (3), M4ARGLEN (3),
+                                                  &offset))
     return;
   if (offset < 0)
     {
@@ -967,7 +976,7 @@ M4BUILTIN_HANDLER (substr)

   length = M4ARGLEN (1);
   if (!m4_arg_empty (argv, 2)
-      && !m4_numeric_arg (context, me, M4ARG (2), &start))
+      && !m4_numeric_arg (context, me, M4ARG (2), M4ARGLEN (2), &start))
     return;
   if (start < 0)
     start += length;
@@ -976,7 +985,7 @@ M4BUILTIN_HANDLER (substr)
     end = length;
   else
     {
-      if (!m4_numeric_arg (context, me, M4ARG (3), &end))
+      if (!m4_numeric_arg (context, me, M4ARG (3), M4ARGLEN (3), &end))
        return;
       if (end < 0)
        end += length;
diff --git a/modules/stdlib.c b/modules/stdlib.c
index b60e977..3fce71f 100644
--- a/modules/stdlib.c
+++ b/modules/stdlib.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1999, 2000, 2001, 2006, 2007, 2008 Free Software
-   Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2006, 2007, 2008, 2009 Free
+   Software Foundation, Inc.

    This file is part of GNU M4.

@@ -108,7 +108,8 @@ M4BUILTIN_HANDLER (setenv)
   int overwrite = 1;

   if (argc >= 4)
-    if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (3), &overwrite))
+    if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (3), M4ARGLEN (3),
+                        &overwrite))
       return;

 #if HAVE_SETENV
@@ -206,7 +207,8 @@ M4BUILTIN_HANDLER (getpwuid)
   struct passwd *pw;
   int uid;

-  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &uid))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+                      &uid))
     return;

   pw = getpwuid (uid);
@@ -261,7 +263,8 @@ M4BUILTIN_HANDLER (srand)
     seed = time (0L) * getpid ();
   else
     {
-      if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &seed))
+      if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1),
+                          M4ARGLEN (1), &seed))
        return;
     }

diff --git a/modules/time.c b/modules/time.c
index a922a67..ee193a7 100644
--- a/modules/time.c
+++ b/modules/time.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1999, 2000, 2001, 2006, 2007, 2008 Free Software
-   Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2006, 2007, 2008, 2009 Free
+   Software Foundation, Inc.

    This file is part of GNU M4.

@@ -104,7 +104,8 @@ M4BUILTIN_HANDLER (ctime)

   if (argc == 2)
     {
-      m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &i);
+      m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+                     &i);
       t = i;
     }
   else
@@ -152,7 +153,8 @@ M4BUILTIN_HANDLER (gmtime)
   time_t t;
   int i;

-  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &i))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+                      &i))
     return;

   t = i;
@@ -167,7 +169,8 @@ M4BUILTIN_HANDLER (localtime)
   time_t t;
   int i;

-  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), &i))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (1), M4ARGLEN (1),
+                      &i))
     return;

   t = i;
@@ -184,19 +187,20 @@ M4BUILTIN_HANDLER (mktime)
   struct tm tm;
   time_t t;

-  if (!m4_numeric_arg (context, me, M4ARG (1), &tm.tm_sec))
+  if (!m4_numeric_arg (context, me, M4ARG (1), M4ARGLEN (1), &tm.tm_sec))
     return;
-  if (!m4_numeric_arg (context, me, M4ARG (2), &tm.tm_min))
+  if (!m4_numeric_arg (context, me, M4ARG (2), M4ARGLEN (2), &tm.tm_min))
     return;
-  if (!m4_numeric_arg (context, me, M4ARG (3), &tm.tm_hour))
+  if (!m4_numeric_arg (context, me, M4ARG (3), M4ARGLEN (3), &tm.tm_hour))
     return;
-  if (!m4_numeric_arg (context, me, M4ARG (4), &tm.tm_mday))
+  if (!m4_numeric_arg (context, me, M4ARG (4), M4ARGLEN (4), &tm.tm_mday))
     return;
-  if (!m4_numeric_arg (context, me, M4ARG (5), &tm.tm_mon))
+  if (!m4_numeric_arg (context, me, M4ARG (5), M4ARGLEN (5), &tm.tm_mon))
     return;
-  if (!m4_numeric_arg (context, me, M4ARG (6), &tm.tm_year))
+  if (!m4_numeric_arg (context, me, M4ARG (6), M4ARGLEN (6), &tm.tm_year))
     return;
-  if (M4ARG (7) && !m4_numeric_arg (context, me, M4ARG (7), &tm.tm_isdst))
+  if (M4ARG (7) && !m4_numeric_arg (context, me, M4ARG (7), M4ARGLEN (7),
+                                   &tm.tm_isdst))
     return;

   t = mktime (&tm);
@@ -216,7 +220,8 @@ M4BUILTIN_HANDLER (strftime)
   char *buf;
   int l;

-  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (2), &l))
+  if (!m4_numeric_arg (context, m4_arg_info (argv), M4ARG (2), M4ARGLEN (2),
+                      &l))
     return;

   t = l;
diff --git a/tests/null.err b/tests/null.err
index 7b9f798..edad891 100644
--- a/tests/null.err
+++ b/tests/null.err
@@ -7,10 +7,16 @@ changeresyntax:
 m4:null.m4:39: Warning: changeresyntax: bad syntax-spec: `\0'
 changesyntax:
 m4:null.m4:48: Warning: changesyntax: undefined syntax code: `\0'
+debuglen:
+m4:null.m4:53: Warning: debuglen: non-numeric argument `1\0001'
+decr:
+m4:null.m4:57: Warning: decr: non-numeric argument `1\0001'
 defn:
-m4:null.m4:57: Warning: defn: undefined macro `\0-\0'
+m4:null.m4:62: Warning: defn: undefined macro `\0-\0'
+divert:
+m4:null.m4:69: Warning: divert: non-numeric argument `1\0001'
 dumpdef:
-m4:null.m4:70: Warning: dumpdef: undefined macro `\0-\0'
+m4:null.m4:77: Warning: dumpdef: undefined macro `\0-\0'
 :      `empty'
 -:     `dash'
 --:   ``$0': $1'
@@ -18,21 +24,34 @@ m4:null.m4:70: Warning: dumpdef: undefined macro `\0-\0'
 --:    `dashes'
 body:  `--'
 errprint: -- --
+eval:
+m4:null.m4:90: Warning: eval: bad input: `1\0+1'
+m4:null.m4:92: Warning: eval: non-numeric argument `2\0002'
+m4:null.m4:94: Warning: eval: non-numeric argument `1\0001'
 format:
-m4:null.m4:87: Warning: format: unrecognized specifier in `%\0%'
-m4:null.m4:87: Warning: format: unrecognized specifier in `%\0%'
+m4:null.m4:99: Warning: format: unrecognized specifier in `%\0%'
+m4:null.m4:99: Warning: format: unrecognized specifier in `%\0%'
+incr:
+m4:null.m4:110: Warning: incr: non-numeric argument `1\0001'
+index:
+m4:null.m4:116: Warning: index: non-numeric argument `1\0001'
 indir:
-m4:null.m4:104: Warning: indir: undefined macro `\0-\0'
-m4:null.m4:106: Warning: \0\0%%: extra arguments ignored: 1 > 0
+m4:null.m4:121: Warning: indir: undefined macro `\0-\0'
+m4:null.m4:123: Warning: \0\0%%: extra arguments ignored: 1 > 0
 patsubst:
-m4:null.m4:124: Warning: patsubst: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:134: Warning: patsubst: bad syntax-spec: `\0'
+m4:null.m4:141: Warning: patsubst: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:151: Warning: patsubst: bad syntax-spec: `\0'
 regexp:
-m4:null.m4:146: Warning: regexp: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:156: Warning: regexp: bad syntax-spec: `\0'
+m4:null.m4:163: Warning: regexp: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:173: Warning: regexp: bad syntax-spec: `\0'
 renamesyms:
-m4:null.m4:161: Warning: renamesyms: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:167: Warning: renamesyms: bad syntax-spec: `\0'
+m4:null.m4:178: Warning: renamesyms: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:184: Warning: renamesyms: bad syntax-spec: `\0'
+substr:
+m4:null.m4:192: Warning: substr: non-numeric argument `1\0001'
+m4:null.m4:194: Warning: substr: non-numeric argument `1\0001'
 traceon:
 m4trace: -1- --(`--') -> `strange: --'
 m4trace: -1- body -> `-'
+undivert:
+m4:null.m4:218: Warning: undivert: invalid file name `1\0001'
diff --git a/tests/null.m4 b/tests/null.m4
index f7a1587..3d57806 100644
--- a/tests/null.m4
+++ b/tests/null.m4
@@ -1,7 +1,7 @@
 dnl Use `m4 -DNUL to print a NUL byte surrounded in [], then exit
 ifdef(`NUL', `[]m4exit`'')dnl
 # This file tests m4 behavior on NUL bytes.
-dnl Use `m4 -Dm4exit' to test rest of file.  NUL not a number, needs to warn
+dnl Use `m4 -Dm4exit' to test rest of file.
 m4exit(`22')dnl
 dnl Raw pass-through:
 raw: --
@@ -48,8 +48,13 @@ errprint(`changesyntax:
 ')changesyntax()dnl
 dnl Ignored by changesyntax: TODO - support ignored category?
 dnl Warning from debugfile: not tested yet. No file name includes NUL, needs 
to warn
+dnl Warning from debuglen:
+errprint(`debuglen:
+')debuglen(`11')dnl
 dnl Warning from debugmode: not tested yet. NUL not a valid mode, needs to warn
-dnl Warning from decr: not tested yet. NUL not a number, needs to warn
+dnl Warning from decr:
+errprint(`decr:
+')decr(`11')dnl
 dnl Definition of define:
 `define:' define(`body', `--')body
 dnl Undefined argument of defn:
@@ -59,7 +64,9 @@ dnl Defined macro name in defn:
 `defn:' defn(`--')dnl
 dnl Macro contents in defn:
  defn(`body')
-dnl Argument to divert: not tested yet. NUL not a number, needs to warn
+dnl Argument to divert:
+errprint(`divert:
+')divert(`11')dnl
 dnl Passed through diversion by divert:
 divert(`1')`divert:' --
 divert`'undivert(`1')dnl
@@ -78,8 +85,13 @@ errprint(`errprint:' --, `--
 dnl Passed to esyscmd: not tested yet. NUL truncates string, needs to warn
 dnl Generated from esyscmd:
 `esyscmd:' esyscmd(__program__` -DNUL '__file__) sysval
-dnl First argument of eval: not tested yet. NUL not a number, needs to warn
-dnl Other arguments of eval: not tested yet. NUL not a number, needs to warn
+dnl First argument of eval:
+errprint(`eval:
+')eval(`1+1')dnl
+dnl Second argument of eval:
+eval(`1', `22')dnl
+dnl Third argument of eval:
+eval(`1', `10', `11')dnl
 dnl First argument to format:
 `format:' format(`%s%s', `-', `-')dnl
 dnl Invalid specifier in format:
@@ -93,10 +105,15 @@ dnl Macro name in ifdef, passed through ifdef:
 dnl Compared in ifelse, passed through ifelse:
 `ifelse:' ifelse(`-', `--', `oops', `--', --, `yes: --')
 dnl Warning from include: not tested yet. No file name includes NUL, needs to 
warn
-dnl Warning from incr: not tested yet. NUL not a number, needs to warn
+dnl Warning from incr:
+errprint(`incr:
+')incr(`11')dnl
 dnl Passed through index:
 `index:' index(`ab', `b') index(`-', `') index(`', `-')dnl
  index(`-', `-')
+dnl Third argument of index:
+errprint(`index:
+')index(`aba', `a', `11')dnl
 dnl Defined first argument of indir:
 `indir:' indir(`--', 11)dnl
 dnl Undefined first argument of indir:
@@ -169,8 +186,14 @@ dnl Passed through shift:
 `shift:' shift(`hi', `--', --)
 dnl Warning from sinclude: not tested yet. No file name includes NUL, needs to 
warn
 dnl First argument of substr:
-`substr:' substr(`----', `1', `3')
-dnl Other arguments of substr: not tested yet. NUL not a number, needs to warn.
+`substr:' substr(`----', `1', `3')dnl
+dnl Second argument of substr:
+errprint(`substr:
+')substr(`abc', `11')dnl
+dnl Third argument of substr:
+substr(`abc', `1', `11')dnl
+dnl Fourth argument of substr:
+ substr(`abc', `1', `1', `11')
 dnl Warning from syncoutput: not tested yet. No mode contains NUL, needs to 
warn
 dnl Passed to syscmd: not tested yet. NUL truncates string, needs to warn
 dnl Sysval takes no arguments, and never produces NUL.
@@ -190,5 +213,7 @@ dnl Character ranges of translit:
 dnl Defined argument of undefine:
 `undefine:' undefine(`--')ifdef(`--', `oops', `ok')
 dnl Undefined argument of undefine: not tested yet. Should it warn?
-dnl Warning from undivert: not tested yet. No file name or number includes 
NUL, needs to warn
+dnl Warning from undivert:
+errprint(`undivert:
+')undivert(`11')dnl
 dnl other modules need to be tested independently
diff --git a/tests/null.out b/tests/null.out
index 97f80dd..cfb78c0 100644
--- a/tests/null.out
+++ b/tests/null.out
@@ -24,7 +24,7 @@ pushdef: ok -
 regexp: 2 ! 1 -- -
 renamesyms: 0 0
 shift: --,--
-substr: --
+substr: -- a11c
 traceon: strange: -- -
 translit: -- .. cd
 undefine: ok
diff --git a/tests/others.at b/tests/others.at
index 6776ee3..3f12351 100644
--- a/tests/others.at
+++ b/tests/others.at
@@ -1,5 +1,6 @@
 # Hand crafted tests for GNU M4.                               -*- Autotest -*-
-# Copyright (C) 2001, 2006, 2007, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.

 # This file is part of GNU M4.
 #
@@ -432,8 +433,11 @@ dnl all but m4exit
 AT_CHECK_M4([-Dm4exit -I "$abs_srcdir" null.m4], [0], [expout], [experr])

 dnl just m4exit
-AT_CHECK_M4(["$abs_srcdir/null.m4"], [2],
+AT_CHECK_M4(["$abs_srcdir/null.m4"], [1],
 [[# This file tests m4 behavior on NUL bytes.
+]], [stderr])
+AT_CHECK([sed "s|$abs_srcdir/||" stderr], [0],
+[[m4:null.m4:5: Warning: m4exit: non-numeric argument `2\0002'
 ]])

 AT_CLEANUP
-- 
1.6.1.2


From 5b4325856360a5d8836f13f17938b1acccd422cf Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Sat, 7 Feb 2009 18:50:21 -0700
Subject: [PATCH 2/3] Stage 28b: Warn on embedded NUL in file arguments.

* m4/path.c (m4_path_search): Quote file names in message.
* modules/m4.c (syscmd, include, m4_make_temp): Handle embedded
NUL.
* modules/gnu.c (debugfile, esyscmd): Likewise.
* tests/others.at (nul character): Adjust test.
* tests/builtins.at (mkdtemp, mkstemp): Likewise.
* tests/null.m4: Likewise.
* tests/null.out: Likewise.
* tests/null.err: Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog         |   15 +++++++++++
 m4/path.c         |    7 +++--
 modules/gnu.c     |   26 +++++++++++++++----
 modules/m4.c      |   35 +++++++++++++++++++++------
 tests/builtins.at |   10 ++++----
 tests/null.err    |   68 +++++++++++++++++++++++++++++++++++------------------
 tests/null.m4     |   34 +++++++++++++++++++-------
 tests/null.out    |    5 +++-
 tests/others.at   |    2 +-
 9 files changed, 146 insertions(+), 56 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index def85f5..29fc9ef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2009-02-11  Eric Blake  <address@hidden>

+       Stage 28b: Warn on embedded NUL in file arguments.
+       Quote warning messages related to file and other NUL-terminated
+       system commands.
+       Memory impact: none.
+       Speed impact: none noticed.
+       * m4/path.c (m4_path_search): Quote file names in message.
+       * modules/m4.c (syscmd, include, m4_make_temp): Handle embedded
+       NUL.
+       * modules/gnu.c (debugfile, esyscmd): Likewise.
+       * tests/others.at (nul character): Adjust test.
+       * tests/builtins.at (mkdtemp, mkstemp): Likewise.
+       * tests/null.m4: Likewise.
+       * tests/null.out: Likewise.
+       * tests/null.err: Likewise.
+
        Stage 28a: Warn on embedded NUL in numeric arguments.
        Quote warning messages related to numeric parsing in order to
        handle embedded NUL.
diff --git a/m4/path.c b/m4/path.c
index bf15193..c83aa21 100644
--- a/m4/path.c
+++ b/m4/path.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1998, 2004, 2006, 2007,
-   2008 Free Software Foundation, Inc.
+   2008, 2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -198,8 +198,9 @@ m4_path_search (m4 *context, const char *file, char 
**expanded_name)
       if (fp != NULL)
        {
          m4_debug_message (context, M4_DEBUG_TRACE_PATH,
-                           _("path search for `%s' found `%s'"),
-                           file, name);
+                           _("path search for %s found %s"),
+                           quotearg_style (locale_quoting_style, file),
+                           quotearg_n_style (1, locale_quoting_style, name));
          if (expanded_name != NULL)
            *expanded_name = name;
          else
diff --git a/modules/gnu.c b/modules/gnu.c
index ce14532..69d938f 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -583,9 +583,17 @@ M4BUILTIN_HANDLER (debugfile)
     m4_debug_set_output (context, me, NULL);
   else if (m4_get_safer_opt (context) && !m4_arg_empty (argv, 1))
     m4_error (context, 0, 0, me, _("disabled by --safer"));
-  else if (!m4_debug_set_output (context, me, M4ARG (1)))
-    m4_error (context, 0, errno, me, _("cannot set debug file %s"),
-             quotearg_style (locale_quoting_style, M4ARG (1)));
+  else
+    {
+      const char *str = M4ARG (1);
+      size_t len = M4ARGLEN (1);
+      if (strlen (str) < len)
+       m4_warn (context, 0, me, _("argument %s truncated"),
+                quotearg_style_mem (locale_quoting_style, str, len));
+      if (!m4_debug_set_output (context, me, str))
+       m4_warn (context, errno, me, _("cannot set debug file %s"),
+             quotearg_style (locale_quoting_style, str));
+    }
 }


@@ -635,6 +643,8 @@ M4BUILTIN_HANDLER (debugmode)
 M4BUILTIN_HANDLER (esyscmd)
 {
   const m4_call_info *me = m4_arg_info (argv);
+  const char *cmd = M4ARG (1);
+  size_t len = M4ARGLEN (1);
   M4_MODULE_IMPORT (m4, m4_set_sysval);
   M4_MODULE_IMPORT (m4, m4_sysval_flush);

@@ -648,9 +658,12 @@ M4BUILTIN_HANDLER (esyscmd)
          m4_error (context, 0, 0, me, _("disabled by --safer"));
          return;
        }
+      if (strlen (cmd) != len)
+       m4_warn (context, 0, me, _("argument %s truncated"),
+                quotearg_style_mem (locale_quoting_style, cmd, len));

       /* Optimize the empty command.  */
-      if (m4_arg_empty (argv, 1))
+      if (!*cmd)
        {
          m4_set_sysval (0);
          return;
@@ -658,11 +671,12 @@ M4BUILTIN_HANDLER (esyscmd)

       m4_sysval_flush (context, false);
       errno = 0;
-      pin = popen (M4ARG (1), "r");
+      pin = popen (cmd, "r");
       if (pin == NULL)
        {
          m4_error (context, 0, errno, me,
-                   _("cannot open pipe to command `%s'"), M4ARG (1));
+                   _("cannot open pipe to command %s"),
+                   quotearg_style (locale_quoting_style, cmd));
          m4_set_sysval (-1);
        }
       else
diff --git a/modules/m4.c b/modules/m4.c
index 44e8a0a..8ce419d 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -504,20 +504,27 @@ m4_sysval_flush (m4 *context, bool report)

 M4BUILTIN_HANDLER (syscmd)
 {
+  const m4_call_info *me = m4_arg_info (argv);
+  const char *cmd = M4ARG (1);
+  size_t len = M4ARGLEN (1);
+
   if (m4_get_safer_opt (context))
     {
       m4_error (context, 0, 0, m4_arg_info (argv), _("disabled by --safer"));
       return;
     }
+  if (strlen (cmd) != len)
+    m4_warn (context, 0, me, _("argument %s truncated"),
+            quotearg_style_mem (locale_quoting_style, cmd, len));

   /* Optimize the empty command.  */
-  if (m4_arg_empty (argv, 1))
+  if (!*cmd)
     {
       m4_set_sysval (0);
       return;
     }
   m4_sysval_flush (context, false);
-  m4_sysval = system (M4ARG (1));
+  m4_sysval = system (cmd);
   /* FIXME - determine if libtool works for OS/2, in which case the
      FUNC_SYSTEM_BROKEN section on the branch must be ported to work
      around the bug in their EMX libc system().  */
@@ -672,13 +679,19 @@ include (m4 *context, int argc, m4_macro_args *argv, bool 
silent)
 {
   FILE *fp;
   char *name = NULL;
+  const m4_call_info *me = m4_arg_info (argv);
+  const char *arg = M4ARG (1);
+  size_t len = M4ARGLEN (1);

-  fp = m4_path_search (context, M4ARG (1), &name);
+  if (strlen (arg) != len)
+    m4_warn (context, 0, me, _("argument %s truncated"),
+            quotearg_style_mem (locale_quoting_style, arg, len));
+  fp = m4_path_search (context, arg, &name);
   if (fp == NULL)
     {
       if (!silent)
        m4_error (context, 0, errno, m4_arg_info (argv), _("cannot open %s"),
-                 quotearg_style (locale_quoting_style, M4ARG (1)));
+                 quotearg_style (locale_quoting_style, arg));
       return;
     }

@@ -725,6 +738,12 @@ m4_make_temp (m4 *context, m4_obstack *obs, const 
m4_call_info *caller,
      successful.  */
   assert (obstack_object_size (obs) == 0);
   obstack_grow (obs, quotes->str1, quotes->len1);
+  if (strlen (pattern) < len)
+    {
+      m4_warn (context, 0, caller, _("argument %s truncated"),
+              quotearg_style_mem (locale_quoting_style, pattern, len));
+      len = strlen (pattern);
+    }
   obstack_grow (obs, pattern, len);
   for (i = 0; len > 0 && i < 6; i++)
     if (pattern[--len] != 'X')
@@ -740,10 +759,10 @@ m4_make_temp (m4 *context, m4_obstack *obs, const 
m4_call_info *caller,
       /* This use of _() will need to change if xgettext ever changes
         its undocumented behavior of parsing both string options.  */

-      m4_error (context, 0, errno, caller,
-               _(dir ? "cannot create directory from template `%s'"
-                 : "cannot create file from template `%s'"),
-               pattern);
+      m4_warn (context, errno, caller,
+              _(dir ? "cannot create directory from template %s"
+                : "cannot create file from template %s"),
+              quotearg_style (locale_quoting_style, pattern));
       obstack_free (obs, obstack_finish (obs));
     }
   else
diff --git a/tests/builtins.at b/tests/builtins.at
index f819403..397f649 100644
--- a/tests/builtins.at
+++ b/tests/builtins.at
@@ -1,5 +1,5 @@
 # Hand crafted tests for GNU M4.                               -*- Autotest -*-
-# Copyright (C) 2001, 2006, 2007, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.

 # This file is part of GNU M4.
 #
@@ -713,8 +713,8 @@ dnl Check that on error, the expansion is void
 AT_DATA([[in]],
 [[mkdtemp(`no_such_dir/m4-fooXXXXXX')
 ]])
-AT_CHECK_M4([in], [1], [[
-]], [[m4:in:1: mkdtemp: cannot create directory from template 
`no_such_dir/m4-fooXXXXXX': No such file or directory
+AT_CHECK_M4([in], [0], [[
+]], [[m4:in:1: Warning: mkdtemp: cannot create directory from template 
`no_such_dir/m4-fooXXXXXX': No such file or directory
 ]])

 dnl Check that umask has an effect.  drws--S--T is okay.
@@ -742,8 +742,8 @@ dnl Check that on error, the expansion is void
 AT_DATA([[in]],
 [[mkstemp(`no_such_dir/m4-fooXXXXXX')
 ]])
-AT_CHECK_M4([in], [1], [[
-]], [[m4:in:1: mkstemp: cannot create file from template 
`no_such_dir/m4-fooXXXXXX': No such file or directory
+AT_CHECK_M4([in], [0], [[
+]], [[m4:in:1: Warning: mkstemp: cannot create file from template 
`no_such_dir/m4-fooXXXXXX': No such file or directory
 ]])

 dnl Check that extra X are appended, but not trailing NUL
diff --git a/tests/null.err b/tests/null.err
index edad891..01f2a8e 100644
--- a/tests/null.err
+++ b/tests/null.err
@@ -7,16 +7,19 @@ changeresyntax:
 m4:null.m4:39: Warning: changeresyntax: bad syntax-spec: `\0'
 changesyntax:
 m4:null.m4:48: Warning: changesyntax: undefined syntax code: `\0'
+debugfile:
+m4:null.m4:52: Warning: debugfile: argument `/no/such\0/file' truncated
+m4:null.m4:52: Warning: debugfile: cannot set debug file `/no/such': No such 
file or directory
 debuglen:
-m4:null.m4:53: Warning: debuglen: non-numeric argument `1\0001'
+m4:null.m4:55: Warning: debuglen: non-numeric argument `1\0001'
 decr:
-m4:null.m4:57: Warning: decr: non-numeric argument `1\0001'
+m4:null.m4:59: Warning: decr: non-numeric argument `1\0001'
 defn:
-m4:null.m4:62: Warning: defn: undefined macro `\0-\0'
+m4:null.m4:64: Warning: defn: undefined macro `\0-\0'
 divert:
-m4:null.m4:69: Warning: divert: non-numeric argument `1\0001'
+m4:null.m4:71: Warning: divert: non-numeric argument `1\0001'
 dumpdef:
-m4:null.m4:77: Warning: dumpdef: undefined macro `\0-\0'
+m4:null.m4:79: Warning: dumpdef: undefined macro `\0-\0'
 :      `empty'
 -:     `dash'
 --:   ``$0': $1'
@@ -24,34 +27,53 @@ m4:null.m4:77: Warning: dumpdef: undefined macro `\0-\0'
 --:    `dashes'
 body:  `--'
 errprint: -- --
+esyscmd:
+m4:null.m4:89: Warning: esyscmd: argument `echo +\0+' truncated
 eval:
-m4:null.m4:90: Warning: eval: bad input: `1\0+1'
-m4:null.m4:92: Warning: eval: non-numeric argument `2\0002'
-m4:null.m4:94: Warning: eval: non-numeric argument `1\0001'
+m4:null.m4:94: Warning: eval: bad input: `1\0+1'
+m4:null.m4:96: Warning: eval: non-numeric argument `2\0002'
+m4:null.m4:98: Warning: eval: non-numeric argument `1\0001'
 format:
-m4:null.m4:99: Warning: format: unrecognized specifier in `%\0%'
-m4:null.m4:99: Warning: format: unrecognized specifier in `%\0%'
+m4:null.m4:103: Warning: format: unrecognized specifier in `%\0%'
+m4:null.m4:103: Warning: format: unrecognized specifier in `%\0%'
+include:
+m4:null.m4:113: Warning: include: argument `/no/such\0/file' truncated
+m4:null.m4:113: include: cannot open `/no/such': No such file or directory
 incr:
-m4:null.m4:110: Warning: incr: non-numeric argument `1\0001'
+m4:null.m4:116: Warning: incr: non-numeric argument `1\0001'
 index:
-m4:null.m4:116: Warning: index: non-numeric argument `1\0001'
+m4:null.m4:122: Warning: index: non-numeric argument `1\0001'
 indir:
-m4:null.m4:121: Warning: indir: undefined macro `\0-\0'
-m4:null.m4:123: Warning: \0\0%%: extra arguments ignored: 1 > 0
+m4:null.m4:127: Warning: indir: undefined macro `\0-\0'
+m4:null.m4:129: Warning: \0\0%%: extra arguments ignored: 1 > 0
+maketemp:
+m4:null.m4:144: Warning: maketemp: recommend using mkstemp instead
+m4:null.m4:144: Warning: maketemp: argument `/no/such\0/file' truncated
+m4:null.m4:144: Warning: maketemp: cannot create file from template 
`/no/such': No such file or directory
+mkdtemp:
+m4:null.m4:147: Warning: mkdtemp: argument `/no/such\0/file' truncated
+m4:null.m4:147: Warning: mkdtemp: cannot create directory from template 
`/no/such': No such file or directory
+mkstemp:
+m4:null.m4:150: Warning: mkstemp: argument `/no/such\0/file' truncated
+m4:null.m4:150: Warning: mkstemp: cannot create file from template `/no/such': 
No such file or directory
 patsubst:
-m4:null.m4:141: Warning: patsubst: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:151: Warning: patsubst: bad syntax-spec: `\0'
+m4:null.m4:153: Warning: patsubst: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:163: Warning: patsubst: bad syntax-spec: `\0'
 regexp:
-m4:null.m4:163: Warning: regexp: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:173: Warning: regexp: bad syntax-spec: `\0'
+m4:null.m4:175: Warning: regexp: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:185: Warning: regexp: bad syntax-spec: `\0'
 renamesyms:
-m4:null.m4:178: Warning: renamesyms: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:184: Warning: renamesyms: bad syntax-spec: `\0'
+m4:null.m4:190: Warning: renamesyms: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:196: Warning: renamesyms: bad syntax-spec: `\0'
+sinclude:
+m4:null.m4:201: Warning: sinclude: argument `/no/such\0/file' truncated
 substr:
-m4:null.m4:192: Warning: substr: non-numeric argument `1\0001'
-m4:null.m4:194: Warning: substr: non-numeric argument `1\0001'
+m4:null.m4:206: Warning: substr: non-numeric argument `1\0001'
+m4:null.m4:208: Warning: substr: non-numeric argument `1\0001'
+syscmd:
+m4:null.m4:214: Warning: syscmd: argument `echo +\0+' truncated
 traceon:
 m4trace: -1- --(`--') -> `strange: --'
 m4trace: -1- body -> `-'
 undivert:
-m4:null.m4:218: Warning: undivert: invalid file name `1\0001'
+m4:null.m4:234: Warning: undivert: invalid file name `1\0001'
diff --git a/tests/null.m4 b/tests/null.m4
index 3d57806..6071144 100644
--- a/tests/null.m4
+++ b/tests/null.m4
@@ -47,7 +47,9 @@ dnl Warning from changesyntax:
 errprint(`changesyntax:
 ')changesyntax()dnl
 dnl Ignored by changesyntax: TODO - support ignored category?
-dnl Warning from debugfile: not tested yet. No file name includes NUL, needs 
to warn
+dnl Warning from debugfile:
+errprint(`debugfile:
+')debugfile(`/no/such/file')dnl
 dnl Warning from debuglen:
 errprint(`debuglen:
 ')debuglen(`11')dnl
@@ -82,9 +84,11 @@ dumpdef(`body')dnl
 dnl Passed through errprint:
 errprint(`errprint:' --, `--
 ')dnl
-dnl Passed to esyscmd: not tested yet. NUL truncates string, needs to warn
+dnl Passed to esyscmd:
+`esyscmd:'errprint(`esyscmd:
+') esyscmd(`echo ++')sysval dnl
 dnl Generated from esyscmd:
-`esyscmd:' esyscmd(__program__` -DNUL '__file__) sysval
+esyscmd(__program__` -DNUL '__file__) sysval
 dnl First argument of eval:
 errprint(`eval:
 ')eval(`1+1')dnl
@@ -104,7 +108,9 @@ dnl Macro name in ifdef, passed through ifdef:
  ifdef(, `oops: --', `no: --')
 dnl Compared in ifelse, passed through ifelse:
 `ifelse:' ifelse(`-', `--', `oops', `--', --, `yes: --')
-dnl Warning from include: not tested yet. No file name includes NUL, needs to 
warn
+dnl Warning from include:
+errprint(`include:
+')include(`/no/such/file')dnl
 dnl Warning from incr:
 errprint(`incr:
 ')incr(`11')dnl
@@ -133,9 +139,15 @@ dnl Defined macro name in m4symbols:
 dnl Passed through m4wrap:
 m4wrap(``m4wrap:' --
 ')dnl
-dnl Warning from maketemp: not tested yet. No file name includes NUL, needs to 
warn
-dnl Warning from mkdtemp: not tested yet. No file name includes NUL, needs to 
warn
-dnl Warning from mkstemp: not tested yet. No file name includes NUL, needs to 
warn
+dnl Warning from maketemp:
+errprint(`maketemp:
+')maketemp(`/no/such/file')dnl
+dnl Warning from mkdtemp:
+errprint(`mkdtemp:
+')mkdtemp(`/no/such/file')dnl
+dnl Warning from mkstemp:
+errprint(`mkstemp:
+')mkstemp(`/no/such/file')dnl
 dnl Bad regex in patsubst:
 errprint(`patsubst:
 ')patsubst(`a', `\\')dnl
@@ -184,7 +196,9 @@ dnl Syntax argument of renamesyms:
 renamesyms(`a', `b', `')dnl
 dnl Passed through shift:
 `shift:' shift(`hi', `--', --)
-dnl Warning from sinclude: not tested yet. No file name includes NUL, needs to 
warn
+dnl Warning from sinclude:
+errprint(`sinclude:
+')sinclude(`/no/such/file')dnl
 dnl First argument of substr:
 `substr:' substr(`----', `1', `3')dnl
 dnl Second argument of substr:
@@ -195,7 +209,9 @@ substr(`abc', `1', `11')dnl
 dnl Fourth argument of substr:
  substr(`abc', `1', `1', `11')
 dnl Warning from syncoutput: not tested yet. No mode contains NUL, needs to 
warn
-dnl Passed to syscmd: not tested yet. NUL truncates string, needs to warn
+dnl Passed to syscmd:
+`syscmd:'errprint(`syscmd:
+') syscmd(`echo ++')sysval
 dnl Sysval takes no arguments, and never produces NUL.
 dnl Passed to traceoff:
 traceoff(`--', `')dnl
diff --git a/tests/null.out b/tests/null.out
index cfb78c0..3859b23 100644
--- a/tests/null.out
+++ b/tests/null.out
@@ -10,7 +10,8 @@ changesyntax: -- --: dash echo .... dash- nul
 define: --
 defn: `$0': $1 --
 divert: --
-esyscmd: [] 0
+esyscmd: +
+0 [] 0
 format: -- 
 ifdef: yes: -- no: --
 ifelse: yes: --
@@ -25,6 +26,8 @@ regexp: 2 ! 1 -- -
 renamesyms: 0 0
 shift: --,--
 substr: -- a11c
+syscmd: +
+0
 traceon: strange: -- -
 translit: -- .. cd
 undefine: ok
diff --git a/tests/others.at b/tests/others.at
index 3f12351..779fd3c 100644
--- a/tests/others.at
+++ b/tests/others.at
@@ -430,7 +430,7 @@ $SED "s|null.m4|$abs_srcdir/null.m4|" < 
"$abs_srcdir/null.out" > expout
 $SED "s|null.m4|$abs_srcdir/null.m4|" < "$abs_srcdir/null.err" > experr

 dnl all but m4exit
-AT_CHECK_M4([-Dm4exit -I "$abs_srcdir" null.m4], [0], [expout], [experr])
+AT_CHECK_M4([-Dm4exit -I "$abs_srcdir" null.m4], [1], [expout], [experr])

 dnl just m4exit
 AT_CHECK_M4(["$abs_srcdir/null.m4"], [1],
-- 
1.6.1.2


From 3e12364a5a32e5947b0c31a2ee6dc5351b7af5e6 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 10 Feb 2009 14:24:11 -0700
Subject: [PATCH 3/3] Stage 28c: Warn on embedded NUL in remaining cases.

* m4/m4module.h (m4_debug_decode, m4_parse_truth_arg): Add
parameter.
* m4/macro.c (m4_macro_call): Improve diagnostic.
* modules/m4.c (defn): Likewise.
* m4/debug.c (m4_debug_decode): Handle embedded NUL.
* m4/utility.c (m4_parse_truth_arg): Likewise.
* modules/format.c (arg_int, arg_long, arg_double): Likewise.
(arg_string): New function.
(ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE): Update callers.
* src/main.c (main): Likewise.
* src/freeze.c (reload_frozen_state): Likewise.
* modules/gnu.c (debugmode, syncoutput): Likewise.
* tests/options.at (--regexp-syntax): Adjust test.
* tests/freeze.at (reloading nul): Likewise.
* tests/null.m4: Likewise.
* tests/null.out: Likewise.
* tests/null.err: Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog        |   22 +++++++++++++++
 m4/debug.c       |   29 +++++++++++++-------
 m4/m4module.h    |    4 +-
 m4/macro.c       |    7 +++--
 m4/utility.c     |   15 ++++++----
 modules/format.c |   61 ++++++++++++++++++++++++++++--------------
 modules/gnu.c    |   12 +++++---
 modules/m4.c     |    8 +++--
 src/freeze.c     |   33 ++++++++++++++++++++---
 src/main.c       |   26 ++++++++++-------
 tests/freeze.at  |   14 +++++++++-
 tests/null.err   |   78 ++++++++++++++++++++++++++++++-----------------------
 tests/null.m4    |   21 ++++++++++----
 tests/null.out   |    2 +-
 tests/options.at |    5 ++-
 15 files changed, 230 insertions(+), 107 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 29fc9ef..278093f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2009-02-11  Eric Blake  <address@hidden>

+       Stage 28c: Warn on embedded NUL in remaining cases.
+       Ensure all remaining warnings can handle embedded NUL.
+       Memory impact: none.
+       Speed impact: none noticed.
+       * m4/m4module.h (m4_debug_decode, m4_parse_truth_arg): Add
+       parameter.
+       * m4/macro.c (m4_macro_call): Improve diagnostic.
+       * modules/m4.c (defn): Likewise.
+       * m4/debug.c (m4_debug_decode): Handle embedded NUL.
+       * m4/utility.c (m4_parse_truth_arg): Likewise.
+       * modules/format.c (arg_int, arg_long, arg_double): Likewise.
+       (arg_string): New function.
+       (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE): Update callers.
+       * src/main.c (main): Likewise.
+       * src/freeze.c (reload_frozen_state): Likewise.
+       * modules/gnu.c (debugmode, syncoutput): Likewise.
+       * tests/options.at (--regexp-syntax): Adjust test.
+       * tests/freeze.at (reloading nul): Likewise.
+       * tests/null.m4: Likewise.
+       * tests/null.out: Likewise.
+       * tests/null.err: Likewise.
+
        Stage 28b: Warn on embedded NUL in file arguments.
        Quote warning messages related to file and other NUL-terminated
        system commands.
diff --git a/m4/debug.c b/m4/debug.c
index f5cb4e2..1e1e9a3 100644
--- a/m4/debug.c
+++ b/m4/debug.c
@@ -1,5 +1,5 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1991, 1992, 1993, 1994, 2006, 2007, 2008 Free
+   Copyright (C) 1991, 1992, 1993, 1994, 2006, 2007, 2008, 2009 Free
    Software Foundation, Inc.

    This file is part of GNU M4.
@@ -30,22 +30,31 @@ static void set_debug_file (m4 *, const m4_call_info *, 
FILE *);


 
-/* Function to decode the debugging flags OPTS.  Used by main while
-   processing option -d, and by the builtin debugmode ().  */
+/* Function to decode the debugging flags OPTS of length LEN; or
+   SIZE_MAX if OPTS is NUL-terminated.  If OPTS is NULL, use the
+   default flags.  Used by main while processing option -d, and by the
+   builtin debugmode ().  */
 int
-m4_debug_decode (m4 *context, const char *opts)
+m4_debug_decode (m4 *context, const char *opts, size_t len)
 {
   int previous = context->debug_level;
   int level;
   char mode = '\0';

-  if (opts == NULL || *opts == '\0')
+  if (!opts)
+    opts = "";
+  if (len == SIZE_MAX)
+    len = strlen (opts);
+  if (!len)
     level = M4_DEBUG_TRACE_DEFAULT | previous;
   else
     {
       if (*opts == '-' || *opts == '+')
-       mode = *opts++;
-      for (level = 0; *opts; opts++)
+       {
+         len--;
+         mode = *opts++;
+       }
+      for (level = 0; len--; opts++)
        {
          switch (*opts)
            {
@@ -101,9 +110,9 @@ m4_debug_decode (m4 *context, const char *opts)
              level |= M4_DEBUG_TRACE_DEREF;
              break;

-            case 'o':
-               level |= M4_DEBUG_TRACE_OUTPUT_DUMPDEF;
-               break;
+           case 'o':
+              level |= M4_DEBUG_TRACE_OUTPUT_DUMPDEF;
+              break;

            case 'V':
              level |= M4_DEBUG_TRACE_VERBOSE;
diff --git a/m4/m4module.h b/m4/m4module.h
index 84c203e..07f8c1a 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -172,7 +172,7 @@ extern bool m4_bad_argc        (m4 *, size_t, const 
m4_call_info *, size_t,
 extern bool    m4_numeric_arg     (m4 *, const m4_call_info *, const char *,
                                    size_t, int *);
 extern bool    m4_parse_truth_arg (m4 *, const m4_call_info *, const char *,
-                                   bool);
+                                   size_t, bool);
 extern m4_symbol *m4_symbol_value_lookup (m4 *, m4_macro_args *, size_t, bool);

 /* Error handling.  */
@@ -421,7 +421,7 @@ enum {

 #define m4_is_debug_bit(C,B)   ((m4_get_debug_level_opt (C) & (B)) != 0)

-extern int     m4_debug_decode         (m4 *, const char *);
+extern int     m4_debug_decode         (m4 *, const char *, size_t);
 extern bool    m4_debug_set_output     (m4 *, const m4_call_info *,
                                         const char *);
 extern void    m4_debug_message_prefix (m4 *);
diff --git a/m4/macro.c b/m4/macro.c
index 7e6ef47..738c8ca 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007,
-   2008 Free Software Foundation, Inc.
+   2008, 2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -675,8 +675,9 @@ m4_macro_call (m4 *context, m4_symbol_value *value, 
m4_obstack *expansion,
                                          argv);
       else if (m4_is_symbol_value_placeholder (value))
        m4_warn (context, 0, argv->info,
-                _("builtin `%s' requested by frozen file not found"),
-                m4_get_symbol_value_placeholder (value));
+                _("builtin %s requested by frozen file not found"),
+                quotearg_style (locale_quoting_style,
+                                m4_get_symbol_value_placeholder (value)));
       else
        {
          assert (!"m4_macro_call");
diff --git a/m4/utility.c b/m4/utility.c
index 6d19526..215fabd 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -102,15 +102,17 @@ m4_numeric_arg (m4 *context, const m4_call_info *caller, 
const char *arg,
   return true;
 }

-/* Parse ARG as a truth value.  If unrecognized, issue a warning on
-   behalf of CALLER and return PREVIOUS; otherwise return the parsed
-   value.  */
+/* Parse ARG of length LEN as a truth value.  If ARG is NUL, use ""
+   instead; otherwise, ARG must have a NUL-terminator (even if it also
+   has embedded NUL).  If LEN is SIZE_MAX, use the string length of
+   ARG.  If unrecognized, issue a warning on behalf of CALLER and
+   return PREVIOUS; otherwise return the parsed value.  */
 bool
 m4_parse_truth_arg (m4 *context, const m4_call_info *caller, const char *arg,
-                   bool previous)
+                   size_t len, bool previous)
 {
   /* 0, no, off, blank... */
-  if (!arg || arg[0] == '\0'
+  if (!arg || len == 0
       || arg[0] == '0'
       || arg[0] == 'n' || arg[0] == 'N'
       || ((arg[0] == 'o' || arg[0] == 'O')
@@ -122,7 +124,8 @@ m4_parse_truth_arg (m4 *context, const m4_call_info 
*caller, const char *arg,
       || ((arg[0] == 'o' || arg[0] == 'O')
          && (arg[1] == 'n' || arg[1] == 'N')))
     return true;
-  m4_warn (context, 0, caller, _("unknown directive `%s'"), arg);
+  m4_warn (context, 0, caller, _("unknown directive %s"),
+          quotearg_style_mem (locale_quoting_style, arg, len));
   return previous;
 }

diff --git a/modules/format.c b/modules/format.c
index af983cd..55333be 100644
--- a/modules/format.c
+++ b/modules/format.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007,
-   2008 Free Software Foundation, Inc.
+   2008, 2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -26,24 +26,27 @@
    same size; likewise for long and unsigned long.  We do not yet
    handle long double or long long.  */

-/* Parse STR as an integer, reporting warnings on behalf of ME.  */
+/* Parse STR of length LEN as an integer, reporting warnings on behalf
+   of ME.  */
 static int
-arg_int (struct m4 *context, const m4_call_info *me, const char *str)
+arg_int (struct m4 *context, const m4_call_info *me, const char *str,
+        size_t len)
 {
   char *endp;
   long value;

   /* TODO - also allow parsing `'a' or `"a' which results in the
      numeric value of 'a', as in printf(1).  */
-  if (*str == '\0')
+  if (!len)
     {
       m4_warn (context, 0, me, _("empty string treated as 0"));
       return 0;
     }
   errno = 0;
   value = strtol (str, &endp, 10);
-  if (*endp != '\0')
-    m4_warn (context, 0, me, _("non-numeric argument `%s'"), str);
+  if (endp - str != len)
+    m4_warn (context, 0, me, _("non-numeric argument %s"),
+            quotearg_style_mem (locale_quoting_style, str, len));
   else if (isspace (to_uchar (*str)))
     m4_warn (context, 0, me, _("leading whitespace ignored"));
   else if (errno == ERANGE || (int) value != value)
@@ -51,24 +54,27 @@ arg_int (struct m4 *context, const m4_call_info *me, const 
char *str)
   return value;
 }

-/* Parse STR as a long, reporting warnings on behalf of ME.  */
+/* Parse STR of length LEN as a long, reporting warnings on behalf of
+   ME.  */
 static long
-arg_long (struct m4 *context, const m4_call_info *me, const char *str)
+arg_long (struct m4 *context, const m4_call_info *me, const char *str,
+         size_t len)
 {
   char *endp;
   long value;

   /* TODO - also allow parsing `'a' or `"a' which results in the
      numeric value of 'a', as in printf(1).  */
-  if (*str == '\0')
+  if (!len)
     {
       m4_warn (context, 0, me, _("empty string treated as 0"));
       return 0L;
     }
   errno = 0;
   value = strtol (str, &endp, 10);
-  if (*endp != '\0')
-    m4_warn (context, 0, me, _("non-numeric argument `%s'"), str);
+  if (endp - str != len)
+    m4_warn (context, 0, me, _("non-numeric argument %s"),
+            quotearg_style_mem (locale_quoting_style, str, len));
   else if (isspace (to_uchar (*str)))
     m4_warn (context, 0, me, _("leading whitespace ignored"));
   else if (errno == ERANGE)
@@ -76,22 +82,37 @@ arg_long (struct m4 *context, const m4_call_info *me, const 
char *str)
   return value;
 }

-/* Parse STR as a double, reporting warnings on behalf of ME.  */
+/* Check STR of length LEN for embedded NUL, reporting warnings on
+   behalf of ME.  */
+static const char *
+arg_string (struct m4 *context, const m4_call_info *me, const char *str,
+           size_t len)
+{
+  if (strlen (str) < len)
+    m4_warn (context, 0, me, _("argument %s truncated"),
+            quotearg_style_mem (locale_quoting_style, str, len));
+  return str;
+}
+
+/* Parse STR of length LEN as a double, reporting warnings on behalf
+   of ME.  */
 static double
-arg_double (struct m4 *context, const m4_call_info *me, const char *str)
+arg_double (struct m4 *context, const m4_call_info *me, const char *str,
+           size_t len)
 {
   char *endp;
   double value;

-  if (*str == '\0')
+  if (!len)
     {
       m4_warn (context, 0, me, _("empty string treated as 0"));
       return 0.0;
     }
   errno = 0;
   value = strtod (str, &endp);
-  if (*endp != '\0')
-    m4_warn (context, 0, me, _("non-numeric argument `%s'"), str);
+  if (endp - str != len)
+    m4_warn (context, 0, me, _("non-numeric argument %s"),
+            quotearg_style_mem (locale_quoting_style, str, len));
   else if (isspace (to_uchar (*str)))
     m4_warn (context, 0, me, _("leading whitespace ignored"));
   else if (errno == ERANGE)
@@ -100,16 +121,16 @@ arg_double (struct m4 *context, const m4_call_info *me, 
const char *str)
 }

 #define ARG_INT(i, argc, argv)                                 \
-  ((argc <= ++i) ? 0 : arg_int (context, me, M4ARG (i)))
+  ((argc <= ++i) ? 0 : arg_int (context, me, M4ARG (i), M4ARGLEN (i)))

 #define ARG_LONG(i, argc, argv)                                        \
-  ((argc <= ++i) ? 0L : arg_long (context, me, M4ARG (i)))
+  ((argc <= ++i) ? 0L : arg_long (context, me, M4ARG (i), M4ARGLEN (i)))

 #define ARG_STR(i, argc, argv)                                 \
-  ((argc <= ++i) ? "" : M4ARG (i))
+  ((argc <= ++i) ? "" : arg_string (context, me, M4ARG (i), M4ARGLEN (i)))

 #define ARG_DOUBLE(i, argc, argv)                              \
-  ((argc <= ++i) ? 0.0 : arg_double (context, me, M4ARG (i)))
+  ((argc <= ++i) ? 0.0 : arg_double (context, me, M4ARG (i), M4ARGLEN (i)))
 

 /* The main formatting function.  Output is placed on the obstack OBS,
diff --git a/modules/gnu.c b/modules/gnu.c
index 69d938f..f44f0ba 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -625,11 +625,14 @@ M4BUILTIN_HANDLER (debuglen)
  **/
 M4BUILTIN_HANDLER (debugmode)
 {
+  const char* mode = M4ARG (1);
+  size_t len = M4ARGLEN (1);
   if (argc == 1)
     m4_set_debug_level_opt (context, 0);
-  else if (m4_debug_decode (context, M4ARG (1)) < 0)
-    m4_error (context, 0, 0, m4_arg_info (argv),
-             _("bad debug flags: `%s'"), M4ARG (1));
+  else if (m4_debug_decode (context, mode, len) < 0)
+    m4_warn (context, 0, m4_arg_info (argv),
+            _("bad debug flags: %s"),
+            quotearg_style_mem (locale_quoting_style, mode, len));
 }


@@ -997,6 +1000,7 @@ M4BUILTIN_HANDLER (m4symbols)
 M4BUILTIN_HANDLER (syncoutput)
 {
   bool value = m4_get_syncoutput_opt (context);
-  value = m4_parse_truth_arg (context, m4_arg_info (argv), M4ARG (1), value);
+  value = m4_parse_truth_arg (context, m4_arg_info (argv), M4ARG (1),
+                             M4ARGLEN (1), value);
   m4_set_syncoutput_opt (context, value);
 }
diff --git a/modules/m4.c b/modules/m4.c
index 8ce419d..1ad1ce1 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -395,8 +395,10 @@ M4BUILTIN_HANDLER (defn)
        m4_push_builtin (context, obs, m4_get_symbol_value (symbol));
       else if (m4_is_symbol_placeholder (symbol))
        m4_warn (context, 0, me,
-                _("%s: builtin `%s' requested by frozen file not found"),
-                M4ARG (i), m4_get_symbol_placeholder (symbol));
+                _("%s: builtin %s requested by frozen file not found"),
+                quotearg_n_mem (2, M4ARG (i), M4ARGLEN (i)),
+                quotearg_style (locale_quoting_style,
+                                m4_get_symbol_placeholder (symbol)));
       else
        {
          assert (!"Bad token data type in m4_defn");
@@ -947,7 +949,7 @@ M4BUILTIN_HANDLER (index)

   if (!m4_arg_empty (argv, 3) && !m4_numeric_arg (context, m4_arg_info (argv),
                                                  M4ARG (3), M4ARGLEN (3),
-                                                  &offset))
+                                                 &offset))
     return;
   if (offset < 0)
     {
diff --git a/src/freeze.c b/src/freeze.c
index 0394d4c..1a03842 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -1,6 +1,6 @@
 /* GNU m4 -- A simple macro processor
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006,
-   2007, 2008 Free Software Foundation, Inc.
+   2007, 2008, 2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -634,7 +634,7 @@ ill-formed frozen file, version 2 directive `%c' 
encountered"), 'd');
          GET_STRING (file, string[0], allocated[0], number[0], false);
          VALIDATE ('\n');

-         if (m4_debug_decode (context, string[0]) < 0)
+         if (m4_debug_decode (context, string[0], number[0]) < 0)
            m4_error (context, EXIT_FAILURE, 0, NULL,
                      _("unknown debug mode %s"),
                      quotearg_style_mem (locale_quoting_style, string[0],
@@ -696,8 +696,21 @@ ill-formed frozen file, version 2 directive `%c' 
encountered"), 'F');
            m4_module *module = NULL;
            m4_symbol_value *token;

+           // Builtins cannot contain a NUL byte.
+           if (strlen (string[1]) < number[1])
+             m4_error (context, EXIT_FAILURE, 0, NULL, _("\
+ill-formed frozen file, invalid builtin %s encountered"),
+                       quotearg_style_mem (locale_quoting_style, string[1],
+                                           number[1]));
            if (number[2] > 0)
-             module = m4__module_find (string[2]);
+             {
+               if (strlen (string[2]) < number[2])
+                 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
+ill-formed frozen file, invalid module %s encountered"),
+                           quotearg_style_mem (locale_quoting_style,
+                                               string[2], number[2]));
+               module = m4__module_find (string[2]);
+             }
            token = m4_builtin_find_by_name (module, string[1]);

            if (token == NULL)
@@ -732,6 +745,11 @@ ill-formed frozen file, version 2 directive `%c' 
encountered"), 'M');
          GET_STRING (file, string[0], allocated[0], number[0], false);
          VALIDATE ('\n');

+         if (strlen (string[0]) < number[0])
+           m4_error (context, EXIT_FAILURE, 0, NULL, _("\
+ill-formed frozen file, invalid module %s encountered"),
+                     quotearg_style_mem (locale_quoting_style,
+                                         string[0], number[0]));
          m4__module_open (context, string[0], NULL);

          break;
@@ -940,7 +958,14 @@ ill-formed frozen file, version 2 directive `%c' 
encountered"), 'T');

            token = (m4_symbol_value *) xzalloc (sizeof *token);
            if (number[2] > 0)
-             module = m4__module_find (string[2]);
+             {
+               if (strlen (string[2]) < number[2])
+                 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
+ill-formed frozen file, invalid module %s encountered"),
+                           quotearg_style_mem (locale_quoting_style,
+                                               string[2], number[2]));
+               module = m4__module_find (string[2]);
+             }

            m4_set_symbol_value_text (token, xmemdup0 (string[1], number[1]),
                                      number[1], 0);
diff --git a/src/main.c b/src/main.c
index b4d8da8..c2e19bd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -459,7 +459,7 @@ main (int argc, char *const *argv, char *const *envp)
          break;

        case 'E':
-         m4_debug_decode (context, "-d");
+         m4_debug_decode (context, "-d", SIZE_MAX);
          if (m4_get_fatal_warnings_opt (context))
            m4_set_warnings_exit_opt (context, true);
          else
@@ -491,12 +491,13 @@ main (int argc, char *const *argv, char *const *envp)
              const char *dlerr = lt_dlerror ();
              if (dlerr == NULL)
                m4_error (context, EXIT_FAILURE, 0, NULL,
-                         _("failed to add search directory `%s'"),
-                         optarg);
+                         _("failed to add search directory %s"),
+                         quotearg_style (locale_quoting_style, optarg));
              else
                m4_error (context, EXIT_FAILURE, 0, NULL,
-                         _("failed to add search directory `%s': %s"),
-                         optarg, dlerr);
+                         _("failed to add search directory %s: %s"),
+                         quotearg_style (locale_quoting_style, optarg),
+                         dlerr);
            }
          break;

@@ -533,8 +534,9 @@ main (int argc, char *const *argv, char *const *envp)
             have effect between files.  */
          if (seen_file || frozen_file_to_read)
            goto defer;
-         if (m4_debug_decode (context, optarg) < 0)
-           error (0, 0, _("bad debug flags: `%s'"), optarg);
+         if (m4_debug_decode (context, optarg, SIZE_MAX) < 0)
+           error (0, 0, _("bad debug flags: %s"),
+                  quotearg_style (locale_quoting_style, optarg));
          break;

        case 'e':
@@ -689,8 +691,9 @@ main (int argc, char *const *argv, char *const *envp)
          break;

        case 'd':
-         if (m4_debug_decode (context, arg) < 0)
-           error (0, 0, _("bad debug flags: `%s'"), arg);
+         if (m4_debug_decode (context, arg, SIZE_MAX) < 0)
+           error (0, 0, _("bad debug flags: %s"),
+                  quotearg_style (locale_quoting_style, arg));
          break;

        case 'm':
@@ -702,7 +705,8 @@ main (int argc, char *const *argv, char *const *envp)
          m4_set_regexp_syntax_opt (context, m4_regexp_syntax_encode (arg));
          if (m4_get_regexp_syntax_opt (context) < 0)
            m4_error (context, EXIT_FAILURE, 0, NULL,
-                     _("bad regexp syntax option: `%s'"), arg);
+                     _("bad syntax-spec: %s"),
+                     quotearg_style (locale_quoting_style, arg));
          break;

        case 't':
@@ -736,7 +740,7 @@ main (int argc, char *const *argv, char *const *envp)
            info.name_len = strlen (info.name);
            m4_set_syncoutput_opt (context,
                                   m4_parse_truth_arg (context, &info, arg,
-                                                      previous));
+                                                      SIZE_MAX, previous));
          }
          break;

diff --git a/tests/freeze.at b/tests/freeze.at
index 693ae54..338f6f5 100644
--- a/tests/freeze.at
+++ b/tests/freeze.at
@@ -1,5 +1,5 @@
 # Hand crafted tests for GNU M4.                               -*- Autotest -*-
-# Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.

 # This file is part of GNU M4.
 #
@@ -415,6 +415,18 @@ AT_CHECK_M4([-R bogus.m4f], [1], [],
 [[m4:bogus.m4f:4: bad syntax-spec `gnu\0'
 ]])

+dnl Reject escape sequences that expand to unexpected NUL
+AT_DATA([bogus.m4f],
+[[# bogus frozen file
+V2
+F3,4
+len
+len\0
+]])
+AT_CHECK_M4([-R bogus.m4f], [1], [],
+[[m4:bogus.m4f:5: ill-formed frozen file, invalid builtin `len\0' encountered
+]])
+
 AT_CLEANUP
 ])

diff --git a/tests/null.err b/tests/null.err
index 01f2a8e..38c610f 100644
--- a/tests/null.err
+++ b/tests/null.err
@@ -12,14 +12,16 @@ m4:null.m4:52: Warning: debugfile: argument 
`/no/such\0/file' truncated
 m4:null.m4:52: Warning: debugfile: cannot set debug file `/no/such': No such 
file or directory
 debuglen:
 m4:null.m4:55: Warning: debuglen: non-numeric argument `1\0001'
+debugmode:
+m4:null.m4:58: Warning: debugmode: bad debug flags: `-\0-'
 decr:
-m4:null.m4:59: Warning: decr: non-numeric argument `1\0001'
+m4:null.m4:61: Warning: decr: non-numeric argument `1\0001'
 defn:
-m4:null.m4:64: Warning: defn: undefined macro `\0-\0'
+m4:null.m4:66: Warning: defn: undefined macro `\0-\0'
 divert:
-m4:null.m4:71: Warning: divert: non-numeric argument `1\0001'
+m4:null.m4:73: Warning: divert: non-numeric argument `1\0001'
 dumpdef:
-m4:null.m4:79: Warning: dumpdef: undefined macro `\0-\0'
+m4:null.m4:81: Warning: dumpdef: undefined macro `\0-\0'
 :      `empty'
 -:     `dash'
 --:   ``$0': $1'
@@ -28,52 +30,60 @@ m4:null.m4:79: Warning: dumpdef: undefined macro `\0-\0'
 body:  `--'
 errprint: -- --
 esyscmd:
-m4:null.m4:89: Warning: esyscmd: argument `echo +\0+' truncated
+m4:null.m4:91: Warning: esyscmd: argument `echo +\0+' truncated
 eval:
-m4:null.m4:94: Warning: eval: bad input: `1\0+1'
-m4:null.m4:96: Warning: eval: non-numeric argument `2\0002'
-m4:null.m4:98: Warning: eval: non-numeric argument `1\0001'
+m4:null.m4:96: Warning: eval: bad input: `1\0+1'
+m4:null.m4:98: Warning: eval: non-numeric argument `2\0002'
+m4:null.m4:100: Warning: eval: non-numeric argument `1\0001'
 format:
-m4:null.m4:103: Warning: format: unrecognized specifier in `%\0%'
-m4:null.m4:103: Warning: format: unrecognized specifier in `%\0%'
+m4:null.m4:105: Warning: format: unrecognized specifier in `%\0%'
+m4:null.m4:105: Warning: format: unrecognized specifier in `%\0%'
+m4:null.m4:107: Warning: format: non-numeric argument `1\0001'
+m4:null.m4:107: Warning: format: argument `-\0-' truncated
 include:
-m4:null.m4:113: Warning: include: argument `/no/such\0/file' truncated
-m4:null.m4:113: include: cannot open `/no/such': No such file or directory
+m4:null.m4:116: Warning: include: argument `/no/such\0/file' truncated
+m4:null.m4:116: include: cannot open `/no/such': No such file or directory
 incr:
-m4:null.m4:116: Warning: incr: non-numeric argument `1\0001'
+m4:null.m4:119: Warning: incr: non-numeric argument `1\0001'
 index:
-m4:null.m4:122: Warning: index: non-numeric argument `1\0001'
+m4:null.m4:125: Warning: index: non-numeric argument `1\0001'
 indir:
-m4:null.m4:127: Warning: indir: undefined macro `\0-\0'
-m4:null.m4:129: Warning: \0\0%%: extra arguments ignored: 1 > 0
+m4:null.m4:130: Warning: indir: undefined macro `\0-\0'
+m4:null.m4:132: Warning: \0\0%%: extra arguments ignored: 1 > 0
 maketemp:
-m4:null.m4:144: Warning: maketemp: recommend using mkstemp instead
-m4:null.m4:144: Warning: maketemp: argument `/no/such\0/file' truncated
-m4:null.m4:144: Warning: maketemp: cannot create file from template 
`/no/such': No such file or directory
+m4:null.m4:147: Warning: maketemp: recommend using mkstemp instead
+m4:null.m4:147: Warning: maketemp: argument `/no/such\0/file' truncated
+m4:null.m4:147: Warning: maketemp: cannot create file from template 
`/no/such': No such file or directory
 mkdtemp:
-m4:null.m4:147: Warning: mkdtemp: argument `/no/such\0/file' truncated
-m4:null.m4:147: Warning: mkdtemp: cannot create directory from template 
`/no/such': No such file or directory
+m4:null.m4:150: Warning: mkdtemp: argument `/no/such\0/file' truncated
+m4:null.m4:150: Warning: mkdtemp: cannot create directory from template 
`/no/such': No such file or directory
 mkstemp:
-m4:null.m4:150: Warning: mkstemp: argument `/no/such\0/file' truncated
-m4:null.m4:150: Warning: mkstemp: cannot create file from template `/no/such': 
No such file or directory
+m4:null.m4:153: Warning: mkstemp: argument `/no/such\0/file' truncated
+m4:null.m4:153: Warning: mkstemp: cannot create file from template `/no/such': 
No such file or directory
 patsubst:
-m4:null.m4:153: Warning: patsubst: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:163: Warning: patsubst: bad syntax-spec: `\0'
+m4:null.m4:156: Warning: patsubst: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:166: Warning: patsubst: bad syntax-spec: `\0'
+popdef:
+m4:null.m4:173: Warning: popdef: undefined macro `-\0-'
 regexp:
-m4:null.m4:175: Warning: regexp: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:185: Warning: regexp: bad syntax-spec: `\0'
+m4:null.m4:180: Warning: regexp: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:190: Warning: regexp: bad syntax-spec: `\0'
 renamesyms:
-m4:null.m4:190: Warning: renamesyms: bad regular expression `\\\0\\': Trailing 
backslash
-m4:null.m4:196: Warning: renamesyms: bad syntax-spec: `\0'
+m4:null.m4:195: Warning: renamesyms: bad regular expression `\\\0\\': Trailing 
backslash
+m4:null.m4:201: Warning: renamesyms: bad syntax-spec: `\0'
 sinclude:
-m4:null.m4:201: Warning: sinclude: argument `/no/such\0/file' truncated
+m4:null.m4:206: Warning: sinclude: argument `/no/such\0/file' truncated
 substr:
-m4:null.m4:206: Warning: substr: non-numeric argument `1\0001'
-m4:null.m4:208: Warning: substr: non-numeric argument `1\0001'
+m4:null.m4:211: Warning: substr: non-numeric argument `1\0001'
+m4:null.m4:213: Warning: substr: non-numeric argument `1\0001'
+syncoutput:
+m4:null.m4:218: Warning: syncoutput: unknown directive `\0'
 syscmd:
-m4:null.m4:214: Warning: syscmd: argument `echo +\0+' truncated
+m4:null.m4:221: Warning: syscmd: argument `echo +\0+' truncated
 traceon:
 m4trace: -1- --(`--') -> `strange: --'
 m4trace: -1- body -> `-'
+undefine:
+m4:null.m4:240: Warning: undefine: undefined macro `-\0-'
 undivert:
-m4:null.m4:234: Warning: undivert: invalid file name `1\0001'
+m4:null.m4:243: Warning: undivert: invalid file name `1\0001'
diff --git a/tests/null.m4 b/tests/null.m4
index 6071144..0e13cb4 100644
--- a/tests/null.m4
+++ b/tests/null.m4
@@ -53,7 +53,9 @@ errprint(`debugfile:
 dnl Warning from debuglen:
 errprint(`debuglen:
 ')debuglen(`11')dnl
-dnl Warning from debugmode: not tested yet. NUL not a valid mode, needs to warn
+dnl Warning from debugmode:
+errprint(`debugmode:
+')debugmode(`--')dnl
 dnl Warning from decr:
 errprint(`decr:
 ')decr(`11')dnl
@@ -100,8 +102,9 @@ dnl First argument to format:
 `format:' format(`%s%s', `-', `-')dnl
 dnl Invalid specifier in format:
 errprint(`format:
-') format(`%%')
-dnl Numeric and string arguments to format: not tested yet, needs to warn
+') format(`%%')dnl
+dnl Numeric and string arguments to format:
+ format(`%d%s', `11', `--')
 dnl Character argument to format: not tested yet, %c semantics needed
 dnl Macro name in ifdef, passed through ifdef:
 `ifdef:' ifdef(`--', `yes: --', `oops: --')dnl
@@ -165,7 +168,9 @@ dnl Replacement via reference in patsubst:
  patsubst(`----', `-\(.\)-', `\1-\1')
 dnl Defined argument of popdef:
 `popdef:' popdef(`--')ifdef(`--', `oops', `ok')
-dnl Undefined argument of popdef: not tested yet. Should it warn?
+dnl Undefined argument of popdef:
+errprint(`popdef:
+')popdef(`--')dnl
 dnl Macro name of pushdef:
 `pushdef:' pushdef(`--', `strange: $1')ifdef(`--', `ok', `oops')`'dnl
 dnl Definition of pushdef:
@@ -208,7 +213,9 @@ dnl Third argument of substr:
 substr(`abc', `1', `11')dnl
 dnl Fourth argument of substr:
  substr(`abc', `1', `1', `11')
-dnl Warning from syncoutput: not tested yet. No mode contains NUL, needs to 
warn
+dnl Warning from syncoutput:
+errprint(`syncoutput:
+')syncoutput(`')dnl
 dnl Passed to syscmd:
 `syscmd:'errprint(`syscmd:
 ') syscmd(`echo ++')sysval
@@ -228,7 +235,9 @@ dnl Character ranges of translit:
  translit(`abcd', `-b')
 dnl Defined argument of undefine:
 `undefine:' undefine(`--')ifdef(`--', `oops', `ok')
-dnl Undefined argument of undefine: not tested yet. Should it warn?
+dnl Undefined argument of undefine:
+errprint(`undefine:
+')undefine(`--')dnl
 dnl Warning from undivert:
 errprint(`undivert:
 ')undivert(`11')dnl
diff --git a/tests/null.out b/tests/null.out
index 3859b23..993d9fe 100644
--- a/tests/null.out
+++ b/tests/null.out
@@ -12,7 +12,7 @@ defn: `$0': $1 --
 divert: --
 esyscmd: +
 0 [] 0
-format: -- 
+format: --  1-
 ifdef: yes: -- no: --
 ifelse: yes: --
 index: 2 -1 -1 8
diff --git a/tests/options.at b/tests/options.at
index 48e5b03..ad71f20 100644
--- a/tests/options.at
+++ b/tests/options.at
@@ -1,5 +1,6 @@
 # Hand crafted tests for GNU M4.                               -*- Autotest -*-
-# Copyright (C) 2001, 2006, 2007, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.

 # This file is part of GNU M4.
 #
@@ -722,7 +723,7 @@ AT_DATA([[in]], [[regexp(`(', `(')
 ]])

 AT_CHECK_M4([--regexp-syntax=unknown in], [1], [],
-[[m4: bad regexp syntax option: `unknown'
+[[m4: bad syntax-spec: `unknown'
 ]])

 AT_CHECK_M4([--regexp-syntax= in], [0], [[0
-- 
1.6.1.2

From 1b5e6019b86abfc086af32b79087b8e862ed7a81 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 5 Feb 2008 11:41:05 -0700
Subject: [PATCH] Stage 28: Allow embedded NUL in warning messages.

* src/m4.h (debug_decode): Add argument.
* src/m4.c (main): Adjust caller.
* src/format.c (arg_int, arg_long, arg_double): Likewise.
(arg_string): New function.
(ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE): Support embedded NUL.
* src/freeze.c (reload_frozen_state): Likewise.
* src/debug.c (debug_decode): Likewise.
* src/builtin.c (numeric_arg): Add argument.
(m4_syscmd, m4_esyscmd, m4_eval, m4_incr, m4_decr, m4_divert)
(m4_undivert, include, mkstemp_helper, m4_m4exit, m4_debugmode)
(m4_debugfile, m4_index, m4_substr): Detect embedded NUL.
(m4_defn, m4_maketemp, m4_placeholder): Improve warning message.
* src/path.c (m4_path_search): Likewise.
* doc/m4.texinfo (Syntax, Mkstemp, Using frozen files): Adjust
tests.
* examples/null.m4: Likewise.
* examples/null.out: Likewise.
* examples/null.err: Likewise.

Signed-off-by: Eric Blake <address@hidden>
(cherry picked from commit fd9f6463352aee342c4061b5df9d0f4bf56742c7)
---
 ChangeLog         |   28 ++++++++++++
 doc/m4.texinfo    |   20 ++++++++-
 examples/null.err |   60 +++++++++++++++++++++++----
 examples/null.m4  |   87 ++++++++++++++++++++++++++++----------
 examples/null.out |    9 +++-
 src/builtin.c     |  121 ++++++++++++++++++++++++++++++++++++-----------------
 src/debug.c       |   30 ++++++++-----
 src/format.c      |   55 ++++++++++++++++--------
 src/freeze.c      |   13 ++++--
 src/m4.c          |   14 ++++---
 src/m4.h          |    2 +-
 src/path.c        |   10 +++-
 12 files changed, 333 insertions(+), 116 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2aa09b4..39d40df 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,33 @@
 2009-02-11  Eric Blake  <address@hidden>

+       Stage 28: Allow embedded NUL in warning messages.
+       Issue graceful error messages for a variety of places where
+       embedded NUL is otherwise unhandled.  For example, numeric parsing
+       rejects embedded NUL, while file-related commands warn about
+       truncation at NUL, and frozen files detect unexpected NUL.
+       Memory impact: none.
+       Speed impact: none noticed.
+       * src/m4.h (debug_decode): Add argument.
+       * src/m4.c (main): Adjust caller.
+       * src/format.c (arg_int, arg_long, arg_double): Likewise.
+       (arg_string): New function.
+       (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE): Support embedded NUL.
+       * src/freeze.c (reload_frozen_state): Likewise.
+       * src/debug.c (debug_decode): Likewise.
+       * src/builtin.c (numeric_arg): Add argument.
+       (m4_syscmd, m4_esyscmd, m4_eval, m4_incr, m4_decr, m4_divert)
+       (m4_undivert, include, mkstemp_helper, m4_m4exit, m4_debugmode)
+       (m4_debugfile, m4_index, m4_substr): Detect embedded NUL.
+       (m4_defn, m4_maketemp, m4_placeholder): Improve warning message.
+       * src/path.c (m4_path_search): Likewise.
+       * doc/m4.texinfo (Syntax, Mkstemp, Using frozen files): Adjust
+       tests.
+       * examples/null.m4: Likewise.
+       * examples/null.out: Likewise.
+       * examples/null.err: Likewise.
+
+2009-02-11  Eric Blake  <address@hidden>
+
        Warn when popping traced but undefined macro.
        * src/symtab.c (lookup_symbol): Recognize traced placeholder when
        delete is requested.  Bug introduced 2008-07-18.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index b337e5e..fd48e80 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -1081,14 +1081,16 @@ Syntax

 @comment xout: null.out
 @comment xerr: null.err
address@hidden status: 1
 @example
 define(`m4exit')include(`null.m4')dnl
 @end example

address@hidden status: 2
address@hidden status: 1
 @example
 include(`null.m4')
 @result{}# This file tests m4 behavior on NUL bytes.
address@hidden:examples/null.m4:5: Warning: m4exit: non-numeric argument 
`2\0002'
 @end example
 @end ignore

@@ -7485,10 +7487,12 @@ Mkstemp
 syscmd(`rm -f foo-??????')sysval
 @result{}0
 define(`file1', maketemp(`foo-XXXXXX'))dnl
address@hidden:stdin:3: Warning: maketemp: recommend using mkstemp instead
 ifelse(esyscmd(`echo \` foo-?????? \''), ` foo-?????? ',
        `no file', `created')
 @result{}created
 define(`file2', maketemp(`foo-XX'))dnl
address@hidden:stdin:6: Warning: maketemp: recommend using mkstemp instead
 define(`file3', mkstemp(`foo-XXXXXX'))dnl
 ifelse(len(defn(`file1')), len(defn(`file2')),
        `same length', `different')
@@ -7905,6 +7909,20 @@ Using frozen files
 ])dnl
 @error{}divnum #-- 3 0
 @end example
+
address@hidden Do we reject unexpected NUL bytes?
+
address@hidden
+ifdef(`__unix__', ,
+      `errprint(` skipping: syscmd does not have unix semantics
+')m4exit(`77')')dnl
+changequote(`[', `]')dnl
+syscmd([printf '#bogus\nV1\nF3,4\nlenlen\0\n' > in.m4f \
+     && ]__program__[ -R in.m4f \
+     && rm in.m4f])sysval
address@hidden:in.m4f:4: ill-formed frozen file, invalid builtin `len\0' 
encountered
address@hidden
address@hidden example
 @end ignore

 When an @code{m4} run is to be frozen, the automatic undiversion
diff --git a/examples/null.err b/examples/null.err
index 977b3b7..8446d78 100644
--- a/examples/null.err
+++ b/examples/null.err
@@ -5,10 +5,19 @@ echo: address@hidden/
 m4trace: -1- dumpdef(echo/) -> /
 changeword:
 m4:examples/null.m4:43: Warning: changeword: bad regular expression `\\\0\\': 
Trailing backslash
+debugfile:
+m4:examples/null.m4:49: Warning: debugfile: argument `/no/such\0/file' 
truncated
+m4:examples/null.m4:49: Warning: debugfile: cannot set debug file `/no/such': 
No such file or directory
+debugmode:
+m4:examples/null.m4:52: Warning: debugmode: bad debug flags: `-\0-'
+decr:
+m4:examples/null.m4:55: Warning: decr: non-numeric argument `1\0001'
 defn:
-m4:examples/null.m4:54: Warning: defn: undefined macro `\0-\0'
+m4:examples/null.m4:60: Warning: defn: undefined macro `\0-\0'
+divert:
+m4:examples/null.m4:67: Warning: divert: non-numeric argument `1\0001'
 dumpdef:
-m4:examples/null.m4:67: Warning: dumpdef: undefined macro `\0-\0'
+m4:examples/null.m4:75: Warning: dumpdef: undefined macro `\0-\0'
 :      `empty'
 -:     `dash'
 --:   ``$0': $1'
@@ -16,16 +25,51 @@ m4:examples/null.m4:67: Warning: dumpdef: undefined macro 
`\0-\0'
 --:    `dashes'
 body:  `--'
 errprint: -- --
+esyscmd:
+m4:examples/null.m4:85: Warning: esyscmd: argument `echo +\0+' truncated
+eval:
+m4:examples/null.m4:90: Warning: eval: bad input: `1\0+1'
+m4:examples/null.m4:92: Warning: eval: non-numeric argument `2\0002'
+m4:examples/null.m4:94: Warning: eval: non-numeric argument `1\0001'
 format:
-m4:examples/null.m4:84: Warning: format: unrecognized specifier in `%\0%'
-m4:examples/null.m4:84: Warning: format: unrecognized specifier in `%\0%'
+m4:examples/null.m4:99: Warning: format: unrecognized specifier in `%\0%'
+m4:examples/null.m4:99: Warning: format: unrecognized specifier in `%\0%'
+m4:examples/null.m4:101: Warning: format: non-numeric argument `1\0001'
+m4:examples/null.m4:101: Warning: format: argument `-\0-' truncated
+include:
+m4:examples/null.m4:110: Warning: include: argument `/no/such\0/file' truncated
+m4:examples/null.m4:110: include: cannot open `/no/such': No such file or 
directory
+incr:
+m4:examples/null.m4:113: Warning: incr: non-numeric argument `1\0001'
+index:
+m4:examples/null.m4:119: Warning: index: non-numeric argument `1\0001'
 indir:
-m4:examples/null.m4:101: Warning: indir: undefined macro `\0-\0'
-m4:examples/null.m4:103: Warning: \0\0%%: extra arguments ignored: 1 > 0
+m4:examples/null.m4:124: Warning: indir: undefined macro `\0-\0'
+m4:examples/null.m4:126: Warning: \0\0%%: extra arguments ignored: 1 > 0
+maketemp:
+m4:examples/null.m4:137: Warning: maketemp: recommend using mkstemp instead
+m4:examples/null.m4:137: Warning: maketemp: argument `/no/such\0/file' 
truncated
+m4:examples/null.m4:137: Warning: maketemp: cannot create file from template 
`/no/such': No such file or directory
+mkstemp:
+m4:examples/null.m4:140: Warning: mkstemp: argument `/no/such\0/file' truncated
+m4:examples/null.m4:140: Warning: mkstemp: cannot create file from template 
`/no/such': No such file or directory
 patsubst:
-m4:examples/null.m4:116: Warning: patsubst: bad regular expression `\\\0\\': 
Trailing backslash
+m4:examples/null.m4:143: Warning: patsubst: bad regular expression `\\\0\\': 
Trailing backslash
+popdef:
+m4:examples/null.m4:158: Warning: popdef: undefined macro `-\0-'
 regexp:
-m4:examples/null.m4:136: Warning: regexp: bad regular expression `\\\0\\': 
Trailing backslash
+m4:examples/null.m4:165: Warning: regexp: bad regular expression `\\\0\\': 
Trailing backslash
+sinclude:
+m4:examples/null.m4:180: Warning: sinclude: argument `/no/such\0/file' 
truncated
+substr:
+m4:examples/null.m4:185: Warning: substr: non-numeric argument `1\0001'
+m4:examples/null.m4:187: Warning: substr: non-numeric argument `1\0001'
+syscmd:
+m4:examples/null.m4:192: Warning: syscmd: argument `echo +\0+' truncated
 traceon:
 m4trace: -1- --(`--') -> `strange: --'
 m4trace: -1- body -> `-'
+undefine:
+m4:examples/null.m4:208: Warning: undefine: undefined macro `-\0-'
+undivert:
+m4:examples/null.m4:214: Warning: undivert: invalid file name `1\0001'
diff --git a/examples/null.m4 b/examples/null.m4
index e60aec5..3f901f4 100644
--- a/examples/null.m4
+++ b/examples/null.m4
@@ -1,7 +1,7 @@
 dnl Use `m4 -DNUL to print a NUL byte surrounded in [], then exit
 ifdef(`NUL', `[]m4exit`'')dnl
 # This file tests m4 behavior on NUL bytes.
-dnl Use `m4 -Dm4exit' to test rest of file.  NUL not a number, needs to warn
+dnl Use `m4 -Dm4exit' to test rest of file.
 m4exit(`22')dnl
 dnl Raw pass-through:
 raw: --
@@ -44,9 +44,15 @@ errprint(`changeword:
 ifdef(`c', `errprint(__program__:__file__:decr(__line__): Warning: dnl
 `changeword: bad regular expression `\\\0\\': Trailing backslash
 ')')dnl
-dnl Warning from debugfile: not tested yet. No file name includes NUL, needs 
to warn
-dnl Warning from debugmode: not tested yet. NUL not a valid mode, needs to warn
-dnl Warning from decr: not tested yet. NUL not a number, needs to warn
+dnl Warning from debugfile:
+errprint(`debugfile:
+')debugfile(`/no/such/file')dnl
+dnl Warning from debugmode:
+errprint(`debugmode:
+')debugmode(`--')dnl
+dnl Warning from decr:
+errprint(`decr:
+')decr(`11')dnl
 dnl Definition of define:
 `define:' define(`body', `--')body
 dnl Undefined argument of defn:
@@ -56,7 +62,9 @@ dnl Defined macro name in defn:
 `defn:' defn(`--')dnl
 dnl Macro contents in defn:
  defn(`body')
-dnl Argument to divert: not tested yet. NUL not a number, needs to warn
+dnl Argument to divert:
+errprint(`divert:
+')divert(`11')dnl
 dnl Passed through diversion by divert:
 divert(`1')`divert:' --
 divert`'undivert(`1')dnl
@@ -72,28 +80,43 @@ dumpdef(`body')dnl
 dnl Passed through errprint:
 errprint(`errprint:' --, `--
 ')dnl
-dnl Passed to esyscmd: not tested yet. NUL truncates string, needs to warn
+dnl Passed to esyscmd:
+`esyscmd:'errprint(`esyscmd:
+') esyscmd(`echo ++')sysval dnl
 dnl Generated from esyscmd:
-`esyscmd:' esyscmd(__program__` -DNUL '__file__) sysval
-dnl First argument of eval: not tested yet. NUL not a number, needs to warn
-dnl Other arguments of eval: not tested yet, needs to warn
+esyscmd(__program__` -DNUL '__file__) sysval
+dnl First argument of eval:
+errprint(`eval:
+')eval(`1+1')dnl
+dnl Second argument of eval:
+eval(`1', `22')dnl
+dnl Third argument of eval:
+eval(`1', `10', `11')dnl
 dnl First argument to format:
 `format:' format(`%s%s', `-', `-')dnl
 dnl Invalid specifier in format:
 errprint(`format:
-') format(`%%')
-dnl Numeric and string arguments to format: not tested yet, needs to warn
+') format(`%%')dnl
+dnl Numeric and string arguments to format:
+ format(`%d%s', `11', `--')
 dnl Character argument to format: not tested yet, %c semantics needed
 dnl Macro name in ifdef, passed through ifdef:
 `ifdef:' ifdef(`--', `yes: --', `oops: --')dnl
  ifdef(, `oops: --', `no: --')
 dnl Compared in ifelse, passed through ifelse:
 `ifelse:' ifelse(`-', `--', `oops', `--', --, `yes: --')
-dnl Warning from include: not tested yet. No file name includes NUL, needs to 
warn
-dnl Warning from incr: not tested yet. NUL not a number, needs to warn
+dnl Warning from include:
+errprint(`include:
+')include(`/no/such/file')dnl
+dnl Warning from incr:
+errprint(`incr:
+')incr(`11')dnl
 dnl Passed through index:
 `index:' index(`ab', `b') index(`-', `') index(`', `-')dnl
  index(`-', `-')
+dnl Third argument of index:
+errprint(`index:
+')index(`aba', `a', `11')dnl
 dnl Defined first argument of indir:
 `indir:' indir(`--', 11)dnl
 dnl Undefined first argument of indir:
@@ -109,8 +132,12 @@ dnl Test m4exit separately from m4wrap; see above.
 dnl Passed through m4wrap:
 m4wrap(``m4wrap:' --
 ')dnl
-dnl Warning from maketemp: not tested yet. No file name includes NUL, needs to 
warn
-dnl Warning from mkstemp: not tested yet. No file name includes NUL, needs to 
warn
+dnl Warning from maketemp:
+errprint(`maketemp:
+')maketemp(`/no/such/file')dnl
+dnl Warning from mkstemp:
+errprint(`mkstemp:
+')mkstemp(`/no/such/file')dnl
 dnl Bad regex in patsubst:
 errprint(`patsubst:
 ')patsubst(`a', `\\')dnl
@@ -126,7 +153,9 @@ dnl Replacement via reference in patsubst:
  patsubst(`----', `-\(.\)-', `\1-\1')
 dnl Defined argument of popdef:
 `popdef:' popdef(`--')ifdef(`--', `oops', `ok')
-dnl Undefined argument of popdef: not tested yet. Should it warn?
+dnl Undefined argument of popdef:
+errprint(`popdef:
+')popdef(`--')dnl
 dnl Macro name of pushdef:
 `pushdef:' pushdef(`--', `strange: $1')ifdef(`--', `ok', `oops')`'dnl
 dnl Definition of pushdef:
@@ -146,11 +175,21 @@ dnl Replacement via reference in regexp:
  regexp(`----', `-\(.\)-', `\1-\1')
 dnl Passed through shift:
 `shift:' shift(`hi', `--', --)
-dnl Warning from sinclude: not tested yet. No file name includes NUL, needs to 
warn
+dnl Warning from sinclude:
+errprint(`sinclude:
+')sinclude(`/no/such/file')dnl
 dnl First argument of substr:
-`substr:' substr(`----', `1', `3')
-dnl Other arguments of substr: not tested yet. NUL not a number, needs to warn.
-dnl Passed to syscmd: not tested yet. NUL truncates string, needs to warn
+`substr:' substr(`----', `1', `3')dnl
+dnl Second argument of substr:
+errprint(`substr:
+')substr(`abc', `11')dnl
+dnl Third argument of substr:
+substr(`abc', `1', `11')dnl
+dnl Fourth argument of substr:
+ substr(`abc', `1', `1', `11')
+dnl Passed to syscmd:
+`syscmd:'errprint(`syscmd:
+') syscmd(`echo ++')sysval
 dnl Sysval takes no arguments, and never produces NUL.
 dnl Passed to traceoff:
 traceoff(`--', `')dnl
@@ -167,5 +206,9 @@ dnl Character ranges of translit:
  translit(`abcd', `-b')
 dnl Defined argument of undefine:
 `undefine:' undefine(`--')ifdef(`--', `oops', `ok')
-dnl Undefined argument of undefine: not tested yet. Should it warn?
-dnl Warning from undivert: not tested yet. No file name or number includes 
NUL, needs to warn
+dnl Undefined argument of undefine:
+errprint(`undefine:
+')undefine(`--')dnl
+dnl Warning from undivert:
+errprint(`undivert:
+')undivert(`11')dnl
diff --git a/examples/null.out b/examples/null.out
index c2c1cb9..812df0c 100644
--- a/examples/null.out
+++ b/examples/null.out
@@ -10,8 +10,9 @@ changeword: -- --: dash
 define: --
 defn: `$0': $1 --
 divert: --
-esyscmd: [] 0
-format: -- 
+esyscmd: +
+0 [] 0
+format: --  1-
 ifdef: yes: -- no: --
 ifelse: yes: --
 index: 2 -1 -1 8
@@ -22,7 +23,9 @@ popdef: ok
 pushdef: ok -
 regexp: 2 ! 1 -- -
 shift: --,--
-substr: --
+substr: -- a11c
+syscmd: +
+0
 traceon: strange: -- -
 translit: -- .. cd
 undefine: ok
diff --git a/src/builtin.c b/src/builtin.c
index a398fdb..22d156d 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -564,18 +564,18 @@ bad_argc (const call_info *name, int argc, unsigned int 
min, unsigned int max)
   return false;
 }

-/*-------------------------------------------------------------------.
-| The function numeric_arg () converts ARG to an int pointed to by   |
-| VALUEP.  If the conversion fails, print error message on behalf of |
-| NAME.  Return true iff conversion succeeds.                        |
-`-------------------------------------------------------------------*/
+/*------------------------------------------------------------------.
+| The function numeric_arg () converts ARG of length LEN to an int  |
+| pointed to by VALUEP.  If the conversion fails, print error       |
+| message on behalf of NAME.  Return true iff conversion succeeds.  |
+`------------------------------------------------------------------*/

 static bool
-numeric_arg (const call_info *name, const char *arg, int *valuep)
+numeric_arg (const call_info *name, const char *arg, size_t len, int *valuep)
 {
   char *endp;

-  if (*arg == '\0')
+  if (!len)
     {
       *valuep = 0;
       m4_warn (0, name, _("empty string treated as 0"));
@@ -584,9 +584,10 @@ numeric_arg (const call_info *name, const char *arg, int 
*valuep)
     {
       errno = 0;
       *valuep = strtol (arg, &endp, 10);
-      if (*endp != '\0')
+      if (endp - arg != len)
        {
-         m4_warn (0, name, _("non-numeric argument `%s'"), arg);
+         m4_warn (0, name, _("non-numeric argument %s"),
+                  quotearg_style_mem (locale_quoting_style, arg, len));
          return false;
        }
       if (isspace (to_uchar (*arg)))
@@ -1087,8 +1088,9 @@ m4_defn (struct obstack *obs, int argc, macro_arguments 
*argv)
          b = SYMBOL_FUNC (s);
          if (b == m4_placeholder)
            m4_warn (0, me,
-                    _("builtin `%s' requested by frozen file not found"),
-                    ARG (i));
+                    _("builtin %s requested by frozen file not found"),
+                    quotearg_style_mem (locale_quoting_style, ARG (i),
+                                        ARG_LEN (i)));
          else
            push_macro (obs, b);
          break;
@@ -1140,7 +1142,14 @@ static int sysval;
 static void
 m4_syscmd (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  if (bad_argc (arg_info (argv), argc, 1, 1))
+  const call_info *me = arg_info (argv);
+  const char *cmd = ARG (1);
+  size_t len = ARG_LEN (1);
+
+  if (strlen (cmd) != len)
+    m4_warn (0, me, _("argument %s truncated"),
+            quotearg_style_mem (locale_quoting_style, cmd, len));
+  if (bad_argc (me, argc, 1, 1) || !*cmd)
     {
       /* The empty command is successful.  */
       sysval = 0;
@@ -1148,7 +1157,7 @@ m4_syscmd (struct obstack *obs, int argc, macro_arguments 
*argv)
     }

   debug_flush_files ();
-  sysval = system (ARG (1));
+  sysval = system (cmd);
 #if FUNC_SYSTEM_BROKEN
   /* OS/2 has a buggy system() that returns exit status in the lowest eight
      bits, although pclose() and WEXITSTATUS are defined to return exit
@@ -1165,10 +1174,15 @@ static void
 m4_esyscmd (struct obstack *obs, int argc, macro_arguments *argv)
 {
   const call_info *me = arg_info (argv);
+  const char *cmd = ARG (1);
+  size_t len = ARG_LEN (1);
   FILE *pin;
   int ch;

-  if (bad_argc (me, argc, 1, 1))
+  if (strlen (cmd) != len)
+    m4_warn (0, me, _("argument %s truncated"),
+            quotearg_style_mem (locale_quoting_style, cmd, len));
+  if (bad_argc (me, argc, 1, 1) || !*cmd)
     {
       /* The empty command is successful.  */
       sysval = 0;
@@ -1177,10 +1191,11 @@ m4_esyscmd (struct obstack *obs, int argc, 
macro_arguments *argv)

   debug_flush_files ();
   errno = 0;
-  pin = popen (ARG (1), "r");
+  pin = popen (cmd, "r");
   if (pin == NULL)
     {
-      m4_warn (errno, me, _("cannot open pipe to command `%s'"), ARG (1));
+      m4_warn (errno, me, _("cannot open pipe to command %s"),
+              quotearg_style (locale_quoting_style, cmd));
       sysval = -1;
     }
   else
@@ -1217,7 +1232,7 @@ m4_eval (struct obstack *obs, int argc, macro_arguments 
*argv)
   if (bad_argc (me, argc, 1, 3))
     return;

-  if (!arg_empty (argv, 2) && !numeric_arg (me, ARG (2), &radix))
+  if (!arg_empty (argv, 2) && !numeric_arg (me, ARG (2), ARG_LEN (2), &radix))
     return;

   if (radix < 1 || radix > 36)
@@ -1226,7 +1241,7 @@ m4_eval (struct obstack *obs, int argc, macro_arguments 
*argv)
       return;
     }

-  if (argc >= 4 && !numeric_arg (me, ARG (3), &min))
+  if (argc >= 4 && !numeric_arg (me, ARG (3), ARG_LEN (3), &min))
     return;
   if (min < 0)
     {
@@ -1281,7 +1296,7 @@ m4_incr (struct obstack *obs, int argc, macro_arguments 
*argv)
   if (bad_argc (me, argc, 1, 1))
     return;

-  if (!numeric_arg (me, ARG (1), &value))
+  if (!numeric_arg (me, ARG (1), ARG_LEN (1), &value))
     return;

   shipout_int (obs, value + 1);
@@ -1296,7 +1311,7 @@ m4_decr (struct obstack *obs, int argc, macro_arguments 
*argv)
   if (bad_argc (me, argc, 1, 1))
     return;

-  if (!numeric_arg (me, ARG (1), &value))
+  if (!numeric_arg (me, ARG (1), ARG_LEN (1), &value))
     return;

   shipout_int (obs, value - 1);
@@ -1317,7 +1332,7 @@ m4_divert (struct obstack *obs, int argc, macro_arguments 
*argv)
   int i = 0;

   bad_argc (me, argc, 0, 2);
-  if (argc >= 2 && !numeric_arg (me, ARG (1), &i))
+  if (argc >= 2 && !numeric_arg (me, ARG (1), ARG_LEN (1), &i))
     return;

   make_diversion (i);
@@ -1357,11 +1372,16 @@ m4_undivert (struct obstack *obs, int argc, 
macro_arguments *argv)
     for (i = 1; i < argc; i++)
       {
        const char *str = ARG (i);
+       size_t len = ARG_LEN (i);
        file = strtol (str, &endp, 10);
-       if (*endp == '\0' && !isspace (to_uchar (*str)))
+       if (endp - str == len && !isspace (to_uchar (*str)))
          insert_diversion (file);
        else if (no_gnu_extensions)
-         m4_warn (0, me, _("non-numeric argument `%s'"), str);
+         m4_warn (0, me, _("non-numeric argument %s"),
+                  quotearg_style_mem (locale_quoting_style, str, len));
+       else if (strlen (str) != len)
+         m4_warn (0, me, _("invalid file name %s"),
+                  quotearg_style_mem (locale_quoting_style, str, len));
        else
          {
            fp = m4_path_search (str, NULL);
@@ -1473,16 +1493,23 @@ include (int argc, macro_arguments *argv, bool silent)
   const call_info *me = arg_info (argv);
   FILE *fp;
   char *name;
+  const char *arg;
+  size_t len;

   if (bad_argc (me, argc, 1, 1))
     return;

-  fp = m4_path_search (ARG (1), &name);
+  arg = ARG (1);
+  len = ARG_LEN (1);
+  if (strlen (arg) != len)
+    m4_warn (0, me, _("argument %s truncated"),
+            quotearg_style_mem (locale_quoting_style, arg, len));
+  fp = m4_path_search (arg, &name);
   if (fp == NULL)
     {
       if (!silent)
        m4_error (0, errno, me, _("cannot open %s"),
-                 quotearg_style (locale_quoting_style, ARG (1)));
+                 quotearg_style (locale_quoting_style, arg));
       return;
     }

@@ -1532,6 +1559,12 @@ mkstemp_helper (struct obstack *obs, const call_info 
*me, const char *pattern,
      user forgot to supply them.  Output must be quoted if
      successful.  */
   obstack_grow (obs, curr_quote.str1, curr_quote.len1);
+  if (strlen (pattern) < len)
+    {
+      m4_warn (0, me, _("argument %s truncated"),
+              quotearg_style_mem (locale_quoting_style, pattern, len));
+      len = strlen (pattern);
+    }
   obstack_grow (obs, pattern, len);
   for (i = 0; len > 0 && i < 6; i++)
     if (pattern[--len] != 'X')
@@ -1543,7 +1576,8 @@ mkstemp_helper (struct obstack *obs, const call_info *me, 
const char *pattern,
   fd = mkstemp (name);
   if (fd < 0)
     {
-      m4_warn (errno, me, _("cannot create tempfile `%s'"), pattern);
+      m4_warn (errno, me, _("cannot create file from template %s"),
+              quotearg_style (locale_quoting_style, pattern));
       obstack_free (obs, obstack_finish (obs));
     }
   else
@@ -1562,6 +1596,7 @@ m4_maketemp (struct obstack *obs, int argc, 
macro_arguments *argv)

   if (bad_argc (me, argc, 1, 1))
     return;
+  m4_warn (0, me, _("recommend using mkstemp instead"));
   if (no_gnu_extensions)
     {
       /* POSIX states "any trailing 'X' characters [are] replaced with
@@ -1583,7 +1618,6 @@ m4_maketemp (struct obstack *obs, int argc, 
macro_arguments *argv)
                                       (unsigned long) getpid ());
       char *pid = (char *) obstack_copy0 (scratch, "", 0);

-      m4_warn (0, me, _("recommend using mkstemp instead"));
       for (i = len; i > 1; i--)
        if (str[i - 1] != 'X')
          break;
@@ -1669,7 +1703,7 @@ m4_m4exit (struct obstack *obs, int argc, macro_arguments 
*argv)

   /* Warn on bad arguments, but still exit.  */
   bad_argc (me, argc, 0, 1);
-  if (argc >= 2 && !numeric_arg (me, ARG (1), &exit_code))
+  if (argc >= 2 && !numeric_arg (me, ARG (1), ARG_LEN (1), &exit_code))
     exit_code = EXIT_FAILURE;
   if (exit_code < 0 || exit_code > 255)
     {
@@ -1784,13 +1818,15 @@ m4_debugmode (struct obstack *obs, int argc, 
macro_arguments *argv)
 {
   const call_info *me = arg_info (argv);
   const char *str = ARG (1);
+  size_t len = ARG_LEN (1);

   bad_argc (me, argc, 0, 1);

   if (argc == 1)
     debug_level = 0;
-  else if (debug_decode (str) < 0)
-    m4_warn (0, me, _("bad debug flags: `%s'"), str);
+  else if (debug_decode (str, len) < 0)
+    m4_warn (0, me, _("bad debug flags: %s"),
+            quotearg_style_mem (locale_quoting_style, str, len));
 }

 /*-------------------------------------------------------------------------.
@@ -1807,9 +1843,17 @@ m4_debugfile (struct obstack *obs, int argc, 
macro_arguments *argv)

   if (argc == 1)
     debug_set_output (me, NULL);
-  else if (!debug_set_output (me, ARG (1)))
-    m4_warn (errno, me, _("cannot set debug file %s"),
-            quotearg_style (locale_quoting_style, ARG (1)));
+  else
+    {
+      const char *str = ARG (1);
+      size_t len = ARG_LEN (1);
+      if (strlen (str) < len)
+       m4_warn (0, me, _("argument %s truncated"),
+                quotearg_style_mem (locale_quoting_style, str, len));
+      if (!debug_set_output (me, str))
+       m4_warn (errno, me, _("cannot set debug file %s"),
+                quotearg_style (locale_quoting_style, str));
+    }
 }
 
 /* This section contains text processing macros: "len", "index",
@@ -1855,7 +1899,8 @@ m4_index (struct obstack *obs, int argc, macro_arguments 
*argv)
   haystack = ARG (1);
   haystack_len = ARG_LEN (1);
   needle = ARG (2);
-  if (!arg_empty (argv, 3) && !numeric_arg (arg_info (argv), ARG (3), &offset))
+  if (!arg_empty (argv, 3)
+      && !numeric_arg (arg_info (argv), ARG (3), ARG_LEN (3), &offset))
     return;
   if (offset < 0)
     {
@@ -1907,7 +1952,7 @@ m4_substr (struct obstack *obs, int argc, macro_arguments 
*argv)
     }

   length = ARG_LEN (1);
-  if (!arg_empty (argv, 2) && !numeric_arg (me, ARG (2), &start))
+  if (!arg_empty (argv, 2) && !numeric_arg (me, ARG (2), ARG_LEN (2), &start))
     return;
   if (start < 0)
     start += length;
@@ -1916,7 +1961,7 @@ m4_substr (struct obstack *obs, int argc, macro_arguments 
*argv)
     end = length;
   else
     {
-      if (!numeric_arg (me, ARG (3), &end))
+      if (!numeric_arg (me, ARG (3), ARG_LEN (3), &end))
        return;
       if (end < 0)
        end += length;
@@ -2406,8 +2451,8 @@ m4_patsubst (struct obstack *obs, int argc, 
macro_arguments *argv)
 void
 m4_placeholder (struct obstack *obs, int argc, macro_arguments *argv)
 {
-  m4_warn (0, NULL, _("builtin `%s' requested by frozen file not found"),
-          ARG (0));
+  m4_warn (0, NULL, _("builtin %s requested by frozen file not found"),
+          quotearg_style_mem (locale_quoting_style, ARG (0), ARG_LEN (0)));
 }
 
 /*-------------------------------------------------------------------------.
diff --git a/src/debug.c b/src/debug.c
index b97fca3..8ed2d0d 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -1,7 +1,7 @@
 /* GNU m4 -- A simple macro processor

-   Copyright (C) 1991, 1992, 1993, 1994, 2004, 2006, 2007, 2008 Free
-   Software Foundation, Inc.
+   Copyright (C) 1991, 1992, 1993, 1994, 2004, 2006, 2007, 2008, 2009
+   Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -43,25 +43,33 @@ debug_init (void)
   obstack_init (&trace);
 }
 
-/*------------------------------------------------------------------.
-| Function to decode the debugging flags OPTS.  Used by main while  |
-| processing option -d, and by the builtin debugmode ().  Return -1 |
-| if the parse failed, otherwise change the debug level.           |
-`------------------------------------------------------------------*/
+/*-------------------------------------------------------------------.
+| Function to decode the debugging flags OPTS of length LEN.  If LEN |
+| is SIZE_MAX, use strlen (OPTS) instead.  Used by main while       |
+| processing option -d, and by the builtin debugmode.  Return -1 if  |
+| the parse failed, otherwise change the debug level.               |
+`-------------------------------------------------------------------*/

 int
-debug_decode (const char *opts)
+debug_decode (const char *opts, size_t len)
 {
   int level;
   char mode = '\0';

-  if (opts == NULL || *opts == '\0')
+  if (!opts)
+    opts = "";
+  if (len == SIZE_MAX)
+    len = strlen (opts);
+  if (!len)
     level = DEBUG_TRACE_DEFAULT | debug_level;
   else
     {
       if (*opts == '-' || *opts == '+')
-       mode = *opts++;
-      for (level = 0; *opts; opts++)
+       {
+         len--;
+         mode = *opts++;
+       }
+      for (level = 0; len--; opts++)
        {
          switch (*opts)
            {
diff --git a/src/format.c b/src/format.c
index 8b2b11a..09cffa3 100644
--- a/src/format.c
+++ b/src/format.c
@@ -27,24 +27,26 @@
    same size; likewise for long and unsigned long.  We do not yet
    handle long double or long long.  */

-/* Parse STR as an integer, reporting warnings on behalf of ME.  */
+/* Parse STR of length LEN as an integer, reporting warnings on behalf
+   of ME.  */
 static int
-arg_int (const call_info *me, const char *str)
+arg_int (const call_info *me, const char *str, size_t len)
 {
   char *endp;
   long value;

   /* TODO - also allow parsing `'a' or `"a' which results in the
      numeric value of 'a', as in printf(1).  */
-  if (*str == '\0')
+  if (!len)
     {
       m4_warn (0, me, _("empty string treated as 0"));
       return 0;
     }
   errno = 0;
   value = strtol (str, &endp, 10);
-  if (*endp != '\0')
-    m4_warn (0, me, _("non-numeric argument `%s'"), str);
+  if (endp - str != len)
+    m4_warn (0, me, _("non-numeric argument %s"),
+            quotearg_style_mem (locale_quoting_style, str, len));
   else if (isspace (to_uchar (*str)))
     m4_warn (0, me, _("leading whitespace ignored"));
   else if (errno == ERANGE || (int) value != value)
@@ -52,24 +54,26 @@ arg_int (const call_info *me, const char *str)
   return value;
 }

-/* Parse STR as a long, reporting warnings on behalf of ME.  */
+/* Parse STR of length LEN as a long, reporting warnings on behalf of
+   ME.  */
 static long
-arg_long (const call_info *me, const char *str)
+arg_long (const call_info *me, const char *str, size_t len)
 {
   char *endp;
   long value;

   /* TODO - also allow parsing `'a' or `"a' which results in the
      numeric value of 'a', as in printf(1).  */
-  if (*str == '\0')
+  if (!len)
     {
       m4_warn (0, me, _("empty string treated as 0"));
       return 0L;
     }
   errno = 0;
   value = strtol (str, &endp, 10);
-  if (*endp != '\0')
-    m4_warn (0, me, _("non-numeric argument `%s'"), str);
+  if (endp - str != len)
+    m4_warn (0, me, _("non-numeric argument %s"),
+            quotearg_style_mem (locale_quoting_style, str, len));
   else if (isspace (to_uchar (*str)))
     m4_warn (0, me, _("leading whitespace ignored"));
   else if (errno == ERANGE)
@@ -77,22 +81,35 @@ arg_long (const call_info *me, const char *str)
   return value;
 }

-/* Parse STR as a double, reporting warnings on behalf of ME.  */
+/* Check STR of length LEN for embedded NUL, reporting warnings on
+   behalf of ME.  */
+static const char *
+arg_string (const call_info *me, const char *str, size_t len)
+{
+  if (strlen (str) < len)
+    m4_warn (0, me, _("argument %s truncated"),
+            quotearg_style_mem (locale_quoting_style, str, len));
+  return str;
+}
+
+/* Parse STR of length LEN as a double, reporting warnings on behalf
+   of ME.  */
 static double
-arg_double (const call_info *me, const char *str)
+arg_double (const call_info *me, const char *str, size_t len)
 {
   char *endp;
   double value;

-  if (*str == '\0')
+  if (!len)
     {
       m4_warn (0, me, _("empty string treated as 0"));
       return 0.0;
     }
   errno = 0;
   value = strtod (str, &endp);
-  if (*endp != '\0')
-    m4_warn (0, me, _("non-numeric argument `%s'"), str);
+  if (endp - str != len)
+    m4_warn (0, me, _("non-numeric argument %s"),
+            quotearg_style_mem (locale_quoting_style, str, len));
   else if (isspace (to_uchar (*str)))
     m4_warn (0, me, _("leading whitespace ignored"));
   else if (errno == ERANGE)
@@ -101,16 +118,16 @@ arg_double (const call_info *me, const char *str)
 }

 #define ARG_INT(i, argc, argv)                                         \
-  ((argc <= ++i) ? 0 : arg_int (me, ARG (i)))
+  ((argc <= ++i) ? 0 : arg_int (me, ARG (i), ARG_LEN (i)))

 #define ARG_LONG(i, argc, argv)                                                
\
-  ((argc <= ++i) ? 0L : arg_long (me, ARG (i)))
+  ((argc <= ++i) ? 0L : arg_long (me, ARG (i), ARG_LEN (i)))

 #define ARG_STR(i, argc, argv)                                         \
-  ((argc <= ++i) ? "" : ARG (i))
+  ((argc <= ++i) ? "" : arg_string (me, ARG (i), ARG_LEN (i)))

 #define ARG_DOUBLE(i, argc, argv)                                      \
-  ((argc <= ++i) ? 0.0 : arg_double (me, ARG (i)))
+  ((argc <= ++i) ? 0.0 : arg_double (me, ARG (i), ARG_LEN (i)))
 

 /*------------------------------------------------------------------.
diff --git a/src/freeze.c b/src/freeze.c
index 2648e39..1502532 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -1,7 +1,7 @@
 /* GNU m4 -- A simple macro processor

-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006, 2007, 2008
-   Free Software Foundation, Inc.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006, 2007, 2008,
+   2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -371,8 +371,13 @@ reload_frozen_state (const char *name)

            case 'F':

-             /* Enter a macro having a builtin function as a definition.  */
-
+             /* Enter a macro having a builtin function as a
+                definition.  No builtin contains NUL in the name.  */
+             if (strlen (string[1]) < number[1])
+               m4_error (EXIT_FAILURE, 0, NULL, _("\
+ill-formed frozen file, invalid builtin %s encountered"),
+                         quotearg_style_mem (locale_quoting_style, string[1],
+                                             number[1]));
              bp = find_builtin_by_name (string[1]);
              define_builtin (string[0], number[0], bp, SYMBOL_PUSHDEF);
              break;
diff --git a/src/m4.c b/src/m4.c
index 91842e4..ef244e3 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -480,7 +480,7 @@ main (int argc, char *const *argv, char *const *envp)
        break;

       case 'E':
-       debug_decode ("-d");
+       debug_decode ("-d", SIZE_MAX);
        if (!fatal_warnings)
          fatal_warnings = true;
        else
@@ -530,8 +530,9 @@ main (int argc, char *const *argv, char *const *envp)
       case 'd':
        if (seen_file)
          goto defer;
-       if (debug_decode (optarg) < 0)
-         error (0, 0, "bad debug flags: `%s'", optarg);
+       if (debug_decode (optarg, SIZE_MAX) < 0)
+         error (0, 0, "bad debug flags: %s",
+                quotearg_style (locale_quoting_style, optarg));
        break;

       case 'e':
@@ -603,7 +604,7 @@ main (int argc, char *const *argv, char *const *envp)
   if (interactive)
     {
       signal (SIGINT, SIG_IGN);
-      setbuf (stdout, (char *) NULL);
+      setbuf (stdout, NULL);
     }

   /* Handle deferred command line macro definitions.  Must come after
@@ -631,8 +632,9 @@ main (int argc, char *const *argv, char *const *envp)
          break;

        case 'd':
-         if (debug_decode (arg) < 0)
-           error (0, 0, "bad debug flags: `%s'", arg);
+         if (debug_decode (arg, SIZE_MAX) < 0)
+           error (0, 0, "bad debug flags: %s",
+                  quotearg_style (locale_quoting_style, optarg));
          break;

        case 't':
diff --git a/src/m4.h b/src/m4.h
index 0611db4..b5521ca 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -205,7 +205,7 @@ extern FILE *debug;
 #define DEBUG_TRACE_DEFAULT 0x407

 void debug_init (void);
-int debug_decode (const char *);
+int debug_decode (const char *, size_t);
 void debug_flush_files (void);
 bool debug_set_output (const call_info *, const char *);
 void debug_message (const char *, ...) M4_GNUC_PRINTF (1, 2);
diff --git a/src/path.c b/src/path.c
index d630bad..0ed674c 100644
--- a/src/path.c
+++ b/src/path.c
@@ -1,7 +1,7 @@
 /* GNU m4 -- A simple macro processor

-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 2004, 2006, 2007, 2008
-   Free Software Foundation, Inc.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 2004, 2006, 2007, 2008,
+   2009 Free Software Foundation, Inc.

    This file is part of GNU M4.

@@ -176,7 +176,11 @@ m4_path_search (const char *file, char **result)
       if (fp != NULL)
        {
          if (debug_level & DEBUG_TRACE_PATH)
-           debug_message ("path search for `%s' found `%s'", file, name);
+           debug_message ("path search for %s found %s",
+                          quotearg_style (locale_quoting_style, file),
+                          quotearg_n_style (1, locale_quoting_style, name));
+         if (set_cloexec_flag (fileno (fp), true) != 0)
+           m4_warn (errno, NULL, _("cannot protect input file across forks"));
          if (result)
            *result = name;
          else
-- 
1.6.1.2


reply via email to

[Prev in Thread] Current Thread [Next in Thread]