[SV 40361] Don't use vsnprintf(), which is an ISO C99 function.

* output.c (error, fatal, message): Take an extra argument specifying
how many bytes are used by the formatted arguments.
(get_buffer): New function that allocates the requested buffer size.
Remove msc_vsnprintf(), vfmtconcat(), and fmtconcat() as unneeded.
* makeint.h: Declare various helper macros for generating output.
* *.c: Change all error(), fatal(), message() calls to use the macros,
or pass the extra length argument directly.
diff --git a/amiga.c b/amiga.c
index 73ed59a..8025483 100644
--- a/amiga.c
+++ b/amiga.c
@@ -42,7 +42,7 @@
     buffer = AllocMem (len, MEMF_ANY);
 
     if (!buffer)
-      fatal (NILF, "MyExecute: Cannot allocate space for calling a command");
+      O (fatal, NILF, "MyExecute: Cannot allocate space for calling a command\n");
 
     ptr = buffer;
 
diff --git a/ar.c b/ar.c
index afed591..5d2f000 100644
--- a/ar.c
+++ b/ar.c
@@ -43,7 +43,7 @@
     return 0;
 
   if (p[1] == '(' && end[-1] == ')')
-    fatal (NILF, _("attempt to use unsupported feature: '%s'"), name);
+    OS (fatal, NILF, _("attempt to use unsupported feature: '%s'"), name);
 
   return 1;
 }
@@ -120,7 +120,7 @@
 int
 ar_touch (const char *name)
 {
-  error (NILF, _("touch archive member is not available on VMS"));
+  O (error, NILF, _("touch archive member is not available on VMS"));
   return -1;
 }
 #else
@@ -144,24 +144,24 @@
   switch (ar_member_touch (arname, memname))
     {
     case -1:
-      error (NILF, _("touch: Archive '%s' does not exist"), arname);
+      OS (error, NILF, _("touch: Archive '%s' does not exist"), arname);
       break;
     case -2:
-      error (NILF, _("touch: '%s' is not a valid archive"), arname);
+      OS (error, NILF, _("touch: '%s' is not a valid archive"), arname);
       break;
     case -3:
       perror_with_name ("touch: ", arname);
       break;
     case 1:
-      error (NILF,
-             _("touch: Member '%s' does not exist in '%s'"), memname, arname);
+      OSS (error, NILF,
+           _("touch: Member '%s' does not exist in '%s'"), memname, arname);
       break;
     case 0:
       val = 0;
       break;
     default:
-      error (NILF,
-             _("touch: Bad return code from ar_member_touch on '%s'"), name);
+      OS (error, NILF,
+          _("touch: Bad return code from ar_member_touch on '%s'"), name);
     }
 
   free (arname);
diff --git a/arscan.c b/arscan.c
index 2b3cd5d..e49137b 100644
--- a/arscan.c
+++ b/arscan.c
@@ -37,7 +37,7 @@
 
 static void *VMS_lib_idx;
 
-static char *VMS_saved_memname;
+static const char *VMS_saved_memname;
 
 static time_t VMS_member_date;
 
