m4-patches
[Top][All Lists]
Advanced

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

improved error messages


From: Eric Blake
Subject: improved error messages
Date: Thu, 22 Nov 2007 20:44:24 -0700
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071031 Thunderbird/2.0.0.9 Mnenhy/0.7.5.666

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

A lot of error messages include the macro name, and some even make the
enclosing macro name optional (such as detecting end of file in a string).
 However, blindly printing a macro name to a tty can corrupt terminal
state, if the user has used changeword/changesyntax, or is using indir, to
include non-printing characters in the macro name.  So this patch factors
out the macro name into the m4_warn function, so that a future patch can
use the gnulib quotearg module to protect strange macro names in one place
(other things also need help from quotearg, but one step at a time...).
The branch has the bigger impact, but even head is impacted.

2007-11-22  Eric Blake  <address@hidden>

        More error messages tied to macro names.
        * src/input.c (set_word_regexp): Take additional parameter.
        (input_init): Adjust caller.
        * src/debug.c (debug_set_file, debug_set_output): Take additional
        parameter.
        (debug_init): Adjust caller.
        (expansion_level): Move declaration...
        * src/m4.h (expansion_level): ...here.
        (debug_set_output, set_word_regexp): Adjust prototypes.
        * src/builtin.c (m4_changeword, m4_m4exit, m4_debugfile): Adjust
        callers.
        * src/m4.c (main): Likewise.

        Refactor error messages to avoid macros.
        * src/m4.h (_): Define, as a placeholder for now.
        (M4ERROR, M4ERROR_AT_LINE): Delete.
        (m4_error, m4_error_at_line): Change prototype.
        (m4_warn, m4_warn_at_line): New prototypes.
        * src/m4.c (m4_verror_at_line): New helper function.
        (m4_error, m4_error_at_line): Use new function, and properly call
        va_end.
        (m4_warn, m4_warn_at_line): New functions.
        (stackovf_handler, main): All callers changed.
        * src/builtin.c: Likewise.
        * src/debug.c: Likewise.
        * src/eval.c: Likewise.
        * src/format.c: Likewise.
        * src/freeze.c: Likewise.
        * src/input.c: Likewise.
        * src/macro.c: Likewise.
        * doc/m4.texinfo (Indir, Builtin, Dumpdef, Incr, Eval, Substr)
        (Improved forloop): Adjust tests accordingly.

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

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

iD8DBQFHRkyX84KuGfSFAYARAlc1AJ4n4Mn7kizMBo7IVL6aW9VB9P4e9gCgm5Ct
uRBDpuy4zsZMo4teL/WxxXY=
=mZY6
-----END PGP SIGNATURE-----
>From 910837ec453663d0423f73eb79c17740486450a0 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 22 Nov 2007 14:13:49 -0700
Subject: [PATCH] Refactor error messages to avoid macros.

* src/m4.h (_): Define, as a placeholder for now.
(M4ERROR, M4ERROR_AT_LINE): Delete.
(m4_error, m4_error_at_line): Change prototype.
(m4_warn, m4_warn_at_line): New prototypes.
* src/m4.c (m4_verror_at_line): New helper function.
(m4_error, m4_error_at_line): Use new function, and properly call
va_end.
(m4_warn, m4_warn_at_line): New functions.
(stackovf_handler, main): All callers changed.
* src/builtin.c: Likewise.
* src/debug.c: Likewise.
* src/eval.c: Likewise.
* src/format.c: Likewise.
* src/freeze.c: Likewise.
* src/input.c: Likewise.
* src/macro.c: Likewise.
* doc/m4.texinfo (Indir, Builtin, Dumpdef, Incr, Eval, Substr)
(Improved forloop): Adjust tests accordingly.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog      |   20 ++++++++
 doc/m4.texinfo |   40 ++++++++--------
 src/builtin.c  |  135 ++++++++++++++++++++-----------------------------------
 src/debug.c    |   17 +++-----
 src/eval.c     |   30 ++++--------
 src/format.c   |    6 +--
 src/freeze.c   |   35 ++++++++-------
 src/input.c    |   26 ++++-------
 src/m4.c       |  132 ++++++++++++++++++++++++++++++++++++++++++++----------
 src/m4.h       |   16 ++++---
 src/macro.c    |   13 +++---
 src/output.c   |   54 ++++++++++++-----------
 src/path.c     |    6 +--
 13 files changed, 289 insertions(+), 241 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4d8ec23..2c2cd8c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,25 @@
 2007-11-22  Eric Blake  <address@hidden>
 
