xref: /dflybsd-src/contrib/cvs-1.12/lib/closeout.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /* closeout.c - close standard output
2*86d7f5d3SJohn Marino 
3*86d7f5d3SJohn Marino    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software
4*86d7f5d3SJohn Marino    Foundation, Inc.
5*86d7f5d3SJohn Marino 
6*86d7f5d3SJohn Marino    This program is free software; you can redistribute it and/or modify
7*86d7f5d3SJohn Marino    it under the terms of the GNU General Public License as published by
8*86d7f5d3SJohn Marino    the Free Software Foundation; either version 2, or (at your option)
9*86d7f5d3SJohn Marino    any later version.
10*86d7f5d3SJohn Marino 
11*86d7f5d3SJohn Marino    This program is distributed in the hope that it will be useful,
12*86d7f5d3SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
13*86d7f5d3SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*86d7f5d3SJohn Marino    GNU General Public License for more details.
15*86d7f5d3SJohn Marino 
16*86d7f5d3SJohn Marino    You should have received a copy of the GNU General Public License
17*86d7f5d3SJohn Marino    along with this program; if not, write to the Free Software Foundation,
18*86d7f5d3SJohn Marino    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19*86d7f5d3SJohn Marino 
20*86d7f5d3SJohn Marino #ifdef HAVE_CONFIG_H
21*86d7f5d3SJohn Marino # include <config.h>
22*86d7f5d3SJohn Marino #endif
23*86d7f5d3SJohn Marino 
24*86d7f5d3SJohn Marino #include "closeout.h"
25*86d7f5d3SJohn Marino 
26*86d7f5d3SJohn Marino #include <stdio.h>
27*86d7f5d3SJohn Marino #include <stdbool.h>
28*86d7f5d3SJohn Marino #include <errno.h>
29*86d7f5d3SJohn Marino 
30*86d7f5d3SJohn Marino #include "gettext.h"
31*86d7f5d3SJohn Marino #define _(msgid) gettext (msgid)
32*86d7f5d3SJohn Marino 
33*86d7f5d3SJohn Marino #include "error.h"
34*86d7f5d3SJohn Marino #include "exitfail.h"
35*86d7f5d3SJohn Marino #include "quotearg.h"
36*86d7f5d3SJohn Marino 
37*86d7f5d3SJohn Marino #if USE_UNLOCKED_IO
38*86d7f5d3SJohn Marino # include "unlocked-io.h"
39*86d7f5d3SJohn Marino #endif
40*86d7f5d3SJohn Marino 
41*86d7f5d3SJohn Marino static const char *file_name;
42*86d7f5d3SJohn Marino 
43*86d7f5d3SJohn Marino /* Set the file name to be reported in the event an error is detected
44*86d7f5d3SJohn Marino    by close_stdout.  */
45*86d7f5d3SJohn Marino void
close_stdout_set_file_name(const char * file)46*86d7f5d3SJohn Marino close_stdout_set_file_name (const char *file)
47*86d7f5d3SJohn Marino {
48*86d7f5d3SJohn Marino   file_name = file;
49*86d7f5d3SJohn Marino }
50*86d7f5d3SJohn Marino 
51*86d7f5d3SJohn Marino /* Close standard output, exiting with status 'exit_failure' on failure.
52*86d7f5d3SJohn Marino    If a program writes *anything* to stdout, that program should close
53*86d7f5d3SJohn Marino    stdout and make sure that it succeeds before exiting.  Otherwise,
54*86d7f5d3SJohn Marino    suppose that you go to the extreme of checking the return status
55*86d7f5d3SJohn Marino    of every function that does an explicit write to stdout.  The last
56*86d7f5d3SJohn Marino    printf can succeed in writing to the internal stream buffer, and yet
57*86d7f5d3SJohn Marino    the fclose(stdout) could still fail (due e.g., to a disk full error)
58*86d7f5d3SJohn Marino    when it tries to write out that buffered data.  Thus, you would be
59*86d7f5d3SJohn Marino    left with an incomplete output file and the offending program would
60*86d7f5d3SJohn Marino    exit successfully.  Even calling fflush is not always sufficient,
61*86d7f5d3SJohn Marino    since some file systems (NFS and CODA) buffer written/flushed data
62*86d7f5d3SJohn Marino    until an actual close call.
63*86d7f5d3SJohn Marino 
64*86d7f5d3SJohn Marino    Besides, it's wasteful to check the return value from every call
65*86d7f5d3SJohn Marino    that writes to stdout -- just let the internal stream state record
66*86d7f5d3SJohn Marino    the failure.  That's what the ferror test is checking below.
67*86d7f5d3SJohn Marino 
68*86d7f5d3SJohn Marino    It's important to detect such failures and exit nonzero because many
69*86d7f5d3SJohn Marino    tools (most notably `make' and other build-management systems) depend
70*86d7f5d3SJohn Marino    on being able to detect failure in other tools via their exit status.  */
71*86d7f5d3SJohn Marino 
72*86d7f5d3SJohn Marino void
close_stdout(void)73*86d7f5d3SJohn Marino close_stdout (void)
74*86d7f5d3SJohn Marino {
75*86d7f5d3SJohn Marino   bool prev_fail = ferror (stdout);
76*86d7f5d3SJohn Marino   bool none_pending = (0 == __fpending (stdout));
77*86d7f5d3SJohn Marino   bool fclose_fail = fclose (stdout);
78*86d7f5d3SJohn Marino 
79*86d7f5d3SJohn Marino   if (prev_fail || fclose_fail)
80*86d7f5d3SJohn Marino     {
81*86d7f5d3SJohn Marino       int e = fclose_fail ? errno : 0;
82*86d7f5d3SJohn Marino       char const *write_error;
83*86d7f5d3SJohn Marino 
84*86d7f5d3SJohn Marino       /* If ferror returned zero, no data remains to be flushed, and we'd
85*86d7f5d3SJohn Marino 	 otherwise fail with EBADF due to a failed fclose, then assume that
86*86d7f5d3SJohn Marino 	 it's ok to ignore the fclose failure.  That can happen when a
87*86d7f5d3SJohn Marino 	 program like cp is invoked like this `cp a b >&-' (i.e., with
88*86d7f5d3SJohn Marino 	 stdout closed) and doesn't generate any output (hence no previous
89*86d7f5d3SJohn Marino 	 error and nothing to be flushed).  */
90*86d7f5d3SJohn Marino       if (e == EBADF && !prev_fail && none_pending)
91*86d7f5d3SJohn Marino 	return;
92*86d7f5d3SJohn Marino 
93*86d7f5d3SJohn Marino       write_error = _("write error");
94*86d7f5d3SJohn Marino       if (file_name)
95*86d7f5d3SJohn Marino 	error (exit_failure, e, "%s: %s", quotearg_colon (file_name),
96*86d7f5d3SJohn Marino 	       write_error);
97*86d7f5d3SJohn Marino       else
98*86d7f5d3SJohn Marino 	error (exit_failure, e, "%s", write_error);
99*86d7f5d3SJohn Marino     }
100*86d7f5d3SJohn Marino }
101