@@ -64,8 +64,9 @@
                            &bufdesc.dsc$w_length, 0);
   if (! (status & 1))
     {
-      error (NILF, _("lbr$set_module() failed to extract module info, status = %d"),
-             status);
+      ON (error, NILF,
+          _("lbr$set_module() failed to extract module info, status = %d"),
+          status);
 
       lbr$close (&VMS_lib_idx);
 
@@ -153,9 +154,10 @@
    Returns 0 if have scanned successfully.  */
 
 long int
-ar_scan (const char *archive, ar_member_func_t function, const void *arg)
+ar_scan (const char *archive, ar_member_func_t function, const void *varg)
 {
   char *p;
+  const char *arg = varg;
 
   static struct dsc$descriptor_s libdesc =
     { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
@@ -170,7 +172,7 @@
 
   if (! (status & 1))
     {
-      error (NILF, _("lbr$ini_control() failed with status = %d"), status);
+      ON (error, NILF, _("lbr$ini_control() failed with status = %d"), status);
       return -2;
     }
 
@@ -182,12 +184,12 @@
 
   if (! (status & 1))
     {
-      error (NILF, _("unable to open library '%s' to lookup member '%s'"),
-             archive, (char *)arg);
+      OSS (error, NILF, _("unable to open library '%s' to lookup member '%s'"),
+           archive, arg);
       return -1;
     }
 
-  VMS_saved_memname = (char *)arg;
+  VMS_saved_memname = arg;
 
   /* For comparison, delete .obj from arg name.  */
 
diff --git a/commands.c b/commands.c
index f910358..7c7eabf 100644
--- a/commands.c
+++ b/commands.c
@@ -403,7 +403,7 @@
      CMDS->any_recurse flag.  */
 
   if (nlines > USHRT_MAX)
-    fatal (&cmds->fileinfo, _("Recipe has too many lines (%ud)"), nlines);
+    ON (fatal, &cmds->fileinfo, _("Recipe has too many lines (%ud)"), nlines);
 
   cmds->ncommand_lines = nlines;
   cmds->command_lines = lines;
@@ -627,11 +627,13 @@
       if (ar_member_date (file->name) != file_date)
         {
           if (on_behalf_of)
-            error (NILF, _("*** [%s] Archive member '%s' may be bogus; not deleted"),
-                   on_behalf_of, file->name);
+            OSS (error, NILF,
+                 _("*** [%s] Archive member '%s' may be bogus; not deleted"),
+                 on_behalf_of, file->name);
           else
-            error (NILF, _("*** Archive member '%s' may be bogus; not deleted"),
-                   file->name);
+            OS (error, NILF,
+                _("*** Archive member '%s' may be bogus; not deleted"),
+                file->name);
         }
       return;
     }
@@ -643,9 +645,10 @@
       && FILE_TIMESTAMP_STAT_MODTIME (file->name, st) != file->last_mtime)
     {
       if (on_behalf_of)
-        error (NILF, _("*** [%s] Deleting file '%s'"), on_behalf_of, file->name);
+        OSS (error, NILF,
+             _("*** [%s] Deleting file '%s'"), on_behalf_of, file->name);
       else
-        error (NILF, _("*** Deleting file '%s'"), file->name);
+        OS (error, NILF, _("*** Deleting file '%s'"), file->name);
       if (unlink (file->name) < 0
           && errno != ENOENT)   /* It disappeared; so what.  */
         perror_with_name ("unlink: ", file->name);
diff --git a/dir.c b/dir.c
index d046bd9..77b7bfe 100644
--- a/dir.c
+++ b/dir.c
@@ -674,7 +674,7 @@
       if (d == 0)
         {
           if (errno)
-            fatal (NILF, "INTERNAL: readdir: %s\n", strerror (errno));
+            pfatal_with_name ("INTERNAL: readdir");
           break;
         }
 
diff --git a/expand.c b/expand.c
index ba04e48..28ec198 100644
--- a/expand.c
+++ b/expand.c
@@ -121,9 +121,9 @@
     {
       if (!v->exp_count)
         /* Expanding V causes infinite recursion.  Lose.  */
-        fatal (*expanding_var,
-               _("Recursive variable '%s' references itself (eventually)"),
-               v->name);
+        OS (fatal, *expanding_var,
+            _("Recursive variable '%s' references itself (eventually)"),
+            v->name);
       --v->exp_count;
     }
 
@@ -266,7 +266,7 @@
             end = strchr (beg, closeparen);
             if (end == 0)
               /* Unterminated variable reference.  */
-              fatal (*expanding_var, _("unterminated variable reference"));
+              O (fatal, *expanding_var, _("unterminated variable reference"));
             p1 = lindex (beg, end, '$');
             if (p1 != 0)
               {
diff --git a/file.c b/file.c
index b209d88..92c4052 100644
--- a/file.c
+++ b/file.c
@@ -261,22 +261,25 @@
         to_file->cmds = from_file->cmds;
       else if (from_file->cmds != to_file->cmds)
         {
+          size_t l = strlen (from_file->name);
           /* We have two sets of commands.  We will go with the
              one given in the rule explicitly mentioning this name,
              but give a message to let the user know what's going on.  */
           if (to_file->cmds->fileinfo.filenm != 0)
             error (&from_file->cmds->fileinfo,
+                   l + strlen (to_file->cmds->fileinfo.filenm) + INTSTR_LENGTH,
                    _("Recipe was specified for file '%s' at %s:%lu,"),
                    from_file->name, to_file->cmds->fileinfo.filenm,
                    to_file->cmds->fileinfo.lineno);
           else
-            error (&from_file->cmds->fileinfo,
+            error (&from_file->cmds->fileinfo, l,
                    _("Recipe for file '%s' was found by implicit rule search,"),
                    from_file->name);
-          error (&from_file->cmds->fileinfo,
+          l += strlen (to_hname);
+          error (&from_file->cmds->fileinfo, l,
                  _("but '%s' is now considered the same file as '%s'."),
                  from_file->name, to_hname);
-          error (&from_file->cmds->fileinfo,
+          error (&from_file->cmds->fileinfo, l,
                  _("Recipe for '%s' will be ignored in favor of the one for '%s'."),
                  to_hname, from_file->name);
         }
@@ -297,13 +300,14 @@
   merge_variable_set_lists (&to_file->variables, from_file->variables);
 
   if (to_file->double_colon && from_file->is_target && !from_file->double_colon)
-    fatal (NILF, _("can't rename single-colon '%s' to double-colon '%s'"),
-           from_file->name, to_hname);
+    OSS (fatal, NILF, _("can't rename single-colon '%s' to double-colon '%s'"),
+         from_file->name, to_hname);
   if (!to_file->double_colon  && from_file->double_colon)
     {
       if (to_file->is_target)
-        fatal (NILF, _("can't rename double-colon '%s' to single-colon '%s'"),
-               from_file->name, to_hname);
+        OSS (fatal, NILF,
+             _("can't rename double-colon '%s' to single-colon '%s'"),
+             from_file->name, to_hname);
       else
         to_file->double_colon = from_file->double_colon;
     }
@@ -393,7 +397,8 @@
             if (!f->dontcare)
               {
                 if (sig)
-                  error (NILF, _("*** Deleting intermediate file '%s'"), f->name);
+                  OS (error, NILF,
+                      _("*** Deleting intermediate file '%s'"), f->name);
                 else
                   {
                     if (! doneany)
@@ -803,10 +808,11 @@
          && product <= ts && ts <= ORDINARY_MTIME_MAX))
     {
       char buf[FILE_TIMESTAMP_PRINT_LEN_BOUND + 1];
+      const char *f = fname ? fname : _("Current time");
       ts = s <= OLD_MTIME ? ORDINARY_MTIME_MIN : ORDINARY_MTIME_MAX;
       file_timestamp_sprintf (buf, ts);
-      error (NILF, _("%s: Timestamp out of range; substituting %s"),
-             fname ? fname : _("Current time"), buf);
+      OSS (error, NILF,
+           _("%s: Timestamp out of range; substituting %s"), f, buf);
     }
 
   return ts;
@@ -1046,9 +1052,10 @@
 /* Verify the integrity of the data base of files.  */
 
 #define VERIFY_CACHED(_p,_n) \
-    do{\
-        if (_p->_n && _p->_n[0] && !strcache_iscached (_p->_n)) \
-          error (NULL, _("%s: Field '%s' not cached: %s"), _p->name, # _n, _p->_n); \
+    do{                                                                       \
+        if (_p->_n && _p->_n[0] && !strcache_iscached (_p->_n))               \
+          error (NULL, strlen (_p->name) + CSTRLEN (# _n) + strlen (_p->_n),  \
+                 _("%s: Field '%s' not cached: %s"), _p->name, # _n, _p->_n); \
     }while(0)
 
 static void
diff --git a/function.c b/function.c
index ecce627..5752d09 100644
--- a/function.c
+++ b/function.c
@@ -726,7 +726,7 @@
       break;
 
   if (s <= end || end - beg < 0)
-    fatal (*expanding_var, "%s: '%s'", msg, beg);
+    OSS (fatal, *expanding_var, "%s: '%s'", msg, beg);
 }
 
 
@@ -743,8 +743,8 @@
   i = atoi (argv[0]);
 
   if (i == 0)
-    fatal (*expanding_var,
-           _("first argument to 'word' function must be greater than 0"));
+    O (fatal, *expanding_var,
+       _("first argument to 'word' function must be greater than 0"));
 
   end_p = argv[1];
   while ((p = find_next_token (&end_p, 0)) != 0)
@@ -770,8 +770,8 @@
 
   start = atoi (argv[0]);
   if (start < 1)
-    fatal (*expanding_var,
-           "invalid first argument to 'wordlist' function: '%d'", start);
+    ON (fatal, *expanding_var,
+        "invalid first argument to 'wordlist' function: '%d'", start);
 
   count = atoi (argv[1]) - start + 1;
 
@@ -1082,10 +1082,10 @@
   switch (*funcname)
     {
     case 'e':
-      fatal (reading_file, "%s", msg);
+      OS (fatal, reading_file, "%s", msg);
 
     case 'w':
-      error (reading_file, "%s", msg);
+      OS (error, reading_file, "%s", msg);
       break;
 
     case 'i':
@@ -1094,7 +1094,7 @@
       break;
 
     default:
-      fatal (*expanding_var, "Internal error: func_error: '%s'", funcname);
+      OS (fatal, *expanding_var, "Internal error: func_error: '%s'", funcname);
     }
 
   /* The warning function expands to the empty string.  */
@@ -1457,7 +1457,8 @@
         }
       if (hIn == INVALID_HANDLE_VALUE)
         {
-          error (NILF, _("windows32_openpipe: DuplicateHandle(In) failed (e=%ld)\n"), e);
+          ON (error, NILF,
+              _("windows32_openpipe: DuplicateHandle(In) failed (e=%ld)\n"), e);
           return -1;
         }
     }
@@ -1480,14 +1481,15 @@
         }
       if (hErr == INVALID_HANDLE_VALUE)
         {
-          error (NILF, _("windows32_openpipe: DuplicateHandle(Err) failed (e=%ld)\n"), e);
+          ON (error, NILF,
+              _("windows32_openpipe: DuplicateHandle(Err) failed (e=%ld)\n"), e);
           return -1;
         }
     }
 
   if (! CreatePipe (&hChildOutRd, &hChildOutWr, &saAttr, 0))
     {
-      error (NILF, _("CreatePipe() failed (e=%ld)\n"), GetLastError());
+      ON (error, NILF, _("CreatePipe() failed (e=%ld)\n"), GetLastError());
       return -1;
     }
 
@@ -1495,7 +1497,7 @@
 
   if (!hProcess)
     {
-      error (NILF, _("windows32_openpipe(): process_init_fd() failed\n"));
+      O (error, NILF, _("windows32_openpipe(): process_init_fd() failed\n"));
       return -1;
     }
 
@@ -2007,7 +2009,7 @@
     {
 #if defined(__CYGWIN__) && defined(HAVE_DOS_PATHS)
       if (STOP_SET (name[0], MAP_PATHSEP))
-	root_len = 1;
+        root_len = 1;
 #endif
       strncpy (apath, name, root_len);
       apath[root_len] = '\0';
@@ -2148,20 +2150,25 @@
 
       fp = fopen (fn, mode);
       if (fp == NULL)
-        fatal (reading_file, _("open: %s: %s"), fn, strerror (errno));
+        {
+          const char *err = strerror (errno);
+          OSS (fatal, reading_file, _("open: %s: %s"), fn, err);
+        }
       else
         {
           int l = strlen (argv[1]);
           int nl = (l == 0 || argv[1][l-1] != '\n');
 
           if (fputs (argv[1], fp) == EOF || (nl && fputc ('\n', fp) == EOF))
-            fatal (reading_file, _("write: %s: %s"), fn, strerror (errno));
-
+            {
+              const char *err = strerror (errno);
+              OSS (fatal, reading_file, _("write: %s: %s"), fn, err);
+            }
           fclose (fp);
         }
     }
   else
-    fatal (reading_file, _("Invalid file operation: %s"), fn);
+    OS (fatal, reading_file, _("Invalid file operation: %s"), fn);
 
   return o;
 }
@@ -2275,7 +2282,7 @@
   char *p;
 
   if (argc < (int)entry_p->minimum_args)
-    fatal (*expanding_var,
+    fatal (*expanding_var, strlen (entry_p->name),
            _("insufficient number of arguments (%d) to function '%s'"),
            argc, entry_p->name);
 
@@ -2287,8 +2294,8 @@
     return o;
 
   if (!entry_p->fptr.func_ptr)
-    fatal (*expanding_var,
-           _("unimplemented on this platform: function '%s'"), entry_p->name);
+    OS (fatal, *expanding_var,
+        _("unimplemented on this platform: function '%s'"), entry_p->name);
 
   if (!entry_p->alloc_fn)
     return entry_p->fptr.func_ptr (o, argv, entry_p->name);
@@ -2350,7 +2357,7 @@
       break;
 
   if (count >= 0)
-    fatal (*expanding_var,
+    fatal (*expanding_var, strlen (entry_p->name),
            _("unterminated call to function '%s': missing '%c'"),
            entry_p->name, closeparen);
 
@@ -2543,17 +2550,17 @@
   len = e - name;
 
   if (len == 0)
-    fatal (flocp, _("Empty function name\n"));
+    O (fatal, flocp, _("Empty function name"));
   if (*name == '.' || *e != '\0')
-    fatal (flocp, _("Invalid function name: %s\n"), name);
+    OS (fatal, flocp, _("Invalid function name: %s"), name);
   if (len > 255)
-    fatal (flocp, _("Function name too long: %s\n"), name);
+    OS (fatal, flocp, _("Function name too long: %s"), name);
   if (min > 255)
-    fatal (flocp, _("Invalid minimum argument count (%d) for function %s\n"),
-           min, name);
+    ONS (fatal, flocp,
+         _("Invalid minimum argument count (%d) for function %s"), min, name);
   if (max > 255 || (max && max < min))
-    fatal (flocp, _("Invalid maximum argument count (%d) for function %s\n"),
-           max, name);
+    ONS (fatal, flocp,
+         _("Invalid maximum argument count (%d) for function %s"), max, name);
 
   ent = xmalloc (sizeof (struct function_table_entry));
   ent->name = name;
diff --git a/job.c b/job.c
index febfac0..2b17f42 100644
--- a/job.c
+++ b/job.c
@@ -359,7 +359,7 @@
   *fd = -1;
   if (error_string == NULL)
     error_string = _("Cannot create a temporary file\n");
-  fatal (NILF, error_string);
+  O (fatal, NILF, error_string);
 
   /* not reached */
   return NULL;
@@ -474,6 +474,7 @@
   const struct file *f = child->file;
   const gmk_floc *flocp = &f->cmds->fileinfo;
   const char *nm;
+  size_t l = strlen (f->name);
 
   if (ignored && silent_flag)
     return;
@@ -498,7 +499,10 @@
 
   OUTPUT_SET (&child->output);
 
-  message (0, _("%s: recipe for target '%s' failed"), nm, f->name);
+  message (0, l + strlen (nm),
+           _("%s: recipe for target '%s' failed"), nm, f->name);
+
+  l += strlen (pre) + strlen (post);
 
 #ifdef VMS
   if ((exit_code & 1) != 0)
@@ -507,14 +511,17 @@
       return;
     }
 
-  error (NILF, _("%s[%s] Error 0x%x%s"), pre, f->name, exit_code, post);
+  error (NILF, l + INTSTR_LENGTH,
+         _("%s[%s] Error 0x%x%s"), pre, f->name, exit_code, post);
 #else
   if (exit_sig == 0)
-    error (NILF, _("%s[%s] Error %d%s"), pre, f->name, exit_code, post);
+    error (NILF, l + INTSTR_LENGTH,
+           _("%s[%s] Error %d%s"), pre, f->name, exit_code, post);
   else
     {
       const char *s = strsignal (exit_sig);
-      error (NILF, _("%s[%s] %s%s%s"), pre, f->name, s, dump, post);
+      error (NILF, l + strlen (s) + strlen (dump),
+             _("%s[%s] %s%s%s"), pre, f->name, s, dump, post);
     }
 #endif /* VMS */
 
@@ -606,7 +613,7 @@
              Only print this message once no matter how many jobs are left.  */
           fflush (stdout);
           if (!printed)
-            error (NILF, _("*** Waiting for unfinished jobs...."));
+            O (error, NILF, _("*** Waiting for unfinished jobs...."));
           printed = 1;
         }
 
@@ -992,8 +999,8 @@
   output_close (&child->output);
 
   if (!jobserver_tokens)
-    fatal (NILF, "INTERNAL: Freeing child %p (%s) but no tokens left!\n",
-           child, child->file->name);
+    ONS (fatal, NILF, "INTERNAL: Freeing child %p (%s) but no tokens left!\n",
+         child, child->file->name);
 
   /* If we're using the jobserver and this child is not the only outstanding
      job, put a token back into the pipe for it.  */
@@ -1004,8 +1011,9 @@
       if (! release_jobserver_semaphore ())
         {
           DWORD err = GetLastError ();
-          fatal (NILF, _("release jobserver semaphore: (Error %ld: %s)"),
-                 err, map_windows32_error_to_string (err));
+          const char *estr = map_windows32_error_to_string (err);
+          OSN (fatal, NILF,
+               _("release jobserver semaphore: (Error %ld: %s)"), err, estr);
         }
 
       DB (DB_JOBS, (_("Released token for child %p (%s).\n"), child, child->file->name));
@@ -1277,7 +1285,7 @@
   /* Print the command if appropriate.  */
   if (just_print_flag || trace_flag
       || (!(flags & COMMANDS_SILENT) && !silent_flag))
-    message (0, "%s", p);
+    OS (message, 0, "%s", p);
 
   /* Tell update_goal_chain that a command has been started on behalf of
      this target.  It is important that this happens here and not in
@@ -1940,7 +1948,7 @@
         /* There must be at least one child already, or we have no business
            waiting for a token. */
         if (!children)
-          fatal (NILF, "INTERNAL: no children as we go to sleep on read\n");
+          O (fatal, NILF, "INTERNAL: no children as we go to sleep on read\n");
 
 #ifdef WINDOWS32
         /* On Windows we simply wait for the jobserver semaphore to become
@@ -1950,8 +1958,10 @@
         if (got_token < 0)
           {
             DWORD err = GetLastError ();
-            fatal (NILF, _("semaphore or child process wait: (Error %ld: %s)"),
-                   err, map_windows32_error_to_string (err));
+            const char *estr = map_windows32_error_to_string (err);
+            OSN (fatal, NILF,
+                 _("semaphore or child process wait: (Error %ld: %s)"),
+                 err, estr);
           }
 #else
         /* Set interruptible system calls, and read() for a job token.  */
@@ -2000,10 +2010,11 @@
         }
 
       if (newer[0] == '\0')
-        message (0, _("%s: target '%s' does not exist"), nm, c->file->name);
+        OSS (message, 0,
+             _("%s: target '%s' does not exist"), nm, c->file->name);
       else
-        message (0, _("%s: update target '%s' due to: %s"), nm,
-                 c->file->name, newer);
+        OSSS (message, 0,
+              _("%s: update target '%s' due to: %s"), nm, c->file->name, newer);
 
       free (newer);
     }
@@ -2114,8 +2125,8 @@
         {
           if (errno == 0)
             /* An errno value of zero means getloadavg is just unsupported.  */
-            error (NILF,
-                   _("cannot enforce load limits on this operating system"));
+            O (error, NILF,
+               _("cannot enforce load limits on this operating system"));
           else
             perror_with_name (_("cannot enforce load limit: "), "getloadavg");
         }
@@ -2196,7 +2207,7 @@
     {
       save_stdin = dup (FD_STDIN);
       if (save_stdin < 0)
-        fatal (NILF, _("no more file handles: could not duplicate stdin\n"));
+        O (fatal, NILF, _("no more file handles: could not duplicate stdin\n"));
       CLOSE_ON_EXEC (save_stdin);
 
       dup2 (stdin_fd, FD_STDIN);
@@ -2207,7 +2218,8 @@
     {
       save_stdout = dup (FD_STDOUT);
       if (save_stdout < 0)
-        fatal (NILF, _("no more file handles: could not duplicate stdout\n"));
+        O (fatal, NILF,
+           _("no more file handles: could not duplicate stdout\n"));
       CLOSE_ON_EXEC (save_stdout);
 
       dup2 (stdout_fd, FD_STDOUT);
@@ -2220,7 +2232,8 @@
         {
           save_stderr = dup (FD_STDERR);
           if (save_stderr < 0)
-            fatal (NILF, _("no more file handles: could not duplicate stderr\n"));
+            O (fatal, NILF,
+               _("no more file handles: could not duplicate stderr\n"));
           CLOSE_ON_EXEC (save_stderr);
         }
 
@@ -2235,7 +2248,7 @@
   if (save_stdin >= 0)
     {
       if (dup2 (save_stdin, FD_STDIN) != FD_STDIN)
-        fatal (NILF, _("Could not restore stdin\n"));
+        O (fatal, NILF, _("Could not restore stdin\n"));
       else
         close (save_stdin);
     }
@@ -2243,7 +2256,7 @@
   if (save_stdout >= 0)
     {
       if (dup2 (save_stdout, FD_STDOUT) != FD_STDOUT)
-        fatal (NILF, _("Could not restore stdout\n"));
+        O (fatal, NILF, _("Could not restore stdout\n"));
       else
         close (save_stdout);
     }
@@ -2251,7 +2264,7 @@
   if (save_stderr >= 0)
     {
       if (dup2 (save_stderr, FD_STDERR) != FD_STDERR)
-        fatal (NILF, _("Could not restore stderr\n"));
+        O (fatal, NILF, _("Could not restore stderr\n"));
       else
         close (save_stderr);
     }
@@ -2400,7 +2413,7 @@
   switch (errno)
     {
     case ENOENT:
-      error (NILF, _("%s: Command not found"), argv[0]);
+      OS (error, NILF, _("%s: Command not found"), argv[0]);
       break;
     case ENOEXEC:
       {
@@ -2460,7 +2473,7 @@
         execvp (shell, new_argv);
 # endif
         if (errno == ENOENT)
-          error (NILF, _("%s: Shell program not found"), shell);
+          OS (error, NILF, _("%s: Shell program not found"), shell);
         else
           perror_with_name ("execvp: ", shell);
         break;
@@ -2469,7 +2482,7 @@
 # ifdef __EMX__
     case EINVAL:
       /* this nasty error was driving me nuts :-( */
-      error (NILF, _("spawnvpe: environment space might be exhausted"));
+      O (error, NILF, _("spawnvpe: environment space might be exhausted"));
       /* FALLTHROUGH */
 # endif
 
@@ -3441,7 +3454,8 @@
       }
 #else
     else
-      fatal (NILF, _("%s (line %d) Bad shell context (!unixy && !batch_mode_shell)\n"),
+      fatal (NILF, CSTRLEN (__FILE__) + INTSTR_LENGTH,
+             _("%s (line %d) Bad shell context (!unixy && !batch_mode_shell)\n"),
             __FILE__, __LINE__);
 #endif
 
diff --git a/job.h b/job.h
index 8a20177..207fe89 100644
--- a/job.h
+++ b/job.h
@@ -39,8 +39,8 @@
 
 #ifdef NO_OUTPUT_SYNC
 # define RECORD_SYNC_MUTEX(m) \
-    error (NILF, \
-           _("-O[TYPE] (--output-sync[=TYPE]) is not configured for this build."));
+    O (error, NILF,                                                    \
+       _("-O[TYPE] (--output-sync[=TYPE]) is not configured for this build."));
 #else
 # ifdef WINDOWS32
 /* For emulations in w32/compat/posixfcn.c.  */
diff --git a/load.c b/load.c
index a2cbc25..ca73ac4 100644
--- a/load.c
+++ b/load.c
@@ -50,7 +50,10 @@
     {
       global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL);
       if (! global_dl)
-        fatal (flocp, _("Failed to open global symbol table: %s"), dlerror ());
+        {
+          const char *err = dlerror ();
+          OS (fatal, flocp, _("Failed to open global symbol table: %s"), err);
+        }
     }
 
   symp = (load_func_t) dlsym (global_dl, symname);
@@ -74,23 +77,28 @@
       /* Still no?  Then fail.  */
       if (! dlp)
         {
+          const char *err = dlerror ();
           if (noerror)
-            DB (DB_BASIC, ("%s", dlerror ()));
+            DB (DB_BASIC, ("%s", err));
           else
-            error (flocp, "%s", dlerror ());
+            OS (error, flocp, "%s", err);
           return NULL;
         }
 
       /* Assert that the GPL license symbol is defined.  */
       symp = (load_func_t) dlsym (dlp, "plugin_is_GPL_compatible");
       if (! symp)
-        fatal (flocp, _("Loaded object %s is not declared to be GPL compatible"),
-               ldname);
+        OS (fatal, flocp,
+             _("Loaded object %s is not declared to be GPL compatible"),
+             ldname);
 
       symp = (load_func_t) dlsym (dlp, symname);
       if (! symp)
-        fatal (flocp, _("Failed to load symbol %s from %s: %s"),
-               symname, ldname, dlerror ());
+        {
+          const char *err = dlerror ();
+          OSSS (fatal, flocp, _("Failed to load symbol %s from %s: %s"),
+                symname, ldname, err);
+        }
 
       /* Add this symbol to a trivial lookup table.  This is not efficient but
          it's highly unlikely we'll be loading lots of objects, and we only
@@ -133,7 +141,7 @@
 
           ++fp;
           if (fp == ep)
-            fatal (flocp, _("Empty symbol name for load: %s"), *ldname);
+            OS (fatal, flocp, _("Empty symbol name for load: %s"), *ldname);
 
           /* Make a copy of the ldname part.  */
           memcpy (new, *ldname, l);
@@ -226,7 +234,8 @@
 load_file (const gmk_floc *flocp, const char **ldname, int noerror)
 {
   if (! noerror)
-    fatal (flocp, _("The 'load' operation is not supported on this platform."));
+    O (fatal, flocp,
+       _("The 'load' operation is not supported on this platform."));
 
   return 0;
 }
@@ -234,7 +243,7 @@
 void
 unload_file (const char *name)
 {
-  fatal (NILF, "INTERNAL: Cannot unload when load is not supported!");
+  O (fatal, NILF, "INTERNAL: Cannot unload when load is not supported!");
 }
 
 #endif  /* MAKE_LOAD */
diff --git a/main.c b/main.c
index 3b33ea5..80c08d9 100644
--- a/main.c
+++ b/main.c
@@ -644,7 +644,7 @@
   char *expanded = 0;
 
   if (name[0] == '\0')
-    fatal (NILF, _("empty string invalid as file name"));
+    O (fatal, NILF, _("empty string invalid as file name"));
 
   if (name[0] == '~')
     {
@@ -731,7 +731,8 @@
                 db_level |= DB_BASIC | DB_VERBOSE;
                 break;
               default:
-                fatal (NILF, _("unknown debug level specification '%s'"), p);
+                OS (fatal, NILF,
+                    _("unknown debug level specification '%s'"), p);
               }
 
             while (*(++p) != '\0')
@@ -774,7 +775,7 @@
       else if (streq (p, "recurse"))
         output_sync = OUTPUT_SYNC_RECURSE;
       else
-        fatal (NILF, _("unknown output-sync type '%s'"), p);
+        OS (fatal, NILF, _("unknown output-sync type '%s'"), p);
     }
 
   if (sync_mutex)
@@ -784,7 +785,7 @@
 
       for (idx = 1; idx < sync_mutex->idx; idx++)
         if (!streq (sync_mutex->list[0], sync_mutex->list[idx]))
-          fatal (NILF, _("internal error: multiple --sync-mutex options"));
+          O (fatal, NILF, _("internal error: multiple --sync-mutex options"));
 
       /* Now parse the mutex handle string.  */
       mp = sync_mutex->list[0];
@@ -1238,7 +1239,7 @@
 #ifdef  HAVE_GETCWD
       perror_with_name ("getcwd", "");
 #else
-      error (NILF, "getwd: %s", current_directory);
+      OS (error, NILF, "getwd: %s", current_directory);
 #endif
       current_directory[0] = '\0';
       directory_before_chdir = 0;
@@ -1530,7 +1531,8 @@
 
       for (ui=1; ui < jobserver_fds->idx; ++ui)
         if (!streq (jobserver_fds->list[0], jobserver_fds->list[ui]))
-          fatal (NILF, _("internal error: multiple --jobserver-fds options"));
+          O (fatal, NILF,
+             _("internal error: multiple --jobserver-fds options"));
 
       /* Now parse the fds string and make sure it has the proper format.  */
 
@@ -1540,14 +1542,16 @@
       if (! open_jobserver_semaphore (cp))
         {
           DWORD err = GetLastError ();
-          fatal (NILF, _("internal error: unable to open jobserver semaphore '%s': (Error %ld: %s)"),
-                 cp, err, map_windows32_error_to_string (err));
+          const char *estr = map_windows32_error_to_string (err);
+          fatal (NILF, strlen (cp) + INTSTR_LENGTH + strlen (estr),
+                 _("internal error: unable to open jobserver semaphore '%s': (Error %ld: %s)"),
+                 cp, err, estr);
         }
       DB (DB_JOBS, (_("Jobserver client (semaphore %s)\n"), cp));
 #else
       if (sscanf (cp, "%d,%d", &job_fds[0], &job_fds[1]) != 2)
-        fatal (NILF,
-               _("internal error: invalid --jobserver-fds string '%s'"), cp);
+        OS (fatal, NILF,
+            _("internal error: invalid --jobserver-fds string '%s'"), cp);
 
       DB (DB_JOBS,
           (_("Jobserver client (fds %d,%d)\n"), job_fds[0], job_fds[1]));
@@ -1563,7 +1567,8 @@
       if (job_slots > 0)
         {
           if (! restarts)
-            error (NILF, _("warning: -jN forced in submake: disabling jobserver mode."));
+            O (error, NILF,
+               _("warning: -jN forced in submake: disabling jobserver mode."));
         }
 #ifndef WINDOWS32
 #ifdef HAVE_FCNTL
@@ -1581,8 +1586,8 @@
           if (errno != EBADF)
             pfatal_with_name (_("dup jobserver"));
 
-          error (NILF,
-                 _("warning: jobserver unavailable: using -j1.  Add '+' to parent make rule."));
+          O (error, NILF,
+             _("warning: jobserver unavailable: using -j1.  Add '+' to parent make rule."));
           job_slots = 1;
           job_fds[0] = job_fds[1] = -1;
         }
@@ -1723,7 +1728,7 @@
 #ifdef  HAVE_GETCWD
           perror_with_name ("getcwd", "");
 #else
-          error (NILF, "getwd: %s", current_directory);
+          OS (error, NILF, "getwd: %s", current_directory);
 #endif
           starting_directory = 0;
         }
@@ -1748,7 +1753,8 @@
             char *template, *tmpdir;
 
             if (stdin_nm)
-              fatal (NILF, _("Makefile from standard input specified twice."));
+              O (fatal, NILF,
+                 _("Makefile from standard input specified twice."));
 
 #ifdef VMS
 # define DEFAULT_TMPDIR     "sys$scratch:"
@@ -1979,9 +1985,9 @@
 # endif
       )
     {
-      error (NILF,
-             _("Parallel jobs (-j) are not supported on this platform."));
-      error (NILF, _("Resetting to single job (-j1) mode."));
+      O (error, NILF,
+         _("Parallel jobs (-j) are not supported on this platform."));
+      O (error, NILF, _("Resetting to single job (-j1) mode."));
       job_slots = 1;
     }
 #endif
@@ -2008,8 +2014,9 @@
       if (! create_jobserver_semaphore (job_slots - 1))
         {
           DWORD err = GetLastError ();
-          fatal (NILF, _("creating jobserver semaphore: (Error %ld: %s)"),
-                 err, map_windows32_error_to_string (err));
+          const char *estr = map_windows32_error_to_string (err);
+          OSN (fatal, NILF,
+               _("creating jobserver semaphore: (Error %ld: %s)"), err, estr);
         }
 #else
       char c = '+';
@@ -2061,7 +2068,7 @@
 #ifndef MAKE_SYMLINKS
   if (check_symlink_flag)
     {
-      error (NILF, _("Symbolic links not supported: disabling -L."));
+      O (error, NILF, _("Symbolic links not supported: disabling -L."));
       check_symlink_flag = 0;
     }
 #endif
@@ -2250,8 +2257,8 @@
                         FILE_TIMESTAMP mtime;
                         /* The update failed and this makefile was not
                            from the MAKEFILES variable, so we care.  */
-                        error (NILF, _("Failed to remake makefile '%s'."),
-                               d->file->name);
+                        OS (error, NILF, _("Failed to remake makefile '%s'."),
+                            d->file->name);
                         mtime = file_mtime_no_search (d->file);
                         any_remade |= (mtime != NONEXISTENT_MTIME
                                        && mtime != makefile_mtimes[i]);
@@ -2262,18 +2269,20 @@
                   /* This makefile was not found at all.  */
                   if (! (d->changed & RM_DONTCARE))
                     {
+                      const char *dnm = dep_name (d);
+                      size_t l = strlen (dnm);
+
                       /* This is a makefile we care about.  See how much.  */
                       if (d->changed & RM_INCLUDED)
-                        /* An included makefile.  We don't need
-                           to die, but we do want to complain.  */
-                        error (NILF,
-                               _("Included makefile '%s' was not found."),
-                               dep_name (d));
+                        /* An included makefile.  We don't need to die, but we
+                           do want to complain.  */
+                        error (NILF, l,
+                               _("Included makefile '%s' was not found."), dnm);
                       else
                         {
                           /* A normal makefile.  We must die later.  */
-                          error (NILF, _("Makefile '%s' was not found"),
-                                 dep_name (d));
+                          error (NILF, l,
+                                 _("Makefile '%s' was not found"), dnm);
                           any_failed = 1;
                         }
                     }
@@ -2338,7 +2347,8 @@
                     bad = 0;
                 }
               if (bad)