+       Refactor error messages to avoid macros.
+       * src/m4.h (_): Define, as a placeholder for now.
+       (M4ERROR, M4ERROR_AT_LINE): Delete.
+       (m4_error, m4_error_at_line): Change prototype.
+       (m4_warn, m4_warn_at_line): New prototypes.
+       * src/m4.c (m4_verror_at_line): New helper function.
+       (m4_error, m4_error_at_line): Use new function, and properly call
+       va_end.
+       (m4_warn, m4_warn_at_line): New functions.
+       (stackovf_handler, main): All callers changed.
+       * src/builtin.c: Likewise.
+       * src/debug.c: Likewise.
+       * src/eval.c: Likewise.
+       * src/format.c: Likewise.
+       * src/freeze.c: Likewise.
+       * src/input.c: Likewise.
+       * src/macro.c: Likewise.
+       * doc/m4.texinfo (Indir, Builtin, Dumpdef, Incr, Eval, Substr)
+       (Improved forloop): Adjust tests accordingly.
+
        Security fix: avoid arbitrary code execution with 'm4 -F'.
        * src/freeze.c (produce_frozen_state): Never pass raw file name as
        printf format.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 6ae09f7..3cc3539 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -2367,7 +2367,7 @@ f(define(`f', `2'))
 indir(`f', define(`f', `3'))
 @result{}3
 indir(`f', undefine(`f'))
address@hidden:stdin:4: indir: undefined macro `f'
address@hidden:stdin:4: Warning: indir: undefined macro `f'
 @result{}
 @end example
 
@@ -2389,7 +2389,7 @@ indir(`define', `foo', defn(`divnum'))
 foo
 @result{}0
 indir(`divert', defn(`foo'))
address@hidden:stdin:5: divert: empty string treated as 0
address@hidden:stdin:5: Warning: divert: empty string treated as 0
 @result{}
 @end example
 
@@ -2452,10 +2452,10 @@ $ @kbd{m4 -P}
 m4_builtin(`divnum')
 @result{}0
 m4_builtin(`m4_divnum')
address@hidden:stdin:2: m4_builtin: undefined builtin `m4_divnum'
address@hidden:stdin:2: Warning: m4_builtin: undefined builtin `m4_divnum'
 @result{}
 m4_indir(`divnum')
address@hidden:stdin:3: m4_indir: undefined macro `divnum'
address@hidden:stdin:3: Warning: m4_indir: undefined macro `divnum'
 @result{}
 m4_indir(`m4_divnum')
 @result{}0
@@ -2469,13 +2469,13 @@ recognized; but it will provoke a warning, and result 
in a void expansion.
 builtin
 @result{}builtin
 builtin()
address@hidden:stdin:2: builtin: undefined builtin `'
address@hidden:stdin:2: Warning: builtin: undefined builtin `'
 @result{}
 builtin(`builtin')
 @error{}m4:stdin:3: Warning: builtin: too few arguments: 0 < 1
 @result{}
 builtin(`builtin',)
address@hidden:stdin:4: builtin: undefined builtin `'
address@hidden:stdin:4: Warning: builtin: undefined builtin `'
 @result{}
 @end example
 
@@ -3133,7 +3133,7 @@ f(popdef(`f')dumpdef(`f'))
 @error{}f:@tabchar{}``$0'1'
 @result{}f2
 f(popdef(`f')dumpdef(`f'))
address@hidden:stdin:3: dumpdef: undefined macro `f'
address@hidden:stdin:3: Warning: dumpdef: undefined macro `f'
 @result{}f1
 @end example
 
@@ -3231,7 +3231,7 @@ undefine(`foo')
 ifdef(`foo', `yes', `no')
 @result{}no
 indir(`foo')
address@hidden:stdin:8: indir: undefined macro `foo'
address@hidden:stdin:8: Warning: indir: undefined macro `foo'
 @result{}
 define(`foo', `blah')
 @result{}
@@ -4920,7 +4920,7 @@ substr(`abc')
 @error{}m4:stdin:1: Warning: substr: too few arguments: 1 < 2
 @result{}abc
 substr(`abc',)
address@hidden:stdin:2: substr: empty string treated as 0
address@hidden:stdin:2: Warning: substr: empty string treated as 0
 @result{}abc
 @end example
 
@@ -5266,10 +5266,10 @@ incr(`4')
 decr(`7')
 @result{}6
 incr()
address@hidden:stdin:3: incr: empty string treated as 0
address@hidden:stdin:3: Warning: incr: empty string treated as 0
 @result{}1
 decr()
address@hidden:stdin:4: decr: empty string treated as 0
address@hidden:stdin:4: Warning: decr: empty string treated as 0
 @result{}-1
 @end example
 
@@ -5388,12 +5388,12 @@ eval(`+ + - ~ ! ~ 0')
 eval(`2 || 1 / 0')
 @result{}1
 eval(`0 || 1 / 0')
address@hidden:stdin:9: eval: divide by zero: 0 || 1 / 0
address@hidden:stdin:9: Warning: eval: divide by zero: 0 || 1 / 0
 @result{}
 eval(`0 && 1 % 0')
 @result{}0
 eval(`2 && 1 % 0')
address@hidden:stdin:11: eval: modulo by zero: 2 && 1 % 0
address@hidden:stdin:11: Warning: eval: modulo by zero: 2 && 1 % 0
 @result{}
 @end example
 
@@ -5414,9 +5414,9 @@ eval(`2 ** 0')
 @result{}1
 eval(`0 ** 0')
 @result{}
address@hidden:stdin:5: eval: divide by zero: 0 ** 0
address@hidden:stdin:5: Warning: eval: divide by zero: 0 ** 0
 eval(`4 ** -2')
address@hidden:stdin:6: eval: negative exponent: 4 ** -2
address@hidden:stdin:6: Warning: eval: negative exponent: 4 ** -2
 @result{}
 @end example
 
@@ -5461,7 +5461,7 @@ square(square(`5')` + 1')
 define(`foo', `666')
 @result{}
 eval(`foo / 6')
address@hidden:stdin:11: eval: bad expression: foo / 6
address@hidden:stdin:11: Warning: eval: bad expression: foo / 6
 @result{}
 eval(foo / 6)
 @result{}111
@@ -5529,13 +5529,13 @@ eval(`10', `', `0')
 eval(`10', `16')
 @result{}a
 eval(`1', `37')
address@hidden:stdin:9: eval: radix 37 out of range
address@hidden:stdin:9: Warning: eval: radix 37 out of range
 @result{}
 eval(`1', , `-1')
address@hidden:stdin:10: eval: negative width
address@hidden:stdin:10: Warning: eval: negative width
 @result{}
 eval()
address@hidden:stdin:11: eval: empty string treated as 0
address@hidden:stdin:11: Warning: eval: empty string treated as 0
 @result{}0
 @end example
 
@@ -6769,7 +6769,7 @@ forloop(`', `1', `2', ` odd iterator name')
 forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')')
 @result{} 0xa 0xb 0xc
 forloop(`i', `a', `b', `non-numeric bounds')
address@hidden:stdin:6: eval: bad expression (bad input): (b) >= (a)
address@hidden:stdin:6: Warning: eval: bad expression (bad input): (b) >= (a)
 @result{}
 @end example
 
diff --git a/src/builtin.c b/src/builtin.c
index 48df3d6..e10bbde 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -374,11 +374,9 @@ set_macro_sequence (const char *regexp)
 
   msg = re_compile_pattern (regexp, strlen (regexp), &macro_sequence_buf);
   if (msg != NULL)
-    {
-      M4ERROR ((EXIT_FAILURE, 0,
-               "--warn-macro-sequence: bad regular expression `%s': %s",
-               regexp, msg));
-    }
+    m4_error (EXIT_FAILURE, 0, NULL,
+             _("--warn-macro-sequence: bad regular expression `%s': %s"),
+             regexp, msg);
   re_set_registers (&macro_sequence_buf, &macro_sequence_regs,
                    macro_sequence_regs.num_regs,
                    macro_sequence_regs.start, macro_sequence_regs.end);
@@ -439,16 +437,15 @@ define_user_macro (const char *name, const char *text, 
symbol_lookup mode)
              offset = macro_sequence_regs.end[0];
              tmp = defn[offset];
              defn[offset] = '\0';
-             M4ERROR ((warning_status, 0,
-                       "Warning: definition of `%s' contains sequence `%s'",
-                       name, defn + macro_sequence_regs.start[0]));
+             m4_warn (0, NULL, _("definition of `%s' contains sequence `%s'"),
+                      name, defn + macro_sequence_regs.start[0]);
              defn[offset] = tmp;
            }
        }
       if (offset == -2)
-       M4ERROR ((warning_status, 0,
-                 "error checking --warn-macro-sequence for macro `%s'",
-                 name));
+       m4_warn (0, NULL,
+                _("problem checking --warn-macro-sequence for macro `%s'"),
+                name);
     }
 }
 
@@ -505,16 +502,11 @@ bad_argc (const char *name, int argc, unsigned int min, 
unsigned int max)
 {
   if (argc - 1 < min)
     {
-      if (!suppress_warnings)
-       M4ERROR ((warning_status, 0,
-                 "Warning: %s: too few arguments: %d < %d",
-                 name, argc - 1, min));
+      m4_warn (0, name, _("too few arguments: %d < %d"), argc - 1, min);
       return true;
     }
-  if (argc - 1 > max && !suppress_warnings)
-    M4ERROR ((warning_status, 0,
-             "Warning: %s: extra arguments ignored: %d > %d",
-             name, argc - 1, max));
+  if (argc - 1 > max)
+    m4_warn (0, name, _("extra arguments ignored: %d > %d"), argc - 1, max);
   return false;
 }
 
@@ -532,7 +524,7 @@ numeric_arg (const char *name, const char *arg, int *valuep)
   if (*arg == '\0')
     {
       *valuep = 0;
-      M4ERROR ((warning_status, 0, "%s: empty string treated as 0", name));
+      m4_warn (0, name, _("empty string treated as 0"));
     }
   else
     {
@@ -540,16 +532,13 @@ numeric_arg (const char *name, const char *arg, int 
*valuep)
       *valuep = strtol (arg, &endp, 10);
       if (*endp != '\0')
        {
-         M4ERROR ((warning_status, 0,
-                   "%s: non-numeric argument `%s'", name, arg));
+         m4_warn (0, name, _("non-numeric argument `%s'"), arg);
          return false;
        }
       if (isspace (to_uchar (*arg)))
-       M4ERROR ((warning_status, 0,
-                 "%s: leading whitespace ignored", name));
+       m4_warn (0, name, _("leading whitespace ignored"));
       else if (errno == ERANGE)
-       M4ERROR ((warning_status, 0,
-                 "%s: numeric overflow detected", name));
+       m4_warn (0, name, _("numeric overflow detected"));
     }
   return true;
 }
@@ -664,8 +653,7 @@ define_macro (int argc, token_data **argv, symbol_lookup 
mode)
 
   if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
     {
-      M4ERROR ((warning_status, 0,
-               "Warning: %s: invalid macro name ignored", me));
+      m4_warn (0, me, _("invalid macro name ignored"));
       return;
     }
 
@@ -864,8 +852,7 @@ m4_dumpdef (struct obstack *obs, int argc, token_data 
**argv)
          if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID)
            dump_symbol (s, &data);
          else
-           M4ERROR ((warning_status, 0,
-                     "%s: undefined macro `%s'", me, ARG (i)));
+           m4_warn (0, me, _("undefined macro `%s'"), ARG (i));
        }
     }
 
@@ -925,16 +912,14 @@ m4_builtin (struct obstack *obs, int argc, token_data 
**argv)
     return;
   if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
     {
-      M4ERROR ((warning_status, 0,
-               "Warning: %s: invalid macro name ignored", me));
+      m4_warn (0, me, _("invalid macro name ignored"));
       return;
     }
 
   name = ARG (1);
   bp = find_builtin_by_name (name);
   if (bp->func == m4_placeholder)
-    M4ERROR ((warning_status, 0,
-             "%s: undefined builtin `%s'", me, name));
+    m4_warn (0, me, _("undefined builtin `%s'"), name);
   else
     {
       int i;
@@ -967,16 +952,14 @@ m4_indir (struct obstack *obs, int argc, token_data 
**argv)
     return;
   if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
     {
-      M4ERROR ((warning_status, 0,
-               "Warning: %s: invalid macro name ignored", me));
+      m4_warn (0, me, _("invalid macro name ignored"));
       return;
     }
 
   name = ARG (1);
   s = lookup_symbol (name, SYMBOL_LOOKUP);
   if (s == NULL || SYMBOL_TYPE (s) == TOKEN_VOID)
-    M4ERROR ((warning_status, 0,
-             "%s: undefined macro `%s'", me, name));
+    m4_warn (0, me, _("undefined macro `%s'"), name);
   else
     {
       int i;
@@ -1025,12 +1008,11 @@ m4_defn (struct obstack *obs, int argc, token_data 
**argv)
        case TOKEN_FUNC:
          b = SYMBOL_FUNC (s);
          if (b == m4_placeholder)
-           M4ERROR ((warning_status, 0, "\
-Warning: %s: builtin `%s' requested by frozen file not found", me, ARG (i)));
+           m4_warn (0, me,
+                    _("builtin `%s' requested by frozen file not found"),
+                    ARG (i));
          else if (argc != 2)
-           M4ERROR ((warning_status, 0,
-                     "Warning: %s: cannot concatenate builtin `%s'",
-                     me, ARG (i)));
+           m4_warn (0, me, _("cannot concatenate builtin `%s'"), ARG (i));
          else
            push_macro (b);
          break;
@@ -1122,8 +1104,7 @@ m4_esyscmd (struct obstack *obs, int argc, token_data 
**argv)
   pin = popen (ARG (1), "r");
   if (pin == NULL)
     {
-      M4ERROR ((warning_status, errno,
-               "%s: cannot open pipe to command `%s'", me, ARG (1)));
+      m4_warn (errno, me, _("cannot open pipe to command `%s'"), ARG (1));
       sysval = -1;
     }
   else
@@ -1164,8 +1145,7 @@ m4_eval (struct obstack *obs, int argc, token_data **argv)
 
   if (radix < 1 || radix > (int) strlen (digits))
     {
-      M4ERROR ((warning_status, 0,
-               "%s: radix %d out of range", me, radix));
+      m4_warn (0, me, _("radix %d out of range"), radix);
       return;
     }
 
@@ -1173,13 +1153,12 @@ m4_eval (struct obstack *obs, int argc, token_data 
**argv)
     return;
   if (min < 0)
     {
-      M4ERROR ((warning_status, 0, "%s: negative width", me));
+      m4_warn (0, me, _("negative width"));
       return;
     }
 
   if (!*ARG (1))
-    M4ERROR ((warning_status, 0,
-             "%s: empty string treated as 0", me));
+    m4_warn (0, me, _("empty string treated as 0"));
   else if (evaluate (me, ARG (1), &value))
     return;
 
@@ -1300,8 +1279,7 @@ m4_undivert (struct obstack *obs, int argc, token_data 
**argv)
        if (*endp == '\0' && !isspace (to_uchar (*str)))
          insert_diversion (file);
        else if (no_gnu_extensions)
-         M4ERROR ((warning_status, 0,
-                   "%s: non-numeric argument `%s'", me, str));
+         m4_warn (0, me, _("non-numeric argument `%s'"), str);
        else
          {
            fp = m4_path_search (str, NULL);
@@ -1309,12 +1287,10 @@ m4_undivert (struct obstack *obs, int argc, token_data 
**argv)
              {
                insert_file (fp);
                if (fclose (fp) == EOF)
-                 M4ERROR ((warning_status, errno,
-                           "%s: error undiverting `%s'", me, str));
+                 m4_warn (errno, me, _("error undiverting `%s'"), str);
              }
            else
-             M4ERROR ((warning_status, errno,
-                       "%s: cannot undivert `%s'", me, str));
+             m4_warn (errno, me, _("cannot undivert `%s'"), str);
          }
       }
 }
@@ -1420,11 +1396,7 @@ include (int argc, token_data **argv, bool silent)
   if (fp == NULL)
     {
       if (!silent)
-       {
-         M4ERROR ((warning_status, errno, "%s: cannot open `%s'",
-                   me, ARG (1)));
-         retcode = EXIT_FAILURE;
-       }
+       m4_error (0, errno, me, _("cannot open `%s'"), ARG (1));
       return;
     }
 
@@ -1483,7 +1455,7 @@ mkstemp_helper (struct obstack *obs, const char *me, 
const char *name)
   fd = mkstemp ((char *) obstack_base (obs));
   if (fd < 0)
     {
-      M4ERROR ((0, errno, "%s: cannot create tempfile `%s'", me, name));
+      m4_warn (errno, me, _("cannot create tempfile `%s'"), name);
       obstack_free (obs, obstack_finish (obs));
     }
   else
@@ -1515,7 +1487,7 @@ m4_maketemp (struct obstack *obs, int argc, token_data 
**argv)
       int i;
       int len2;
 
-      M4ERROR ((warning_status, 0, "%s: recommend using mkstemp instead", me));
+      m4_warn (0, me, _("recommend using mkstemp instead"));
       for (i = len; i > 1; i--)
        if (str[i - 1] != 'X')
          break;
@@ -1607,8 +1579,7 @@ m4_m4exit (struct obstack *obs, int argc, token_data 
**argv)
     exit_code = EXIT_FAILURE;
   if (exit_code < 0 || exit_code > 255)
     {
-      M4ERROR ((warning_status, 0,
-               "%s: exit status out of range: `%d'", me, exit_code));
+      m4_warn (0, me, _("exit status out of range: %d"), exit_code);
       exit_code = EXIT_FAILURE;
     }
   /* Change debug stream back to stderr, to force flushing debug stream and
@@ -1730,8 +1701,7 @@ m4_debugmode (struct obstack *obs, int argc, token_data 
**argv)
        }
 
       if (new_debug_level < 0)
-       M4ERROR ((warning_status, 0,
-                 "%s: bad debug flags: `%s'", me, str));
+       m4_warn (0, me, _("bad debug flags: `%s'"), str);
       else
        {
          switch (change_flag)
@@ -1767,8 +1737,7 @@ m4_debugfile (struct obstack *obs, int argc, token_data 
**argv)
   if (argc == 1)
     debug_set_output (NULL);
   else if (!debug_set_output (ARG (1)))
-    M4ERROR ((warning_status, errno,
-             "%s: cannot set error file: `%s'", me, ARG (1)));
+    m4_warn (errno, me, _("cannot set error file: `%s'"), ARG (1));
 }
 
 /* This section contains text processing macros: "len", "index",
@@ -2020,8 +1989,8 @@ substitute (struct obstack *obs, const char *me, const 
char *victim,
        case '0':
          if (!substitute_warned)
            {
-             M4ERROR ((warning_status, 0, "\
-Warning: %s: \\0 will disappear, use \\& instead in replacements", me));
+             m4_warn (0, me, _("\
+\\0 will disappear, use \\& instead in replacements"));
              substitute_warned = 1;
            }
          /* Fall through.  */
@@ -2036,16 +2005,14 @@ Warning: %s: \\0 will disappear, use \\& instead in 
replacements", me));
        case '7': case '8': case '9':
          ch -= '0';
          if (!regs || regs->num_regs - 1 <= ch)
-           M4ERROR ((warning_status, 0,
-                     "Warning: %s: sub-expression %d not present", me, ch));
+           m4_warn (0, me, _("sub-expression %d not present"), ch);
          else if (regs->end[ch] > 0)
            obstack_grow (obs, victim + regs->start[ch],
                          regs->end[ch] - regs->start[ch]);
          break;
 
        case '\0':
-         M4ERROR ((warning_status, 0,
-                   "Warning: %s: trailing \\ ignored in replacement", me));
+         m4_warn (0, me, _("trailing \\ ignored in replacement"));
          return;
 
        default:
@@ -2125,8 +2092,7 @@ m4_regexp (struct obstack *obs, int argc, token_data 
**argv)
   msg = compile_pattern (regexp, strlen (regexp), &buf, &regs);
   if (msg != NULL)
     {
-      M4ERROR ((warning_status, 0,
-               "%s: bad regular expression: `%s': %s", me, regexp, msg));
+      m4_warn (0, me, _("bad regular expression: `%s': %s"), regexp, msg);
       return;
     }
 
@@ -2136,8 +2102,7 @@ m4_regexp (struct obstack *obs, int argc, token_data 
**argv)
                        argc == 3 ? NULL : regs);
 
   if (startpos == -2)
-    M4ERROR ((warning_status, 0,
-             "%s: error matching regular expression `%s'", me, regexp));
+    m4_warn (0, me, _("problem matching regular expression `%s'"), regexp);
   else if (argc == 3)
     shipout_int (obs, startpos);
   else if (startpos >= 0)
@@ -2195,8 +2160,7 @@ m4_patsubst (struct obstack *obs, int argc, token_data 
**argv)
   msg = compile_pattern (regexp, strlen (regexp), &buf, &regs);
   if (msg != NULL)
     {
-      M4ERROR ((warning_status, 0,
-               "%s: bad regular expression `%s': %s", me, regexp, msg));
+      m4_warn (0, me, _("bad regular expression `%s': %s"), regexp, msg);
       return;
     }
 
@@ -2216,9 +2180,8 @@ m4_patsubst (struct obstack *obs, int argc, token_data 
**argv)
             copied verbatim.  */
 
          if (matchpos == -2)
-           M4ERROR ((warning_status, 0,
-                     "%s: error matching regular expression `%s'",
-                     me, regexp));
+           m4_warn (0, me, _("problem matching regular expression `%s'"),
+                    regexp);
          else if (offset < length)
            obstack_grow (obs, victim + offset, length - offset);
          break;
@@ -2262,8 +2225,8 @@ m4_patsubst (struct obstack *obs, int argc, token_data 
**argv)
 void
 m4_placeholder (struct obstack *obs, int argc, token_data **argv)
 {
-  M4ERROR ((warning_status, 0, "\
-builtin `%s' requested by frozen file not found", ARG (0)));
+  m4_warn (0, NULL, _("builtin `%s' requested by frozen file not found"),
+          ARG (0));
 }
 
 /*-------------------------------------------------------------------------.
diff --git a/src/debug.c b/src/debug.c
index 998ccb9..4173f9b 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -134,10 +134,8 @@ debug_set_file (FILE *fp)
 
   if (debug != NULL && debug != stderr && debug != stdout
       && close_stream (debug) != 0)
-    {
-      M4ERROR ((warning_status, errno, "error writing to debug stream"));
-      retcode = EXIT_FAILURE;
-    }
+    /* FIXME - report on behalf of macro caller.  */
+    m4_error (0, errno, NULL, _("error writing to debug stream"));
   debug = fp;
 
   if (debug != NULL && debug != stdout)
@@ -154,11 +152,8 @@ debug_set_file (FILE *fp)
          && stdout_stat.st_ino != 0)
        {
          if (debug != stderr && close_stream (debug) != 0)
-           {
-             M4ERROR ((warning_status, errno,
-                       "error writing to debug stream"));
-             retcode = EXIT_FAILURE;
-           }
+           /* FIXME - report on behalf of macro caller.  */
+           m4_error (0, errno, NULL, _("error writing to debug stream"));
          debug = stdout;
        }
     }
@@ -217,8 +212,8 @@ debug_set_output (const char *name)
        return false;
 
       if (set_cloexec_flag (fileno (fp), true) != 0)
-       M4ERROR ((warning_status, errno,
-                 "Warning: cannot protect debug file across forks"));
+       /* FIXME - report on behalf of macro caller.  */
+       m4_warn (errno, NULL, _("cannot protect debug file across forks"));
       debug_set_file (fp);
     }
   return true;
diff --git a/src/eval.c b/src/eval.c
index 1af596e..fdb50b7 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -310,45 +310,36 @@ evaluate (const char *me, const char *expr, int32_t *val)
       break;
 
     case MISSING_RIGHT:
-      M4ERROR ((warning_status, 0,
-               "%s: bad expression (missing right parenthesis): %s",
-               me, expr));
+      m4_warn (0, me, _("bad expression (missing right parenthesis): %s"),
+              expr);
       break;
 
     case SYNTAX_ERROR:
-      M4ERROR ((warning_status, 0,
-               "%s: bad expression: %s", me, expr));
+      m4_warn (0, me, _("bad expression: %s"), expr);
       break;
 
     case UNKNOWN_INPUT:
-      M4ERROR ((warning_status, 0,
-               "%s: bad expression (bad input): %s", me, expr));
+      m4_warn (0, me, _("bad expression (bad input): %s"), expr);
       break;
 
     case EXCESS_INPUT:
-      M4ERROR ((warning_status, 0,
-               "%s: bad expression (excess input): %s", me, expr));
+      m4_warn (0, me, _("bad expression (excess input): %s"), expr);
       break;
 
     case INVALID_OPERATOR:
-      M4ERROR ((warning_status, 0,
-               "%s: invalid operator: %s", me, expr));
-      retcode = EXIT_FAILURE;
+      m4_error (0, 0, me, _("invalid operator: %s"), expr);
       break;
 
     case DIVIDE_ZERO:
-      M4ERROR ((warning_status, 0,
-               "%s: divide by zero: %s", me, expr));
+      m4_warn (0, me, _("divide by zero: %s"), expr);
       break;
 
     case MODULO_ZERO:
-      M4ERROR ((warning_status, 0,
-               "%s: modulo by zero: %s", me, expr));
+      m4_warn (0, me, _("modulo by zero: %s"), expr);
       break;
 
     case NEGATIVE_EXPONENT:
-      M4ERROR ((warning_status, 0,
-               "%s: negative exponent: %s", me, expr));
+      m4_warn (0, me, _("negative exponent: %s"), expr);
       break;
 
     default:
@@ -530,8 +521,7 @@ equality_term (const char *me, eval_token et, int32_t *v1)
 
       if (op == ASSIGN)
       {
-       M4ERROR ((warning_status, 0, "\
-Warning: %s: recommend ==, not =, for equality", me));
+       m4_warn (0, me, _("recommend ==, not =, for equality"));
        op = EQ;
       }
       *v1 = (op == EQ) == (*v1 == v2);
diff --git a/src/format.c b/src/format.c
index a9e7150..96ac562 100644
--- a/src/format.c
+++ b/src/format.c
@@ -233,8 +233,7 @@ format (struct obstack *obs, int argc, token_data **argv)
       c = *fmt++;
       if (c > sizeof ok || !ok[c])
        {
-         M4ERROR ((warning_status, 0,
-                   "Warning: %s: unrecognized specifier in `%s'", me, f));
+         m4_warn (0, me, _("unrecognized specifier in `%s'"), f);
          if (c == '\0')
            fmt--;
          continue;
@@ -308,8 +307,7 @@ format (struct obstack *obs, int argc, token_data **argv)
         Issue a warning, then proceed.  */
       if (str == NULL)
        {
-         M4ERROR ((warning_status, 0,
-                   "Warning: %s: unable to format output for `%s'", me, f));
+         m4_warn (0, me, _("unable to format output for `%s'"), f);
          continue;
        }
 
diff --git a/src/freeze.c b/src/freeze.c
index df68f3a..16a4ed2 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -56,9 +56,10 @@ produce_frozen_state (const char *name)
   symbol *sym;
   const builtin *bp;
 
-  if (file = fopen (name, O_BINARY ? "wb" : "w"), !file)
+  file = fopen (name, O_BINARY ? "wb" : "w");
+  if (!file)
     {
-      M4ERROR ((warning_status, errno, "%s", name));
+      m4_error (0, errno, NULL, _("cannot open %s"), name);
       return;
     }
 
@@ -151,7 +152,7 @@ produce_frozen_state (const char *name)
 
   fputs ("# End of frozen state file\n", file);
   if (close_stream (file) != 0)
-    M4ERROR ((EXIT_FAILURE, errno, "unable to create frozen state"));
+    m4_error (EXIT_FAILURE, errno, NULL, _("unable to create frozen state"));
 }
 
 /*----------------------------------------------------------------------.
@@ -162,10 +163,10 @@ static void
 issue_expect_message (int expected)
 {
   if (expected == '\n')
-    M4ERROR ((EXIT_FAILURE, 0, "expecting line feed in frozen file"));
+    m4_error (EXIT_FAILURE, 0, NULL, _("expecting line feed in frozen file"));
   else
-    M4ERROR ((EXIT_FAILURE, 0, "expecting character `%c' in frozen file",
-             expected));
+    m4_error (EXIT_FAILURE, 0, NULL,
+             _("expecting character `%c' in frozen file"), expected);
 }
 
 /*-------------------------------------------------.
@@ -226,7 +227,7 @@ reload_frozen_state (const char *name)
 
   file = m4_path_search (name, NULL);
   if (file == NULL)
-    M4ERROR ((EXIT_FAILURE, errno, "cannot open %s", name));
+    m4_error (EXIT_FAILURE, errno, NULL, _("cannot open %s"), name);
 
   allocated[0] = 100;
   string[0] = xcharalloc ((size_t) allocated[0]);
@@ -239,12 +240,12 @@ reload_frozen_state (const char *name)
   GET_CHARACTER;
   GET_NUMBER (number[0]);
   if (number[0] > 1)
-    M4ERROR ((EXIT_MISMATCH, 0,
-             "frozen file version %d greater than max supported of 1",
-             number[0]));
+    m4_error (EXIT_MISMATCH, 0, NULL,
+             _("frozen file version %d greater than max supported of 1"),
+             number[0]);
   else if (number[0] < 1)
-    M4ERROR ((EXIT_FAILURE, 0,
-             "ill-formed frozen file, version directive expected"));
+    m4_error (EXIT_FAILURE, 0, NULL,
+             _("ill-formed frozen file, version directive expected"));
   VALIDATE ('\n');
 
   GET_DIRECTIVE;
@@ -253,7 +254,7 @@ reload_frozen_state (const char *name)
       switch (character)
        {
        default:
-         M4ERROR ((EXIT_FAILURE, 0, "ill-formed frozen file"));
+         m4_error (EXIT_FAILURE, 0, NULL, _("ill-formed frozen file"));
 
        case 'C':
        case 'D':
@@ -292,7 +293,8 @@ reload_frozen_state (const char *name)
 
              if (number[0] > 0)
                if (!fread (string[0], (size_t) number[0], 1, file))
-                 M4ERROR ((EXIT_FAILURE, 0, "premature end of frozen file"));
+                 m4_error (EXIT_FAILURE, 0, NULL,
+                           _("premature end of frozen file"));
 
              string[0][number[0]] = '\0';
            }
@@ -308,7 +310,8 @@ reload_frozen_state (const char *name)
 
          if (number[1] > 0)
            if (!fread (string[1], (size_t) number[1], 1, file))
-             M4ERROR ((EXIT_FAILURE, 0, "premature end of frozen file"));
+             m4_error (EXIT_FAILURE, 0, NULL,
+                       _("premature end of frozen file"));
 
          string[1][number[1]] = '\0';
          GET_CHARACTER;
@@ -372,7 +375,7 @@ reload_frozen_state (const char *name)
   free (string[1]);
   errno = 0;
   if (ferror (file) || fclose (file) != 0)
-    M4ERROR ((EXIT_FAILURE, errno, "unable to read frozen state"));
+    m4_error (EXIT_FAILURE, errno, NULL, _("unable to read frozen state"));
 
 #undef GET_CHARACTER
 #undef GET_DIRECTIVE
diff --git a/src/input.c b/src/input.c
index 58f24be..156df33 100644
--- a/src/input.c
+++ b/src/input.c
@@ -354,16 +354,12 @@ pop_input (bool cleanup)
 
       if (ferror (isp->u.u_f.fp))
        {
-         M4ERROR ((warning_status, 0, "read error"));
+         m4_error (0, 0, NULL, _("read error"));
          if (isp->u.u_f.close)
            fclose (isp->u.u_f.fp);
-         retcode = EXIT_FAILURE;
        }
       else if (isp->u.u_f.close && fclose (isp->u.u_f.fp) == EOF)
-       {
-         M4ERROR ((warning_status, errno, "error reading file"));
-         retcode = EXIT_FAILURE;
-       }
+       m4_error (0, errno, NULL, _("error reading file"));
       start_of_input_line = isp->u.u_f.advance;
       output_current_line = -1;
       break;
@@ -575,8 +571,8 @@ skip_line (const char *name)
   if (ch == CHAR_EOF)
     /* current_file changed to "" if we see CHAR_EOF, use the
        previous value we stored earlier.  */
-    M4ERROR_AT_LINE ((warning_status, 0, file, line,
-                     "Warning: %s: end of file treated as newline", name));
+    m4_warn_at_line (0, file, line, name,
+                    _("end of file treated as newline"));
   /* On the rare occasion that dnl crosses include file boundaries
      (either the input file did not end in a newline, or changeword
      was used), calling next_char can update current_file and
@@ -776,8 +772,8 @@ set_word_regexp (const char *regexp)
 
   if (msg != NULL)
     {
-      M4ERROR ((warning_status, 0,
-               "bad regular expression `%s': %s", regexp, msg));
+      /* FIXME - report on behalf of macro caller.  */
+      m4_warn (0, NULL, _("bad regular expression `%s': %s"), regexp, msg);
       return;
     }
 
@@ -874,9 +870,8 @@ next_token (token_data *td, int *line, const char *caller)
       else
        /* current_file changed to "" if we see CHAR_EOF, use the
           previous value we stored earlier.  */
-       M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, *line,
-                         "%s%send of file in comment",
-                         caller ? caller : "", caller ? ": " : ""));
+       m4_error_at_line (EXIT_FAILURE, 0, file, *line, caller,
+                         _("end of file in comment"));
 
       type = TOKEN_STRING;
     }
@@ -958,9 +953,8 @@ next_token (token_data *td, int *line, const char *caller)
          if (ch == CHAR_EOF)
            /* current_file changed to "" if we see CHAR_EOF, use
               the previous value we stored earlier.  */
-           M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, *line,
-                             "%s%send of file in string",
-                             caller ? caller : "", caller ? ": " : ""));
+           m4_error_at_line (EXIT_FAILURE, 0, file, *line, caller,
+                             _("end of file in string"));
 
          if (MATCH (ch, rquote.string, true))
            {
diff --git a/src/m4.c b/src/m4.c
index 38e26e9..de9abc0 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -24,6 +24,7 @@
 #include <getopt.h>
 #include <limits.h>
 #include <signal.h>
+#include <stdarg.h>
 
 #include "version-etc.h"
 
@@ -83,34 +84,115 @@ typedef struct macro_definition macro_definition;
 
 /* Error handling functions.  */
 
-/*-----------------------.
-| Wrapper around error.  |
-`-----------------------*/
+/*------------------------------------------------------------------.
+| Helper for all the error reporting, as a wrapper around          |
+| error_at_line.  Report error message based on FORMAT and ARGS, on |
+| behalf of MACRO, at the location FILE and LINE (but with no      |
+| location if LINE is 0).  If ERRNUM, decode the errno value that   |
+| caused the error.  If STATUS, exit immediately with that status.  |
+| If WARN, prepend 'Warning: '.                                            |
+`------------------------------------------------------------------*/
+
+static void
+m4_verror_at_line (bool warn, int status, int errnum, const char *file,
+                  int line, const char *macro, const char *format,
+                  va_list args)
+{
+  char *full = NULL;
+  /* Prepend warning and the macro name, as needed.  But if that fails
+     for non-memory reasons (unlikely), then still use the original
+     format.  */
+  if (warn && macro)
+    full = xasprintf (_("Warning: %s: %s"), macro, format);
+  else if (warn)
+    full = xasprintf (_("Warning: %s"), format);
+  else if (macro)
+    full = xasprintf (_("%s: %s"), macro, format);
+  verror_at_line (status, errnum, line ? file : NULL, line,
+                 full ? full : format, args);
+  free (full);
+  if ((!warn || fatal_warnings) && !retcode)
+    retcode = EXIT_FAILURE;
+}
+
+/*----------------------------------------------------------------.
+| Wrapper around error.  Report error message based on FORMAT and |
+| subsequent args, on behalf of MACRO, and the current input line |
+| (if any).  If ERRNUM, decode the errno value that caused the   |
+| error.  If STATUS, exit immediately with that status.                  |
+`----------------------------------------------------------------*/
 
 void
-m4_error (int status, int errnum, const char *format, ...)
+m4_error (int status, int errnum, const char *macro, const char *format, ...)
 {
   va_list args;
   va_start (args, format);
-  verror_at_line (status, errnum, current_line ? current_file : NULL,
-                 current_line, format, args);
-  if (fatal_warnings && ! retcode)
-    retcode = EXIT_FAILURE;
+  if (status == EXIT_SUCCESS && warning_status)
+    status = EXIT_FAILURE;
+  m4_verror_at_line (false, status, errnum, current_file, current_line,
+                    macro, format, args);
+  va_end (args);
 }
 
-/*-------------------------------.
-| Wrapper around error_at_line.  |
-`-------------------------------*/
+/*----------------------------------------------------------------.
+| Wrapper around error_at_line.  Report error message based on   |
+| FORMAT and subsequent args, on behalf of MACRO, at the location |
+| FILE and LINE (but with no location if LINE is 0).  If ERRNUM,  |
+| decode the errno value that caused the error.  If STATUS, exit  |
+| immediately with that status.                                          |
+`----------------------------------------------------------------*/
 
 void
 m4_error_at_line (int status, int errnum, const char *file, int line,
-                 const char *format, ...)
+                 const char *macro, const char *format, ...)
 {
   va_list args;
   va_start (args, format);
-  verror_at_line (status, errnum, line ? file : NULL, line, format, args);
-  if (fatal_warnings && ! retcode)
-    retcode = EXIT_FAILURE;
+  if (status == EXIT_SUCCESS && warning_status)
+    status = EXIT_FAILURE;
+  m4_verror_at_line (false, status, errnum, file, line, macro, format, args);
+  va_end (args);
+}
+
+/*------------------------------------------------------------------.
+| Wrapper around error.  Report warning message based on FORMAT and |
+| subsequent args, on behalf of MACRO, and the current input line   |
+| (if any).  If ERRNUM, decode the errno value that caused the     |
+| warning.                                                         |
+`------------------------------------------------------------------*/
+
+void
+m4_warn (int errnum, const char *macro, const char *format, ...)
+{
+  va_list args;
+  if (!suppress_warnings)
+    {
+      va_start (args, format);
+      m4_verror_at_line (true, warning_status, errnum, current_file,
+                        current_line, macro, format, args);
+      va_end (args);
+    }
+}
+
+/*----------------------------------------------------------------.
+| Wrapper around error_at_line.  Report warning message based on  |
+| FORMAT and subsequent args, on behalf of MACRO, at the location |
+| FILE and LINE (but with no location if LINE is 0).  If ERRNUM,  |
+| decode the errno value that caused the warning.                |
+`----------------------------------------------------------------*/
+
+void
+m4_warn_at_line (int errnum, const char *file, int line, const char *macro,
+                const char *format, ...)
+{
+  va_list args;
+  if (!suppress_warnings)
+    {
+      va_start (args, format);
+      m4_verror_at_line (true, warning_status, errnum, file, line, macro,
+                        format, args);
+      va_end (args);
+    }
 }
 
 #ifdef USE_STACKOVF
@@ -122,8 +204,8 @@ m4_error_at_line (int status, int errnum, const char *file, 
int line,
 static void
 stackovf_handler (void)
 {
-  M4ERROR ((EXIT_FAILURE, 0,
-           "ERROR: stack overflow.  (Infinite define recursion?)"));
+  m4_error (EXIT_FAILURE, 0, NULL,
+           _("ERROR: stack overflow.  (Infinite define recursion?)"));
 }
 
 #endif /* USE_STACKOV */
@@ -406,7 +488,7 @@ main (int argc, char *const *argv, char *const *envp)
        break;
 
       case 'E':
-       if (! fatal_warnings)
+       if (!fatal_warnings)
          fatal_warnings = true;
        else
          warning_status = EXIT_FAILURE;
@@ -485,11 +567,11 @@ main (int argc, char *const *argv, char *const *envp)
        break;
 
       case WARN_MACRO_SEQUENCE_OPTION:
-         /* Don't call set_macro_sequence here, as it can exit.
-            --warn-macro-sequence sets optarg to NULL (which uses the
-            default regexp); --warn-macro-sequence= sets optarg to ""
-            (which disables these warnings).  */
-        macro_sequence = optarg;
+        /* Don't call set_macro_sequence here, as it can exit.
+           --warn-macro-sequence sets optarg to NULL (which uses the
+           default regexp); --warn-macro-sequence= sets optarg to ""
+           (which disables these warnings).  */
+       macro_sequence = optarg;
        break;
 
       case VERSION_OPTION:
@@ -506,7 +588,7 @@ main (int argc, char *const *argv, char *const *envp)
 
   /* Do the basic initializations.  */
   if (debugfile && !debug_set_output (debugfile))
-    M4ERROR ((0, errno, "cannot set debug file `%s'", debugfile));
+    m4_error (0, errno, NULL, _("cannot set debug file `%s'"), debugfile);
 
   input_init ();
   output_init ();
@@ -564,7 +646,7 @@ main (int argc, char *const *argv, char *const *envp)
 
        case '\1':
          seen_file = true;
-          process_file (defines->arg);
+         process_file (defines->arg);
          break;
 
        default:
diff --git a/src/m4.h b/src/m4.h
index c372ffd..d1fe1ae 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -69,6 +69,10 @@
 
 /* Used for version mismatch, when -R detects a frozen file it can't parse.  */
 #define EXIT_MISMATCH 63
+
+/* M4 1.4.x is not yet internationalized.  But when it is, this can be
+   redefined as gettext().  */
+#define _(STRING) STRING
 
 /* Various declarations.  */
 
@@ -128,12 +132,12 @@ extern const char *user_word_regexp;      /* -W */
 extern int retcode;
 extern const char *program_name;
 
-void m4_error (int, int, const char *, ...) M4_GNUC_PRINTF(3, 4);
-void m4_error_at_line (int, int, const char *, int,
-                      const char *, ...) M4_GNUC_PRINTF(5, 6);
-
-#define M4ERROR(Arglist) (m4_error Arglist)
-#define M4ERROR_AT_LINE(Arglist) (m4_error_at_line Arglist)
+void m4_error (int, int, const char *, const char *, ...) M4_GNUC_PRINTF(4, 5);
+void m4_error_at_line (int, int, const char *, int, const char *,
+                      const char *, ...) M4_GNUC_PRINTF(6, 7);
+void m4_warn (int, const char *, const char *, ...) M4_GNUC_PRINTF(3, 4);
+void m4_warn_at_line (int, const char *, int, const char *,
+                     const char *, ...) M4_GNUC_PRINTF(5, 6);
 
 #ifdef USE_STACKOVF
 void setup_stackovf_trap (char *const *, char *const *,
diff --git a/src/macro.c b/src/macro.c
index 6781cd9..8a678d4 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -134,8 +134,7 @@ warn_builtin_concat (const char *caller, builtin_func *func)
 {
   const builtin *bp = find_builtin_by_addr (func);
   assert (bp);
-  M4ERROR ((warning_status, 0, "Warning: %s: cannot concatenate builtin `%s'",
-           caller, bp->name));
+  m4_warn (0, caller, _("cannot concatenate builtin `%s'"), bp->name);
 }
 
 /*-------------------------------------------------------------------.
@@ -202,8 +201,8 @@ expand_argument (struct obstack *obs, token_data *argp, 
const char *caller)
        case TOKEN_EOF:
          /* current_file changed to "" if we see TOKEN_EOF, use the
             previous value we stored earlier.  */
-         M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, line,
-                           "%s: end of file in argument list", caller));
+         m4_error_at_line (EXIT_FAILURE, 0, file, line, caller,
+                           _("end of file in argument list"));
          break;
 
        case TOKEN_WORD:
@@ -342,9 +341,9 @@ expand_macro (symbol *sym)
   SYMBOL_PENDING_EXPANSIONS (sym)++;
   expansion_level++;
   if (nesting_limit > 0 && expansion_level > nesting_limit)
-    M4ERROR ((EXIT_FAILURE, 0,
-             "recursion limit of %d exceeded, use -L<N> to change it",
-             nesting_limit));
+    m4_error (EXIT_FAILURE, 0, NULL,
+             _("recursion limit of %d exceeded, use -L<N> to change it"),
+             nesting_limit);
 
   macro_call_id++;
   my_call_id = macro_call_id;
diff --git a/src/output.c b/src/output.c
index 5873d8c..478d3b2 100644
--- a/src/output.c
+++ b/src/output.c
@@ -169,8 +169,8 @@ cleanup_tmpfile (void)
          if (!diversion->size && diversion->u.file
              && close_stream_temp (diversion->u.file) != 0)
            {
-             M4ERROR ((0, errno,
-                       "cannot clean temporary file for diversion"));
+             m4_warn (errno, NULL,
+                      _("cannot clean temporary file for diversion"));
              fail = true;
            }
        }
@@ -198,8 +198,8 @@ m4_tmpname (int divnum)
       tail = strrchr (buffer, '-') + 1;
     }
   if (sprintf (tail, "%d", divnum) < 0)
-    M4ERROR ((EXIT_FAILURE, errno,
-             "cannot create temporary file for diversion"));
+    m4_error (EXIT_FAILURE, errno, NULL,
+             _("cannot create temporary file for diversion"));
   return buffer;
 }
 
@@ -219,8 +219,8 @@ m4_tmpfile (int divnum)
     {
       output_temp_dir = create_temp_dir ("m4-", NULL, true);
       if (output_temp_dir == NULL)
-       M4ERROR ((EXIT_FAILURE, errno,
-                 "cannot create temporary file for diversion"));
+       m4_error (EXIT_FAILURE, errno, NULL,
+                 _("cannot create temporary file for diversion"));
       atexit (cleanup_tmpfile);
     }
   name = m4_tmpname (divnum);
@@ -229,12 +229,11 @@ m4_tmpfile (int divnum)
   if (file == NULL)
     {
       unregister_temp_file (output_temp_dir, name);
-      M4ERROR ((EXIT_FAILURE, errno,
-               "cannot create temporary file for diversion"));
+      m4_error (EXIT_FAILURE, errno, NULL,
+               _("cannot create temporary file for diversion"));
     }
   else if (set_cloexec_flag (fileno (file), true) != 0)
-    M4ERROR ((warning_status, errno,
-             "Warning: cannot protect diversion across forks"));
+    m4_warn (errno, NULL, _("cannot protect diversion across forks"));
   return file;
 }
 
@@ -249,16 +248,15 @@ m4_tmpopen (int divnum)
 
   file = fopen_temp (name, O_BINARY ? "ab+" : "a+");
   if (file == NULL)
-    M4ERROR ((EXIT_FAILURE, errno,
-             "cannot create temporary file for diversion"));
+    m4_error (EXIT_FAILURE, errno, NULL,
+             _("cannot create temporary file for diversion"));
   else if (set_cloexec_flag (fileno (file), true) != 0)
-    M4ERROR ((warning_status, errno,
-             "Warning: cannot protect diversion across forks"));
+    m4_warn (errno, NULL, _("cannot protect diversion across forks"));
   /* POSIX states that it is undefined whether an append stream starts
      at offset 0 or at the end.  We want the beginning.  */
   else if (fseeko (file, 0, SEEK_SET) != 0)
-    M4ERROR ((EXIT_FAILURE, errno,
-             "cannot seek to beginning of diversion"));
+    m4_error (EXIT_FAILURE, errno, NULL,
+             _("cannot seek to beginning of diversion"));
   return file;
 }
 
