xref: /dflybsd-src/contrib/cvs-1.12/lib/closeout.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /* closeout.c - close standard output
286d7f5d3SJohn Marino 
386d7f5d3SJohn Marino    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software
486d7f5d3SJohn Marino    Foundation, Inc.
586d7f5d3SJohn Marino 
686d7f5d3SJohn Marino    This program is free software; you can redistribute it and/or modify
786d7f5d3SJohn Marino    it under the terms of the GNU General Public License as published by
886d7f5d3SJohn Marino    the Free Software Foundation; either version 2, or (at your option)
986d7f5d3SJohn Marino    any later version.
1086d7f5d3SJohn Marino 
1186d7f5d3SJohn Marino    This program is distributed in the hope that it will be useful,
1286d7f5d3SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
1386d7f5d3SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1486d7f5d3SJohn Marino    GNU General Public License for more details.
1586d7f5d3SJohn Marino 
1686d7f5d3SJohn Marino    You should have received a copy of the GNU General Public License
1786d7f5d3SJohn Marino    along with this program; if not, write to the Free Software Foundation,
1886d7f5d3SJohn Marino    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
1986d7f5d3SJohn Marino 
2086d7f5d3SJohn Marino #ifdef HAVE_CONFIG_H
2186d7f5d3SJohn Marino # include <config.h>
2286d7f5d3SJohn Marino #endif
2386d7f5d3SJohn Marino 
2486d7f5d3SJohn Marino #include "closeout.h"
2586d7f5d3SJohn Marino 
2686d7f5d3SJohn Marino #include <stdio.h>
2786d7f5d3SJohn Marino #include <stdbool.h>
2886d7f5d3SJohn Marino #include <errno.h>
2986d7f5d3SJohn Marino 
3086d7f5d3SJohn Marino #include "gettext.h"
3186d7f5d3SJohn Marino #define _(msgid) gettext (msgid)
3286d7f5d3SJohn Marino 
3386d7f5d3SJohn Marino #include "error.h"
3486d7f5d3SJohn Marino #include "exitfail.h"
3586d7f5d3SJohn Marino #include "quotearg.h"
3686d7f5d3SJohn Marino 
3786d7f5d3SJohn Marino #if USE_UNLOCKED_IO
3886d7f5d3SJohn Marino # include "unlocked-io.h"
3986d7f5d3SJohn Marino #endif
4086d7f5d3SJohn Marino 
4186d7f5d3SJohn Marino static const char *file_name;
4286d7f5d3SJohn Marino 
4386d7f5d3SJohn Marino /* Set the file name to be reported in the event an error is detected
4486d7f5d3SJohn Marino    by close_stdout.  */
4586d7f5d3SJohn Marino void
close_stdout_set_file_name(const char * file)4686d7f5d3SJohn Marino close_stdout_set_file_name (const char *file)
4786d7f5d3SJohn Marino {
4886d7f5d3SJohn Marino   file_name = file;
4986d7f5d3SJohn Marino }
5086d7f5d3SJohn Marino 
5186d7f5d3SJohn Marino /* Close standard output, exiting with status 'exit_failure' on failure.
5286d7f5d3SJohn Marino    If a program writes *anything* to stdout, that program should close
5386d7f5d3SJohn Marino    stdout and make sure that it succeeds before exiting.  Otherwise,
5486d7f5d3SJohn Marino    suppose that you go to the extreme of checking the return status
5586d7f5d3SJohn Marino    of every function that does an explicit write to stdout.  The last
5686d7f5d3SJohn Marino    printf can succeed in writing to the internal stream buffer, and yet
5786d7f5d3SJohn Marino    the fclose(stdout) could still fail (due e.g., to a disk full error)
5886d7f5d3SJohn Marino    when it tries to write out that buffered data.  Thus, you would be
5986d7f5d3SJohn Marino    left with an incomplete output file and the offending program would
6086d7f5d3SJohn Marino    exit successfully.  Even calling fflush is not always sufficient,
6186d7f5d3SJohn Marino    since some file systems (NFS and CODA) buffer written/flushed data
6286d7f5d3SJohn Marino    until an actual close call.
6386d7f5d3SJohn Marino 
6486d7f5d3SJohn Marino    Besides, it's wasteful to check the return value from every call
6586d7f5d3SJohn Marino    that writes to stdout -- just let the internal stream state record
6686d7f5d3SJohn Marino    the failure.  That's what the ferror test is checking below.
6786d7f5d3SJohn Marino 
6886d7f5d3SJohn Marino    It's important to detect such failures and exit nonzero because many
6986d7f5d3SJohn Marino    tools (most notably `make' and other build-management systems) depend
7086d7f5d3SJohn Marino    on being able to detect failure in other tools via their exit status.  */
7186d7f5d3SJohn Marino 
7286d7f5d3SJohn Marino void
close_stdout(void)7386d7f5d3SJohn Marino close_stdout (void)
7486d7f5d3SJohn Marino {
7586d7f5d3SJohn Marino   bool prev_fail = ferror (stdout);
7686d7f5d3SJohn Marino   bool none_pending = (0 == __fpending (stdout));
7786d7f5d3SJohn Marino   bool fclose_fail = fclose (stdout);
7886d7f5d3SJohn Marino 
7986d7f5d3SJohn Marino   if (prev_fail || fclose_fail)
8086d7f5d3SJohn Marino     {
8186d7f5d3SJohn Marino       int e = fclose_fail ? errno : 0;
8286d7f5d3SJohn Marino       char const *write_error;
8386d7f5d3SJohn Marino 
8486d7f5d3SJohn Marino       /* If ferror returned zero, no data remains to be flushed, and we'd
8586d7f5d3SJohn Marino 	 otherwise fail with EBADF due to a failed fclose, then assume that
8686d7f5d3SJohn Marino 	 it's ok to ignore the fclose failure.  That can happen when a
8786d7f5d3SJohn Marino 	 program like cp is invoked like this `cp a b >&-' (i.e., with
8886d7f5d3SJohn Marino 	 stdout closed) and doesn't generate any output (hence no previous
8986d7f5d3SJohn Marino 	 error and nothing to be flushed).  */
9086d7f5d3SJohn Marino       if (e == EBADF && !prev_fail && none_pending)
9186d7f5d3SJohn Marino 	return;
9286d7f5d3SJohn Marino 
9386d7f5d3SJohn Marino       write_error = _("write error");
9486d7f5d3SJohn Marino       if (file_name)
9586d7f5d3SJohn Marino 	error (exit_failure, e, "%s: %s", quotearg_colon (file_name),
9686d7f5d3SJohn Marino 	       write_error);
9786d7f5d3SJohn Marino       else
9886d7f5d3SJohn Marino 	error (exit_failure, e, "%s", write_error);
9986d7f5d3SJohn Marino     }
10086d7f5d3SJohn Marino }
101