-                fatal (NILF, _("Couldn't change back to original directory."));
+                O (fatal, NILF,
+                   _("Couldn't change back to original directory."));
             }
 
           ++restarts;
@@ -2492,7 +2502,8 @@
                 {
                   /* .DEFAULT_GOAL should contain one target. */
                   if (ns->next != 0)
-                    fatal (NILF, _(".DEFAULT_GOAL contains more than one target"));
+                    O (fatal, NILF,
+                       _(".DEFAULT_GOAL contains more than one target"));
 
                   f = enter_file (strcache_add (ns->name));
 
@@ -2515,9 +2526,9 @@
   if (!goals)
     {
       if (read_files == 0)
-        fatal (NILF, _("No targets specified and no makefile found"));
+        O (fatal, NILF, _("No targets specified and no makefile found"));
 
-      fatal (NILF, _("No targets"));
+      O (fatal, NILF, _("No targets"));
     }
 
   /* Update the goals.  */
@@ -2546,8 +2557,8 @@
 
     /* If we detected some clock skew, generate one last warning */
     if (clock_skew_detected)
-      error (NILF,
-             _("warning:  Clock skew detected.  Your build may be incomplete."));
+      O (error, NILF,
+         _("warning:  Clock skew detected.  Your build may be incomplete."));
 
     /* Exit.  */
     die (makefile_status);
@@ -2807,7 +2818,8 @@
                       else
                         op = cs->long_name;
 
-                      error (NILF, _("the '%s%s' option requires a non-empty string argument"),
+                      error (NILF, strlen (op),
+                             _("the '%s%s' option requires a non-empty string argument"),
                              short_option (cs->c) ? "-" : "--", op);
                       bad = 1;
                     }
@@ -2861,7 +2873,8 @@
 
                       if (i < 1 || cp[0] != '\0')
                         {
-                          error (NILF, _("the '-%c' option requires a positive integer argument"),
+                          error (NILF, 0,
+                                 _("the '-%c' option requires a positive integer argument"),
                                  cs->c);
                           bad = 1;
                         }
@@ -3314,9 +3327,9 @@
 #endif
     {
       if (status != 2)
-        error (NILF,
-               "INTERNAL: Exiting with %u jobserver tokens (should be 0)!",
-               jobserver_tokens);
+        ON (error, NILF,
+            "INTERNAL: Exiting with %u jobserver tokens (should be 0)!",
+            jobserver_tokens);
       else
         /* Don't write back the "free" token */
         while (--jobserver_tokens)
@@ -3354,9 +3367,9 @@
 #endif
 
       if (tcnt != master_job_slots)
-        error (NILF,
-               "INTERNAL: Exiting with %u jobserver tokens available; should be %u!",
-               tcnt, master_job_slots);
+        ONN (error, NILF,
+             "INTERNAL: Exiting with %u jobserver tokens available; should be %u!",
+             tcnt, master_job_slots);
 
 #ifdef WINDOWS32
       free_jobserver_semaphore ();
diff --git a/makeint.h b/makeint.h
index c591427..f3f0111 100644
--- a/makeint.h
+++ b/makeint.h
@@ -56,12 +56,6 @@
 #endif
 #include "gnumake.h"
 
-/* Force MinGW64 to use a replacement for MS broken vsnprintf
-   implementation.  */
-#ifdef __MINGW64_VERSION_MAJOR
-# define __USE_MINGW_ANSI_STDIO 1
-#endif
-
 #ifdef  CRAY
 /* This must happen before #include <signal.h> so
    that the declaration therein is changed.  */
@@ -179,9 +173,6 @@
   (! INTEGER_TYPE_SIGNED (t) ? (t) 0 : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))
 #define INTEGER_TYPE_MAXIMUM(t) (~ (t) 0 - INTEGER_TYPE_MINIMUM (t))
 
-/* The maximum number of digits needed to represent the largest integer.  */
-#define INTEGER_LENGTH sizeof("18446744073709551616")
-
 #ifndef CHAR_MAX
 # define CHAR_MAX INTEGER_TYPE_MAXIMUM (char)
 #endif
@@ -427,17 +418,36 @@
 
 #define NILF ((gmk_floc *)0)
 
-#define CSTRLEN(_s) (sizeof (_s)-1)
+#define CSTRLEN(_s)           (sizeof (_s)-1)
 #define STRING_SIZE_TUPLE(_s) (_s), CSTRLEN(_s)
 
+/* The number of bytes needed to represent the largest integer as a string.  */
+#define INTSTR_LENGTH         CSTRLEN ("18446744073709551616")
+
 
 const char *concat (unsigned int, ...);
-void message (int prefix, const char *fmt, ...)
-              __attribute__ ((__format__ (__printf__, 2, 3)));
-void error (const gmk_floc *flocp, const char *fmt, ...)
-            __attribute__ ((__format__ (__printf__, 2, 3)));
-void fatal (const gmk_floc *flocp, const char *fmt, ...)
-                   __attribute__ ((noreturn, __format__ (__printf__, 2, 3)));
+void message (int prefix, size_t length, const char *fmt, ...)
+              __attribute__ ((__format__ (__printf__, 3, 4)));
+void error (const gmk_floc *flocp, size_t length, const char *fmt, ...)
+            __attribute__ ((__format__ (__printf__, 3, 4)));
+void fatal (const gmk_floc *flocp, size_t length, const char *fmt, ...)
+                   __attribute__ ((noreturn, __format__ (__printf__, 3, 4)));
+
+#define O(_t,_a,_f)           _t((_a), 0, (_f))
+#define OS(_t,_a,_f,_s)       _t((_a), strlen (_s), (_f), (_s))
+#define OSS(_t,_a,_f,_s1,_s2) _t((_a), strlen (_s1) + strlen (_s2), \
+                                 (_f), (_s1), (_s2))
+#define OSSS(_t,_a,_f,_s1,_s2,_s3) _t((_a), strlen (_s1) + strlen (_s2) + strlen (_s3), \
+                                      (_f), (_s1), (_s2), (_s3))
+#define ON(_t,_a,_f,_n)       _t((_a), INTSTR_LENGTH, (_f), (_n))
+#define ONN(_t,_a,_f,_n1,_n2) _t((_a), INTSTR_LENGTH*2, (_f), (_n1), (_n2))
+
+#define OSN(_t,_a,_f,_s,_n)   _t((_a), strlen (_s) + INTSTR_LENGTH, \
+                                 (_f), (_s), (_n))
+#define ONS(_t,_a,_f,_n,_s)   _t((_a), INTSTR_LENGTH + strlen (_s), \
+                                 (_f), (_n), (_s))
+
+#define OUT_OF_MEM() O (fatal, NILF, _("virtual memory exhausted"))
 
 void die (int) __attribute__ ((noreturn));
 void pfatal_with_name (const char *) __attribute__ ((noreturn));