@@ -351,8 +349,8 @@ make_room_for (int length)
          count = fwrite (selected_buffer, (size_t) selected_diversion->used,
                          1, selected_diversion->u.file);
          if (count != 1)
-           M4ERROR ((EXIT_FAILURE, errno,
-                     "ERROR: cannot flush diversion to temporary file"));
+           m4_error (EXIT_FAILURE, errno, NULL,
+                     _("cannot flush diversion to temporary file"));
        }
 
       /* Reclaim the buffer space for other diversions.  */
@@ -379,7 +377,8 @@ make_room_for (int length)
          FILE *file = selected_diversion->u.file;
          selected_diversion->u.file = NULL;
          if (m4_tmpclose (file) != 0)
-           M4ERROR ((0, errno, "cannot close temporary file for diversion"));
+           m4_warn (errno, NULL,
+                    _("cannot close temporary file for diversion"));
        }
 
       /* The current buffer may be safely reallocated.  */
@@ -441,7 +440,7 @@ output_text (const char *text, int length)
     {
       count = fwrite (text, length, 1, output_file);
       if (count != 1)
-       M4ERROR ((EXIT_FAILURE, errno, "ERROR: copying inserted file"));
+       m4_error (EXIT_FAILURE, errno, NULL, _("error copying inserted file"));
     }
   else
     {
@@ -605,7 +604,8 @@ make_diversion (int divnum)
          FILE *file = output_diversion->u.file;
          output_diversion->u.file = NULL;
          if (m4_tmpclose (file) != 0)
-           M4ERROR ((0, errno, "cannot close temporary file for diversion"));
+           m4_warn (errno, NULL,
+                    _("cannot close temporary file for diversion"));
        }
       output_diversion = NULL;
       output_file = NULL;
