From 29a94ceb76936b88e74052dcb81fe506145f6ff4 Mon Sep 17 00:00:00 2001
From: Paul Smith <psmith@gnu.org>
Date: Sat, 14 Sep 2013 20:40:30 -0400
Subject: [PATCH] [SV 33134] Don't try to close stdout when it's already
 closed.

---
 ChangeLog | 10 ++++++++++
 main.c    |  4 ----
 makeint.h |  2 --
 misc.c    | 47 ----------------------------------------------
 output.c  | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 64 insertions(+), 55 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index fdb921f6..68c771d2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2013-09-14  Paul Smith  <psmith@gnu.org>
+
+	Fix Savannah bug #33134.  Suggested by David Boyce <dsb@boyski.com>.
+
+	* misc.c (close_stdout): Move to output.c.
+	* main.c (main): Move atexit call to output_init().
+	* makeint.h: Remove close_stdout() declaration.
+	* output.c (output_init): Add close_stdout at exit only if it's	open.
+
 2013-09-14  Paul Smith  <psmith@gnu.org>
 
 	* misc.c (set_append_mode, open_tmpfd, open_tmpfile): Move to output.c.
@@ -12,6 +21,7 @@
 	(output_dump): In recurse mode print enter/leave once for the
 	whole makefile.
 	(output_init): Initialize this processes stdio as well as child's.
+
 	* vmsjobs.c: Reformat to be closer to convention.
 
 2013-09-12  Paul Smith  <psmith@gnu.org>
diff --git a/main.c b/main.c
index 4d6b5b6c..eacab286 100644
--- a/main.c
+++ b/main.c
@@ -1063,10 +1063,6 @@ main (int argc, char **argv, char **envp)
   }
 #endif
 
-#ifdef HAVE_ATEXIT
-  atexit (close_stdout);
-#endif
-
   /* Needed for OS/2 */
   initialize_main (&argc, &argv);
 
diff --git a/makeint.h b/makeint.h
index 276a13d9..dac58587 100644
--- a/makeint.h
+++ b/makeint.h
@@ -493,8 +493,6 @@ void user_access (void);
 void make_access (void);
 void child_access (void);
 
-void close_stdout (void);
-
 char *strip_whitespace (const char **begpp, const char **endpp);
 
 /* String caching  */
diff --git a/misc.c b/misc.c
index df8bd43a..d9150325 100644
--- a/misc.c
+++ b/misc.c
@@ -723,50 +723,3 @@ get_path_max (void)
   return value;
 }
 #endif
-
-
-/* This code is stolen from gnulib.
-   If/when we abandon the requirement to work with K&R compilers, we can
-   remove this (and perhaps other parts of GNU make!) and migrate to using
-   gnulib directly.
-
-   This is called only through atexit(), which means die() has already been
-   invoked.  So, call exit() here directly.  Apparently that works...?
-*/
-
-/* Close standard output, exiting with status 'exit_failure' on failure.
-   If a program writes *anything* to stdout, that program should close
-   stdout and make sure that it succeeds before exiting.  Otherwise,
-   suppose that you go to the extreme of checking the return status
-   of every function that does an explicit write to stdout.  The last
-   printf can succeed in writing to the internal stream buffer, and yet
-   the fclose(stdout) could still fail (due e.g., to a disk full error)
-   when it tries to write out that buffered data.  Thus, you would be
-   left with an incomplete output file and the offending program would
-   exit successfully.  Even calling fflush is not always sufficient,
-   since some file systems (NFS and CODA) buffer written/flushed data
-   until an actual close call.
-
-   Besides, it's wasteful to check the return value from every call
-   that writes to stdout -- just let the internal stream state record
-   the failure.  That's what the ferror test is checking below.
-
-   It's important to detect such failures and exit nonzero because many
-   tools (most notably 'make' and other build-management systems) depend
-   on being able to detect failure in other tools via their exit status.  */
-
-void
-close_stdout (void)
-{
-  int prev_fail = ferror (stdout);
-  int fclose_fail = fclose (stdout);
-
-  if (prev_fail || fclose_fail)
-    {
-      if (fclose_fail)
-        error (NILF, _("write error: %s"), strerror (errno));
-      else
-        error (NILF, _("write error"));
-      exit (EXIT_FAILURE);
-    }
-}
diff --git a/output.c b/output.c
index 2e69c6d2..64634325 100644
--- a/output.c
+++ b/output.c
@@ -178,9 +178,9 @@ set_append_mode (int fd)
 /* Semaphore for use in -j mode with output_sync. */
 static sync_handle_t sync_handle = -1;
 
-#define STREAM_OK(_s)       ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
+#define STREAM_OK(_s)    ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
 
-#define FD_NOT_EMPTY(_f)    ((_f) != OUTPUT_NONE && lseek ((_f), 0, SEEK_END) > 0)
+#define FD_NOT_EMPTY(_f) ((_f) != OUTPUT_NONE && lseek ((_f), 0, SEEK_END) > 0)
 
 /* Set up the sync handle.  Disables output_sync on error.  */
 static int
@@ -460,6 +460,53 @@ output_tmpfile (char **name, const char *template)
 }
 
 
+/* This code is stolen from gnulib.
+   If/when we abandon the requirement to work with K&R compilers, we can
+   remove this (and perhaps other parts of GNU make!) and migrate to using
+   gnulib directly.
+
+   This is called only through atexit(), which means die() has already been
+   invoked.  So, call exit() here directly.  Apparently that works...?
+*/
+
+/* Close standard output, exiting with status 'exit_failure' on failure.
+   If a program writes *anything* to stdout, that program should close
+   stdout and make sure that it succeeds before exiting.  Otherwise,
+   suppose that you go to the extreme of checking the return status
+   of every function that does an explicit write to stdout.  The last
+   printf can succeed in writing to the internal stream buffer, and yet
+   the fclose(stdout) could still fail (due e.g., to a disk full error)
+   when it tries to write out that buffered data.  Thus, you would be
+   left with an incomplete output file and the offending program would
+   exit successfully.  Even calling fflush is not always sufficient,
+   since some file systems (NFS and CODA) buffer written/flushed data
+   until an actual close call.
+
+   Besides, it's wasteful to check the return value from every call
+   that writes to stdout -- just let the internal stream state record
+   the failure.  That's what the ferror test is checking below.
+
+   It's important to detect such failures and exit nonzero because many
+   tools (most notably 'make' and other build-management systems) depend
+   on being able to detect failure in other tools via their exit status.  */
+
+static void
+close_stdout (void)
+{
+  int prev_fail = ferror (stdout);
+  int fclose_fail = fclose (stdout);
+
+  if (prev_fail || fclose_fail)
+    {
+      if (fclose_fail)
+        error (NILF, _("write error: %s"), strerror (errno));
+      else
+        error (NILF, _("write error"));
+      exit (EXIT_FAILURE);
+    }
+}
+
+
 void
 output_init (struct output *out)
 {
@@ -487,6 +534,11 @@ output_init (struct output *out)
      lose output due to overlapping writes.  */
   set_append_mode (fileno (stdout));
   set_append_mode (fileno (stderr));
+
+#ifdef HAVE_ATEXIT
+  if (STREAM_OK (stdout))
+    atexit (close_stdout);
+#endif
 }
 
 void