diff --git a/misc.c b/misc.c
index bc54ce6..8e1ad4d 100644
--- a/misc.c
+++ b/misc.c
@@ -219,7 +219,7 @@
   /* Make sure we don't allocate 0, for pre-ISO implementations.  */
   void *result = malloc (size ? size : 1);
   if (result == 0)
-    fatal (NILF, _("virtual memory exhausted"));
+    OUT_OF_MEM();
   return result;
 }
 
@@ -230,7 +230,7 @@
   /* Make sure we don't allocate 0, for pre-ISO implementations.  */
   void *result = calloc (size ? size : 1, 1);
   if (result == 0)
-    fatal (NILF, _("virtual memory exhausted"));
+    OUT_OF_MEM();
   return result;
 }
 
@@ -245,7 +245,7 @@
     size = 1;
   result = ptr ? realloc (ptr, size) : malloc (size);
   if (result == 0)
-    fatal (NILF, _("virtual memory exhausted"));
+    OUT_OF_MEM();
   return result;
 }
 
@@ -262,7 +262,7 @@
 #endif
 
   if (result == 0)
-    fatal (NILF, _("virtual memory exhausted"));
+    OUT_OF_MEM();
 
 #ifdef HAVE_STRDUP
   return result;
@@ -281,7 +281,7 @@
 #ifdef HAVE_STRNDUP
   result = strndup (str, length);
   if (result == 0)