@@ -689,7 +689,7 @@ insert_file (FILE *file)
     {
       length = fread (buffer, 1, COPY_BUFFER_SIZE, file);
       if (ferror (file))
-       M4ERROR ((EXIT_FAILURE, errno, "ERROR: reading inserted file"));
+       m4_error (EXIT_FAILURE, errno, NULL, _("error reading inserted file"));
       if (length == 0)
        break;
       output_text (buffer, length);
@@ -736,10 +736,11 @@ insert_diversion_helper (m4_diversion *diversion)
          diversion->u.file = NULL;
          diversion->used = 0;
          if (m4_tmpclose (file) != 0)
-           M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
+           m4_warn (errno, NULL,
+                    _("cannot clean temporary file for diversion"));
        }
       if (m4_tmpremove (diversion->divnum) != 0)
-       M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
+       m4_warn (errno, NULL, _("cannot clean temporary file for diversion"));
     }
   gl_oset_remove (diversion_table, diversion);
   diversion->u.next = free_list;
@@ -819,10 +820,11 @@ freeze_diversions (FILE *file)
              struct stat file_stat;
              diversion->u.file = m4_tmpopen (diversion->divnum);
              if (fstat (fileno (diversion->u.file), &file_stat) < 0)
-               M4ERROR ((EXIT_FAILURE, errno, "cannot stat diversion"));
+               m4_error (EXIT_FAILURE, errno, NULL,
+                         _("cannot stat diversion"));
              if (file_stat.st_size < 0
                  || file_stat.st_size != (unsigned long int) file_stat.st_size)