-    fatal (NILF, _("virtual memory exhausted"));
+    OUT_OF_MEM();
 #else
   result = xmalloc (length + 1);
   if (length > 0)
diff --git a/output.c b/output.c
index 5e3b073..000e241 100644
--- a/output.c
+++ b/output.c
@@ -47,33 +47,9 @@
 #define OUTPUT_ISSET(_out) ((_out)->out >= 0 || (_out)->err >= 0)
 
 #ifdef HAVE_FCNTL
-# define STREAM_OK(_s)    ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
+# define STREAM_OK(_s) ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
 #else
-# define STREAM_OK(_s)    1
-#endif
-
-/* I really want to move to gnulib.  However, this is a big undertaking
-   especially for non-UNIX platforms: how to get bootstrapping to work, etc.
-   I don't want to take the time to do it right now.  Use a hack to get a
-   useful version of vsnprintf() for Windows.  */
-#ifdef __VMS
-# define va_copy(_d, _s) ((_d) = (_s))
-#endif
-#ifdef _MSC_VER
-# define va_copy(_d, _s) ((_d) = (_s))
-# define vsnprintf msc_vsnprintf
-static int
-msc_vsnprintf (char *str, size_t size, const char *format, va_list ap)
-{
-  int len = -1;
-
-  if (size > 0)
-    len = _vsnprintf_s (str, size, _TRUNCATE, format, ap);
-  if (len == -1)
-    len = _vscprintf (format, ap);
-
-  return len;
-}
+# define STREAM_OK(_s) 1
 #endif
 
 /* Write a string to the current STDOUT or STDERR.  */
@@ -117,7 +93,7 @@
   char *p;
 
   /* Get enough space for the longest possible output.  */
-  need = strlen (program) + INTEGER_LENGTH + 2 + 1;
+  need = strlen (program) + INTSTR_LENGTH + 2 + 1;
   if (starting_directory)
     need += strlen (starting_directory);
 
@@ -512,9 +488,9 @@
   if (prev_fail || fclose_fail)
     {
       if (fclose_fail)
-        error (NILF, _("write error: %s"), strerror (errno));
+        perror_with_name (_("write error: stdout"), "");
       else
-        error (NILF, _("write error"));
+        O (error, NILF, _("write error: stdout"));
       exit (EXIT_FAILURE);
     }
 }
@@ -606,138 +582,117 @@
 }
 
 
-/* Return formatted string buffers.
-   If we move to gnulib we can use vasnprintf() etc. to make this simpler.
-   Note these functions use a static buffer, so each call overwrites the
-   results of the previous call.  */
-
 static struct fmtstring
   {
     char *buffer;
-    unsigned int size;
-    unsigned int len;
-  } fmtbuf = { NULL, 0, 0 };
+    size_t size;
+  } fmtbuf = { NULL, 0 };
 
-/* Concatenate a formatted string onto the format buffer.  */
-static const char *
-vfmtconcat (const char *fmt, va_list args)
+static char *
+get_buffer (size_t need)
 {
-  va_list vcopy;
-  int tot;
-  int unused = fmtbuf.size - fmtbuf.len;
-
-  va_copy (vcopy, args);
-
-  tot = vsnprintf (&fmtbuf.buffer[fmtbuf.len], unused, fmt, args);
-  assert (tot >= 0);
-
-  if (tot >= unused)
+  /* Make sure we have room.  */
+  if (need > fmtbuf.size)
     {
-      fmtbuf.size += tot * 2;
+      fmtbuf.size += need * 2;
       fmtbuf.buffer = xrealloc (fmtbuf.buffer, fmtbuf.size);
-
-      unused = fmtbuf.size - fmtbuf.len;
-      tot = vsnprintf (&fmtbuf.buffer[fmtbuf.len], unused, fmt, vcopy);
     }
 
-  va_end (vcopy);
-
-  fmtbuf.len += tot;
+  fmtbuf.buffer[need] = '\0';
 
   return fmtbuf.buffer;
 }
 
-static const char *
-fmtconcat (const char *fmt, ...)
-{
-  const char *p;
-  va_list args;
-
-  va_start (args, fmt);
-  p = vfmtconcat (fmt, args);
-  va_end (args);
-
-  return p;
-}
-
 /* Print a message on stdout.  */
 
 void
-message (int prefix, const char *fmt, ...)
+message (int prefix, size_t len, const char *fmt, ...)
 {
   va_list args;
+  char *p;
 
-  assert (fmt != NULL);
-
-  fmtbuf.len = 0;
+  len += strlen (fmt) + strlen (program) + INTSTR_LENGTH + 4 + 1 + 1;
+  p = get_buffer (len);
 
   if (prefix)
     {
       if (makelevel == 0)
-        fmtconcat ("%s: ", program);
+        sprintf (p, "%s: ", program);
       else
-        fmtconcat ("%s[%u]: ", program, makelevel);
+        sprintf (p, "%s[%u]: ", program, makelevel);
+      p += strlen (p);
     }
 
   va_start (args, fmt);
-  vfmtconcat (fmt, args);
+  vsprintf (p, fmt, args);
   va_end (args);
 
-  fmtconcat ("\n");
+  strcat (p, "\n");
 
+  assert (fmtbuf.buffer[len] == '\0');
   outputs (0, fmtbuf.buffer);
 }
 
 /* Print an error message.  */
 
 void
-error (const gmk_floc *flocp, const char *fmt, ...)
+error (const gmk_floc *flocp, size_t len, const char *fmt, ...)
 {
   va_list args;
+  char *p;
 
-  assert (fmt != NULL);
-
-  fmtbuf.len = 0;
+  len += (strlen (fmt) + strlen (program)
+          + (flocp && flocp->filenm ? strlen (flocp->filenm) : 0)
+          + INTSTR_LENGTH + 4 + 1 + 1);
+  p = get_buffer (len);
 
   if (flocp && flocp->filenm)
-    fmtconcat ("%s:%lu: ", flocp->filenm, flocp->lineno);
+    sprintf (p, "%s:%lu: ", flocp->filenm, flocp->lineno);
   else if (makelevel == 0)
-    fmtconcat ("%s: ", program);
+    sprintf (p, "%s: ", program);
   else
-    fmtconcat ("%s[%u]: ", program, makelevel);
+    sprintf (p, "%s[%u]: ", program, makelevel);
+  p += strlen (p);
 
   va_start (args, fmt);
-  vfmtconcat (fmt, args);
+  vsprintf (p, fmt, args);
   va_end (args);
 
-  fmtconcat ("\n");
+  strcat (p, "\n");
 
+  assert (fmtbuf.buffer[len] == '\0');
   outputs (1, fmtbuf.buffer);
 }
 
 /* Print an error message and exit.  */
 
 void
-fatal (const gmk_floc *flocp, const char *fmt, ...)
+fatal (const gmk_floc *flocp, size_t len, const char *fmt, ...)
 {
   va_list args;
+  const char *stop = _(".  Stop.\n");
+  char *p;
 
-  assert (fmt != NULL);
-
-  fmtbuf.len = 0;
+  len += (strlen (fmt) + strlen (program)
+          + (flocp && flocp->filenm ? strlen (flocp->filenm) : 0)
+          + INTSTR_LENGTH + 8 + strlen (stop) + 1);
+  p = get_buffer (len);
 
   if (flocp && flocp->filenm)
-    fmtconcat ("%s:%lu: *** ", flocp->filenm, flocp->lineno);
+    sprintf (p, "%s:%lu: *** ", flocp->filenm, flocp->lineno);
   else if (makelevel == 0)
-    fmtconcat ("%s: *** ", program);
+    sprintf (p, "%s: *** ", program);
   else
-    fmtconcat ("%s[%u]: *** ", program, makelevel);
+    sprintf (p, "%s[%u]: *** ", program, makelevel);
+  p += strlen (p);
 
   va_start (args, fmt);
-  vfmtconcat (fmt, args);
+  vsprintf (p, fmt, args);
   va_end (args);
 
-  fmtconcat (_(".  Stop.\n"));
+  strcat (p, stop);
+
+  assert (fmtbuf.buffer[len] == '\0');
   outputs (1, fmtbuf.buffer);
 
   die (2);
@@ -748,7 +703,8 @@
 void
 perror_with_name (const char *str, const char *name)
 {
-  error (NILF, _("%s%s: %s"), str, name, strerror (errno));
+  const char *err = strerror (errno);
+  OSSS (error, NILF, _("%s%s: %s"), str, name, err);
 }
 
 /* Print an error message from errno and exit.  */
@@ -756,7 +712,8 @@
 void
 pfatal_with_name (const char *name)
 {
-  fatal (NILF, _("%s: %s"), name, strerror (errno));
+  const char *err = strerror (errno);
+  OSS (fatal, NILF, _("%s: %s"), name, err);
 
   /* NOTREACHED */
 }
diff --git a/read.c b/read.c
index 2966ebe..663ffe8 100644
--- a/read.c
+++ b/read.c
@@ -368,7 +368,10 @@
     case ENFILE:
 #endif
     case ENOMEM:
-      fatal (reading_file, "%s", strerror (makefile_errno));
+      {
+        const char *err = strerror (makefile_errno);
+        OS (fatal, reading_file, "%s", err);
+      }
     }
 
   /* If the makefile wasn't found and it's either a makefile from
@@ -783,7 +786,7 @@
         if (i != -2)
           {
             if (i == -1)
-              fatal (fstart, _("invalid syntax in conditional"));
+              O (fatal, fstart, _("invalid syntax in conditional"));
 
             ignoring = i;
             continue;
@@ -912,7 +915,10 @@
                                   | (noerror ? RM_DONTCARE : 0)
                                   | (set_default ? 0 : RM_NO_DEFAULT_GOAL)));
               if (!r && !noerror)
-                error (fstart, "%s: %s", name, strerror (errno));
+                {
+                  const char *err = strerror (errno);
+                  OSS (error, fstart, "%s: %s", name, err);
+                }
             }
 
           /* Restore conditional state.  */
@@ -958,7 +964,7 @@
               /* Load the file.  0 means failure.  */
               r = load_file (&ebuf->floc, &name, noerror);
               if (! r && ! noerror)
-                fatal (&ebuf->floc, _("%s: failed to load"), name);
+                OS (fatal, &ebuf->floc, _("%s: failed to load"), name);
 
               free_ns (files);
               files = next;
@@ -984,7 +990,7 @@
          was no preceding target, and the line might have been usable as a
          variable definition.  But now we know it is definitely lossage.  */
       if (line[0] == cmd_prefix)
-        fatal (fstart, _("recipe commences before first target"));
+        O (fatal, fstart, _("recipe commences before first target"));
 
       /* This line describes some target files.  This is complicated by
          the existence of target-specific variables, because we can't
@@ -1033,7 +1039,7 @@
           {
           case w_eol:
             if (cmdleft != 0)
-              fatal (fstart, _("missing rule before recipe"));
+              O (fatal, fstart, _("missing rule before recipe"));
             /* This line contained something but turned out to be nothing
                but whitespace (a comment?).  */
             continue;
@@ -1123,9 +1129,9 @@
             /* There's no need to be ivory-tower about this: check for
                one of the most common bugs found in makefiles...  */
             if (cmd_prefix == '\t' && !strneq (line, "        ", 8))
-              fatal (fstart, _("missing separator (did you mean TAB instead of 8 spaces?)"));
+              O (fatal, fstart, _("missing separator (did you mean TAB instead of 8 spaces?)"));
             else