-               M4ERROR ((EXIT_FAILURE, 0, "diversion too large"));
+               m4_error (EXIT_FAILURE, 0, NULL, _("diversion too large"));
              xfprintf (file, "D%d,%lu\n", diversion->divnum,
                        (unsigned long int) file_stat.st_size);
            }
diff --git a/src/path.c b/src/path.c
index 27ac4cc..98d4567 100644
--- a/src/path.c
+++ b/src/path.c
@@ -133,8 +133,7 @@ m4_path_search (const char *file, char **result)
   if (fp != NULL)
     {
       if (set_cloexec_flag (fileno (fp), true) != 0)
-       M4ERROR ((warning_status, errno,
-                 "Warning: cannot protect input file across forks"));
+       m4_warn (errno, NULL, _("cannot protect input file across forks"));
       if (result)
        *result = xstrdup (file);
       return fp;
@@ -163,8 +162,7 @@ m4_path_search (const char *file, char **result)
          if (debug_level & DEBUG_TRACE_PATH)
            DEBUG_MESSAGE2 ("path search for `%s' found `%s'", file, name);
          if (set_cloexec_flag (fileno (fp), true) != 0)
-           M4ERROR ((warning_status, errno,
-                     "Warning: cannot protect input file across forks"));
+           m4_warn (errno, NULL, _("cannot protect input file across forks"));
          if (result)
            *result = name;
          else
-- 
1.5.3.5


>From 9de0b8950ca83762363605805c40f8f8614acbc8 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 22 Nov 2007 19:54:32 -0700
Subject: [PATCH] More error messages tied to macro names.

* src/input.c (set_word_regexp): Take additional parameter.
(input_init): Adjust caller.
* src/debug.c (debug_set_file, debug_set_output): Take additional
parameter.
(debug_init): Adjust caller.
(expansion_level): Move declaration...
* src/m4.h (expansion_level): ...here.
(debug_set_output, set_word_regexp): Adjust prototypes.
* src/builtin.c (m4_changeword, m4_m4exit, m4_debugfile): Adjust
callers.
* src/m4.c (main): Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog     |   13 +++++++++++++
 src/builtin.c |   15 +++++++--------
 src/debug.c   |   36 ++++++++++++++++--------------------
 src/input.c   |    6 +++---
 src/m4.c      |    4 ++--
 src/m4.h      |    6 ++++--
 6 files changed, 45 insertions(+), 35 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2c2cd8c..9bbf726 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2007-11-22  Eric Blake  <address@hidden>
 
+       More error messages tied to macro names.
+       * src/input.c (set_word_regexp): Take additional parameter.
+       (input_init): Adjust caller.
+       * src/debug.c (debug_set_file, debug_set_output): Take additional
+       parameter.
+       (debug_init): Adjust caller.
+       (expansion_level): Move declaration...
+       * src/m4.h (expansion_level): ...here.
+       (debug_set_output, set_word_regexp): Adjust prototypes.
+       * src/builtin.c (m4_changeword, m4_m4exit, m4_debugfile): Adjust
+       callers.
+       * src/m4.c (main): Likewise.
+
        Refactor error messages to avoid macros.
        * src/m4.h (_): Define, as a placeholder for now.
        (M4ERROR, M4ERROR_AT_LINE): Delete.
diff --git a/src/builtin.c b/src/builtin.c
index e10bbde..cc4e469 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -24,8 +24,6 @@
 
 #include "m4.h"
 
-extern FILE *popen ();
-
 #include "regex.h"
 
 #if HAVE_SYS_WAIT_H
@@ -1365,10 +1363,11 @@ m4_changecom (struct obstack *obs, int argc, token_data 
**argv)
 static void
 m4_changeword (struct obstack *obs, int argc, token_data **argv)
 {
-  if (bad_argc (ARG (0), argc, 1, 1))
-    return;
+  const char *me = ARG (0);
 
-  set_word_regexp (ARG (1));
+  if (bad_argc (me, argc, 1, 1))
+    return;
+  set_word_regexp (me, ARG (1));
 }
 
 #endif /* ENABLE_CHANGEWORD */
@@ -1584,7 +1583,7 @@ m4_m4exit (struct obstack *obs, int argc, token_data 
**argv)
     }
   /* Change debug stream back to stderr, to force flushing debug stream and
      detect any errors it might have encountered.  */