-              fatal (fstart, _("missing separator"));
+              O (fatal, fstart, _("missing separator"));
           }
 
         /* Make the colon the end-of-string so we know where to stop
@@ -1262,13 +1268,13 @@
                                      PARSEFS_NOGLOB);
             ++p2;
             if (target == 0)
-              fatal (fstart, _("missing target pattern"));
+              O (fatal, fstart, _("missing target pattern"));
             else if (target->next != 0)
-              fatal (fstart, _("multiple target patterns"));
+              O (fatal, fstart, _("multiple target patterns"));
             pattern_percent = find_percent_cached (&target->name);
             pattern = target->name;
             if (pattern_percent == 0)
-              fatal (fstart, _("target pattern contains no '%%'"));
+              O (fatal, fstart, _("target pattern contains no '%%'"));
             free_ns (target);
           }
         else
@@ -1390,7 +1396,7 @@
 #undef word1eq
 
   if (conditionals->if_cmds)
-    fatal (fstart, _("missing 'endif'"));
+    O (fatal, fstart, _("missing 'endif'"));
 
   /* At eof, record the last rule.  */
   record_waiting_files ();
@@ -1429,7 +1435,7 @@
   var = allocated_variable_expand (name);
   name = next_token (var);
   if (*name == '\0')
-    fatal (&ebuf->floc, _("empty variable name"));
+    O (fatal, &ebuf->floc, _("empty variable name"));
   p = name + strlen (name) - 1;
   while (p > name && isblank ((unsigned char)*p))
     --p;
@@ -1464,7 +1470,7 @@
   else
     {
       if (var.value[0] != '\0')
-        error (&defstart, _("extraneous text after 'define' directive"));
+        O (error, &defstart, _("extraneous text after 'define' directive"));
 
       /* Chop the string before the assignment token to get the name.  */
       var.name[var.length] = '\0';
@@ -1474,7 +1480,7 @@
   n = allocated_variable_expand (name);
   name = next_token (n);
   if (name[0] == '\0')
-    fatal (&defstart, _("empty variable name"));
+    O (fatal, &defstart, _("empty variable name"));
   p = name + strlen (name) - 1;
   while (p > name && isblank ((unsigned char)*p))
     --p;
@@ -1489,7 +1495,7 @@
 
       /* If there is nothing left to be eval'd, there's no 'endef'!!  */
       if (nlines < 0)
-        fatal (&defstart, _("missing 'endef', unterminated 'define'"));
+        O (fatal, &defstart, _("missing 'endef', unterminated 'define'"));
 
       ebuf->floc.lineno += nlines;
       line = ebuf->buffer;
@@ -1516,8 +1522,8 @@
               p += 5;
               remove_comments (p);
               if (*(next_token (p)) != '\0')
-                error (&ebuf->floc,
-                       _("extraneous text after 'endef' directive"));
+                O (error, &ebuf->floc,
+                   _("extraneous text after 'endef' directive"));
 
               if (--nlevels == 0)
                 break;
@@ -1588,16 +1594,17 @@
   /* Found one: skip past it and any whitespace after it.  */
   line = next_token (line + len);
 
-#define EXTRANEOUS() error (flocp, _("extraneous text after '%s' directive"), cmdname)
+#define EXTRATEXT() OS (error, flocp, _("extraneous text after '%s' directive"), cmdname)
+#define EXTRACMD()  OS (fatal, flocp, _("extraneous '%s'"), cmdname)
 
   /* An 'endif' cannot contain extra text, and reduces the if-depth by 1  */
   if (cmdtype == c_endif)
     {
       if (*line != '\0')
-        EXTRANEOUS ();
+        EXTRATEXT ();
 
       if (!conditionals->if_cmds)
-        fatal (flocp, _("extraneous '%s'"), cmdname);
+        EXTRACMD ();
 
       --conditionals->if_cmds;
 
@@ -1611,12 +1618,12 @@
       const char *p;
 
       if (!conditionals->if_cmds)
-        fatal (flocp, _("extraneous '%s'"), cmdname);
+        EXTRACMD ();
 
       o = conditionals->if_cmds - 1;
 
       if (conditionals->seen_else[o])
-        fatal (flocp, _("only one 'else' per conditional"));
+        O (fatal, flocp, _("only one 'else' per conditional"));
 
       /* Change the state of ignorance.  */
       switch (conditionals->ignoring[o])
@@ -1649,7 +1656,7 @@
       /* If it's 'else' or 'endif' or an illegal conditional, fail.  */
       if (word1eq ("else") || word1eq ("endif")
           || conditional_line (line, len, flocp) < 0)
-        EXTRANEOUS ();
+        EXTRATEXT ();
       else
         {
           /* conditional_line() created a new level of conditional.
@@ -1806,7 +1813,7 @@
       *line = '\0';
       line = next_token (++line);
       if (*line != '\0')
-        EXTRANEOUS ();
+        EXTRATEXT ();
 
       s2 = variable_expand (s2);
       conditionals->ignoring[o] = (streq (s1, s2) == (cmdtype == c_ifneq));
@@ -1891,7 +1898,7 @@
           current_variable_set_list = f->variables;
           v = try_variable_definition (flocp, defn, origin, 1);
           if (!v)
-            fatal (flocp, _("Malformed target-specific variable definition"));
+            O (fatal, flocp, _("Malformed target-specific variable definition"));
           current_variable_set_list = global;
         }
 
@@ -1950,7 +1957,7 @@
      at this time, since they won't get snapped and we'll get core dumps.
      See Savannah bug # 12124.  */
   if (snapped_deps)
-    fatal (flocp, _("prerequisites cannot be defined in recipes"));
+    O (fatal, flocp, _("prerequisites cannot be defined in recipes"));
 
   /* Determine if this is a pattern rule or not.  */
   name = filenames->name;
@@ -2008,7 +2015,7 @@
       unsigned int c;
 
       if (pattern != 0)
-        fatal (flocp, _("mixed implicit and static pattern rules"));
+        O (fatal, flocp, _("mixed implicit and static pattern rules"));
 
       /* Count the targets to create an array of target names.
          We already have the first one.  */
@@ -2031,7 +2038,7 @@
           implicit_percent = find_percent_cached (&name);
 
           if (implicit_percent == 0)
-            fatal (flocp, _("mixed implicit and normal rules"));
+            O (fatal, flocp, _("mixed implicit and normal rules"));
 
           targets[c] = name;
           target_pats[c] = implicit_percent;
@@ -2083,7 +2090,8 @@
          'targets: target%pattern: prereq%pattern; recipe',
          make sure the pattern matches this target name.  */
       if (pattern && !pattern_matches (pattern, pattern_percent, name))
-        error (flocp, _("target '%s' doesn't match the target pattern"), name);
+        OS (error, flocp,
+            _("target '%s' doesn't match the target pattern"), name);
       else if (deps)
         /* If there are multiple targets, copy the chain DEPS for all but the
            last one.  It is not safe for the same deps to go in more than one
@@ -2097,25 +2105,26 @@
              if any.  */
           f = enter_file (strcache_add (name));
           if (f->double_colon)
-            fatal (flocp,
-                   _("target file '%s' has both : and :: entries"), f->name);
+            OS (fatal, flocp,
+                _("target file '%s' has both : and :: entries"), f->name);
 
           /* If CMDS == F->CMDS, this target was listed in this rule
              more than once.  Just give a warning since this is harmless.  */
           if (cmds != 0 && cmds == f->cmds)
-            error (flocp,
-                   _("target '%s' given more than once in the same rule"),
-                   f->name);
+            OS (error, flocp,
+                _("target '%s' given more than once in the same rule"),
+                f->name);
 
           /* Check for two single-colon entries both with commands.
              Check is_target so that we don't lose on files such as .c.o
              whose commands were preinitialized.  */
           else if (cmds != 0 && f->cmds != 0 && f->is_target)
             {
-              error (&cmds->fileinfo,
+              size_t l = strlen (f->name);
+              error (&cmds->fileinfo, l,
                      _("warning: overriding recipe for target '%s'"),
                      f->name);
-              error (&f->cmds->fileinfo,
+              error (&f->cmds->fileinfo, l,
                      _("warning: ignoring old recipe for target '%s'"),
                      f->name);
             }
@@ -2142,8 +2151,8 @@
           /* Check for both : and :: rules.  Check is_target so we don't lose
              on default suffix rules or makefiles.  */
           if (f != 0 && f->is_target && !f->double_colon)
-            fatal (flocp,
-                   _("target file '%s' has both : and :: entries"), f->name);
+            OS (fatal, flocp,
+                _("target file '%s' has both : and :: entries"), f->name);
 
           f = enter_file (strcache_add (name));
           /* If there was an existing entry and it was a double-colon entry,
@@ -2219,7 +2228,8 @@
       /* Reduce escaped percents.  If there are any unescaped it's an error  */
       name = filenames->name;
       if (find_percent_cached (&name))
-        error (flocp, _("*** mixed implicit and normal rules: deprecated syntax"));
+        O (error, flocp,
+           _("*** mixed implicit and normal rules: deprecated syntax"));
     }
 }
 
@@ -2528,8 +2538,8 @@
              lossage strikes again!  (xmkmf puts NULs in its makefiles.)
              There is nothing really to be done; we synthesize a newline so
              the following line doesn't appear to be part of this line.  */
-          error (&ebuf->floc,
-                 _("warning: NUL character seen; rest of line ignored"));
+          O (error, &ebuf->floc,
+             _("warning: NUL character seen; rest of line ignored"));
           p[0] = '\n';
           len = 1;
         }
@@ -3271,7 +3281,7 @@
         switch (glob (name, GLOB_NOSORT|GLOB_ALTDIRFUNC, NULL, &gl))
           {
           case GLOB_NOSPACE:
-            fatal (NILF, _("virtual memory exhausted"));
+            OUT_OF_MEM();
 
           case 0:
             /* Success.  */
diff --git a/remake.c b/remake.c
index 138cdc6..ff22277 100644
--- a/remake.c
+++ b/remake.c
@@ -228,10 +228,10 @@
                   && file->update_status == us_success && !g->changed
                   /* Never give a message under -s or -q.  */
                   && !silent_flag && !question_flag)
-                message (1, ((file->phony || file->cmds == 0)
-                             ? _("Nothing to be done for '%s'.")
-                             : _("'%s' is up to date.")),
-                         file->name);
+                OS (message, 1, ((file->phony || file->cmds == 0)
+                                 ? _("Nothing to be done for '%s'.")
+                                 : _("'%s' is up to date.")),
+                    file->name);
 
               /* This goal is finished.  Remove it from the chain.  */
               if (lastgoal == 0)
@@ -373,24 +373,30 @@
 
   if (d == 0)
     {
-      const char *msg_noparent
-        = _("%sNo rule to make target '%s'%s");
-      const char *msg_parent
-        = _("%sNo rule to make target '%s', needed by '%s'%s");
-
       /* Didn't find any dependencies to complain about. */
-      if (!keep_going_flag)
+      if (file->parent)
         {
-          if (file->parent == 0)
-            fatal (NILF, msg_noparent, "", file->name, "");
+          size_t l = strlen (file->name) + strlen (file->parent->name) + 4;
 
-          fatal (NILF, msg_parent, "", file->name, file->parent->name, "");
+          if (!keep_going_flag)
+            fatal (NILF, l,
+                   _("%sNo rule to make target '%s', needed by '%s'%s"),
+                   "", file->name, file->parent->name, "");
+
+          error (NILF, l, _("%sNo rule to make target '%s', needed by '%s'%s"),
+                 "*** ", file->name, file->parent->name, ".");
         }
-
-      if (file->parent == 0)
-        error (NILF, msg_noparent, "*** ", file->name, ".");
       else
-        error (NILF, msg_parent, "*** ", file->name, file->parent->name, ".");
+        {
+          size_t l = strlen (file->name) + 4;
+
+          if (!keep_going_flag)
+            fatal (NILF, l,
+                   _("%sNo rule to make target '%s'%s"), "", file->name, "");
+
+          error (NILF, l,
+                 _("%sNo rule to make target '%s'%s"), "*** ", file->name, ".");
+        }
 
       file->no_diag = 0;
     }
@@ -478,8 +484,9 @@
       /* Avoid spurious rebuilds due to low resolution time stamps.  */
       int ns = FILE_TIMESTAMP_NS (this_mtime);
       if (ns != 0)
-        error (NILF, _("*** Warning: .LOW_RESOLUTION_TIME file '%s' has a high resolution time stamp"),
-               file->name);
+        OS (error, NILF,
+            _("*** Warning: .LOW_RESOLUTION_TIME file '%s' has a high resolution time stamp"),
+            file->name);
       this_mtime += FILE_TIMESTAMPS_PER_S - 1 - ns;
     }
 
@@ -532,8 +539,8 @@
 
           if (is_updating (d->file))
             {
-              error (NILF, _("Circular %s <- %s dependency dropped."),
-                     file->name, d->file->name);
+              OSS (error, NILF, _("Circular %s <- %s dependency dropped."),
+                   file->name, d->file->name);
               /* We cannot free D here because our the caller will still have
                  a reference to it when we were called recursively via
                  check_dep below.  */
@@ -675,8 +682,8 @@
 
       if (depth == 0 && keep_going_flag
           && !just_print_flag && !question_flag)
-        error (NILF,
-               _("Target '%s' not remade because of errors."), file->name);
+        OS (error, NILF,
+            _("Target '%s' not remade because of errors."), file->name);
 
       return dep_status;
     }
@@ -1064,8 +1071,8 @@
 
               if (is_updating (d->file))
                 {
-                  error (NILF, _("Circular %s <- %s dependency dropped."),
-                         file->name, d->file->name);
+                  OSS (error, NILF, _("Circular %s <- %s dependency dropped."),
+                       file->name, d->file->name);
                   if (ld == 0)
                     {
                       file->deps = d->next;
@@ -1122,7 +1129,7 @@
 touch_file (struct file *file)
 {
   if (!silent_flag)
-    message (0, "touch %s", file->name);
+    OS (message, 0, "touch %s", file->name);
 
   /* Print-only (-n) takes precedence over touch (-t).  */
   if (just_print_flag)
@@ -1369,8 +1376,9 @@
           if (adjusted_now < adjusted_mtime)
             {
 #ifdef NO_FLOAT
-              error (NILF, _("Warning: File '%s' has modification time in the future"),
-                     file->name);
+              OS (error, NILF,
+                  _("Warning: File '%s' has modification time in the future"),
+                  file->name);
 #else
               double from_now =
                 (FILE_TIMESTAMP_S (mtime) - FILE_TIMESTAMP_S (now)
@@ -1382,8 +1390,9 @@
                 sprintf (from_now_string, "%lu", (unsigned long) from_now);
               else
                 sprintf (from_now_string, "%.2g", from_now);
-              error (NILF, _("Warning: File '%s' has modification time %s s in the future"),
-                     file->name, from_now_string);
+              OSS (error, NILF,
+                   _("Warning: File '%s' has modification time %s s in the future"),
+                   file->name, from_now_string);
 #endif
               clock_skew_detected = 1;
             }
@@ -1580,7 +1589,8 @@
         if (!p3)
           {
             /* Give a warning if there is no pattern.  */
-            error (NILF, _(".LIBPATTERNS element '%s' is not a pattern"), p);
+            OS (error, NILF,
+                _(".LIBPATTERNS element '%s' is not a pattern"), p);
             p[len] = c;
             continue;
           }
diff --git a/remote-cstms.c b/remote-cstms.c
index 8d6c635..a5ef99b 100644
--- a/remote-cstms.c
+++ b/remote-cstms.c
@@ -151,7 +151,7 @@
   retsock = Rpc_UdpCreate (True, 0);
   if (retsock < 0)
     {
-      error (NILF, "exporting: Couldn't create return socket.");
+      O (error, NILF, "exporting: Couldn't create return socket.");
       return 1;
     }
 
@@ -192,33 +192,35 @@
 
   host = gethostbyaddr ((char *)&permit.addr, sizeof(permit.addr), AF_INET);
 
-  if (status != RPC_SUCCESS)
-    {
-      (void) close (retsock);
-      (void) close (sock);
-      error (NILF, "exporting to %s: %s",
-             host ? host->h_name : inet_ntoa (permit.addr),
-             Rpc_ErrorMessage (status));
-      return 1;
-    }
-  else if (msg[0] != 'O' || msg[1] != 'k' || msg[2] != '\0')
-    {
-      (void) close (retsock);
-      (void) close (sock);
-      error (NILF, "exporting to %s: %s",
-             host ? host->h_name : inet_ntoa (permit.addr),
-             msg);
-      return 1;
-    }
-  else
-    {
-      error (NILF, "*** exported to %s (id %u)",
-              host ? host->h_name : inet_ntoa (permit.addr),
-              permit.id);
-    }
+  {
+    const char *hnm = host ? host->h_name : inet_ntoa (permit.addr);
+    size_t hlen = strlen (hnm);
 
-  fflush (stdout);
-  fflush (stderr);
+    if (status != RPC_SUCCESS)
+      {
+        const char *err = Rpc_ErrorMessage (status);
+        (void) close (retsock);
+        (void) close (sock);
+        error (NILF, hlen + strlen (err),
+               "exporting to %s: %s", hnm, err);
+        return 1;
+      }
+    else if (msg[0] != 'O' || msg[1] != 'k' || msg[2] != '\0')
+      {
+        (void) close (retsock);
+        (void) close (sock);
+        error (NILF, hlen + strlen (msg), "exporting to %s: %s", hnm, msg);
+        return 1;
+      }
+    else
+      {
+        error (NILF, hlen + INTSTR_LENGTH,
+               "*** exported to %s (id %u)", hnm, permit.id);
+      }
+
+    fflush (stdout);
+    fflush (stderr);
+  }
 
   pid = fork ();
   if (pid < 0)
diff --git a/rule.c b/rule.c
index cb35537..e5716c5 100644
--- a/rule.c
+++ b/rule.c
@@ -528,7 +528,7 @@
       /* This can happen if a fatal error was detected while reading the
          makefiles and thus count_implicit_rule_limits wasn't called yet.  */
       if (num_pattern_rules != 0)
-        fatal (NILF, _("BUG: num_pattern_rules is wrong!  %u != %u"),
-               num_pattern_rules, rules);
+        ONN (fatal, NILF, _("BUG: num_pattern_rules is wrong!  %u != %u"),
+             num_pattern_rules, rules);
     }
 }
diff --git a/variable.c b/variable.c
index 2ff14b6..a782305 100644
--- a/variable.c
+++ b/variable.c
@@ -1551,7 +1551,7 @@
   v->name = allocated_variable_expand (name);
 
   if (v->name[0] == '\0')
-    fatal (&v->fileinfo, _("empty variable name"));
+    O (fatal, &v->fileinfo, _("empty variable name"));
 
   return v;
 }
diff --git a/variable.h b/variable.h
index dec75b1..eda2493 100644
--- a/variable.h
+++ b/variable.h
@@ -219,10 +219,10 @@
 /* Warn that NAME is an undefined variable.  */
 
 #define warn_undefined(n,l) do{\
-                              if (warn_undefined_variables_flag) \
-                                error (reading_file, \
+                              if (warn_undefined_variables_flag)        \
+                                error (reading_file, (l),               \
                                        _("warning: undefined variable '%.*s'"), \
-                                (int)(l), (n)); \
+                                       (int)(l), (n));                  \
                               }while(0)
 
 char **target_environment (struct file *file);
diff --git a/vmsjobs.c b/vmsjobs.c
index 8bacc86..8109b5b 100644
--- a/vmsjobs.c
+++ b/vmsjobs.c
@@ -175,8 +175,8 @@
           break;
 
         default:
-          error (NILF, _("internal error: '%s' command_state"),
-                 c->file->name);
+          OS (error, NILF,
+              _("internal error: '%s' command_state"), c->file->name);
           abort ();
           break;
         }