-  debug_set_output (NULL);
+  debug_set_output (me, NULL);
   debug_flush_files ();
   if (exit_code == EXIT_SUCCESS && retcode != EXIT_SUCCESS)
     exit_code = retcode;
@@ -1735,8 +1734,8 @@ m4_debugfile (struct obstack *obs, int argc, token_data 
**argv)
   bad_argc (me, argc, 0, 1);
 
   if (argc == 1)
-    debug_set_output (NULL);
-  else if (!debug_set_output (ARG (1)))
+    debug_set_output (me, NULL);
+  else if (!debug_set_output (me, ARG (1)))
     m4_warn (errno, me, _("cannot set error file: `%s'"), ARG (1));
 }
 
diff --git a/src/debug.c b/src/debug.c
index 4173f9b..c22a482 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -30,9 +30,7 @@ FILE *debug = NULL;
 /* Obstack for trace messages.  */
 static struct obstack trace;
 
-extern int expansion_level;
-
-static void debug_set_file (FILE *);
+static void debug_set_file (const char *, FILE *);
 
 /*----------------------------------.
 | Initialise the debugging module.  |
@@ -41,7 +39,7 @@ static void debug_set_file (FILE *);
 void
 debug_init (void)
 {
-  debug_set_file (stderr);
+  debug_set_file (NULL, stderr);
   obstack_init (&trace);
 }
 
@@ -128,14 +126,13 @@ debug_decode (const char *opts)
 `------------------------------------------------------------------------*/
 
 static void
-debug_set_file (FILE *fp)
+debug_set_file (const char *caller, FILE *fp)
 {
   struct stat stdout_stat, debug_stat;
 
   if (debug != NULL && debug != stderr && debug != stdout
       && close_stream (debug) != 0)
-    /* FIXME - report on behalf of macro caller.  */
-    m4_error (0, errno, NULL, _("error writing to debug stream"));
+    m4_error (0, errno, caller, _("error writing to debug stream"));
   debug = fp;
 
   if (debug != NULL && debug != stdout)
@@ -152,8 +149,7 @@ debug_set_file (FILE *fp)
          && stdout_stat.st_ino != 0)
        {
          if (debug != stderr && close_stream (debug) != 0)
-           /* FIXME - report on behalf of macro caller.  */
-           m4_error (0, errno, NULL, _("error writing to debug stream"));
+           m4_error (0, errno, caller, _("error writing to debug stream"));
          debug = stdout;
        }
     }
@@ -190,21 +186,22 @@ debug_flush_files (void)
     }
 }
 
-/*-------------------------------------------------------------------------.
-| Change the debug output to file NAME.  If NAME is NULL, debug output is  |
-| reverted to stderr, and if empty debug output is discarded.  Return true |
-| iff the output stream was changed.                                      |
-`-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------.
+| Change the debug output to file NAME.  If NAME is NULL, debug             |
+| output is reverted to stderr, and if empty, debug output is       |
+| discarded.  Return true iff the output stream was changed.  Report |
+| errors on behalf of CALLER.                                       |
+`-------------------------------------------------------------------*/
 
 bool
-debug_set_output (const char *name)
+debug_set_output (const char *caller, const char *name)
 {
   FILE *fp;
 
   if (name == NULL)
-    debug_set_file (stderr);
+    debug_set_file (caller, stderr);
   else if (*name == '\0')
-    debug_set_file (NULL);
+    debug_set_file (caller, NULL);
   else
     {
       fp = fopen (name, "a");
@@ -212,9 +209,8 @@ debug_set_output (const char *name)
        return false;
 
       if (set_cloexec_flag (fileno (fp), true) != 0)
-       /* FIXME - report on behalf of macro caller.  */
-       m4_warn (errno, NULL, _("cannot protect debug file across forks"));
-      debug_set_file (fp);
+       m4_warn (errno, caller, _("cannot protect debug file across forks"));
+      debug_set_file (caller, fp);
     }
   return true;
 }
diff --git a/src/input.c b/src/input.c
index 156df33..3d96ec7 100644
--- a/src/input.c
+++ b/src/input.c
@@ -687,7 +687,7 @@ input_init (void)
   ecomm.length = strlen (ecomm.string);
 
 #ifdef ENABLE_CHANGEWORD
-  set_word_regexp (user_word_regexp);
+  set_word_regexp (NULL, user_word_regexp);
 #endif
 }
 
@@ -752,7 +752,7 @@ set_comment (const char *bc, const char *ec)
 #ifdef ENABLE_CHANGEWORD
 
 void
-set_word_regexp (const char *regexp)
+set_word_regexp (const char *caller, const char *regexp)
 {
   int i;
   char test[2];
@@ -773,7 +773,7 @@ set_word_regexp (const char *regexp)
   if (msg != NULL)
     {
       /* FIXME - report on behalf of macro caller.  */
-      m4_warn (0, NULL, _("bad regular expression `%s': %s"), regexp, msg);
+      m4_warn (0, caller, _("bad regular expression `%s': %s"), regexp, msg);
       return;
     }
 
diff --git a/src/m4.c b/src/m4.c
index de9abc0..217d389 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -587,7 +587,7 @@ main (int argc, char *const *argv, char *const *envp)
   defines = head;
 
   /* Do the basic initializations.  */
-  if (debugfile && !debug_set_output (debugfile))
+  if (debugfile && !debug_set_output (NULL, debugfile))
     m4_error (0, errno, NULL, _("cannot set debug file `%s'"), debugfile);
 
   input_init ();
@@ -676,7 +676,7 @@ main (int argc, char *const *argv, char *const *envp)
   /* Change debug stream back to stderr, to force flushing the debug
      stream and detect any errors it might have encountered.  The
      three standard streams are closed by close_stdin.  */
-  debug_set_output (NULL);
+  debug_set_output (NULL, NULL);
 
   if (frozen_file_to_write)
     produce_frozen_state (frozen_file_to_write);
diff --git a/src/m4.h b/src/m4.h
index d1fe1ae..4f1fa1f 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -231,7 +231,7 @@ extern FILE *debug;
 void debug_init (void);
 int debug_decode (const char *);
 void debug_flush_files (void);
-bool debug_set_output (const char *);
+bool debug_set_output (const char *, const char *);
 void debug_message_prefix (void);
 
 void trace_prepre (const char *, int);
@@ -318,7 +318,7 @@ extern STRING lquote, rquote;
 void set_quotes (const char *, const char *);
 void set_comment (const char *, const char *);
 #ifdef ENABLE_CHANGEWORD
-void set_word_regexp (const char *);
+void set_word_regexp (const char *, const char *);
 #endif
 
 /* File: output.c --- output functions.  */
@@ -388,6 +388,8 @@ void hack_all_symbols (hack_symbol *, void *);
 
 /* File: macro.c  --- macro expansion.  */
 
+extern int expansion_level;
+
 void expand_input (void);
 void call_macro (symbol *, int, token_data **, struct obstack *);
 
-- 
1.5.3.5


reply via email to

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