xref: /dflybsd-src/contrib/grep/lib/close-stream.c (revision 91b9ed38d3db6a8a8ac5b66da1d43e6e331e259a)
195b7b453SJohn Marino /* Close a stream, with nicer error checking than fclose's.
295b7b453SJohn Marino 
3*09d4459fSDaniel Fojt    Copyright (C) 1998-2002, 2004, 2006-2020 Free Software Foundation, Inc.
495b7b453SJohn Marino 
595b7b453SJohn Marino    This program is free software: you can redistribute it and/or modify
695b7b453SJohn Marino    it under the terms of the GNU General Public License as published by
795b7b453SJohn Marino    the Free Software Foundation; either version 3 of the License, or
895b7b453SJohn Marino    (at your option) any later version.
995b7b453SJohn Marino 
1095b7b453SJohn Marino    This program is distributed in the hope that it will be useful,
1195b7b453SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
1295b7b453SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1395b7b453SJohn Marino    GNU General Public License for more details.
1495b7b453SJohn Marino 
1595b7b453SJohn Marino    You should have received a copy of the GNU General Public License
16*09d4459fSDaniel Fojt    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
1795b7b453SJohn Marino 
1895b7b453SJohn Marino #include <config.h>
1995b7b453SJohn Marino 
2095b7b453SJohn Marino #include "close-stream.h"
2195b7b453SJohn Marino 
2295b7b453SJohn Marino #include <errno.h>
2395b7b453SJohn Marino #include <stdbool.h>
2495b7b453SJohn Marino 
2595b7b453SJohn Marino #include "fpending.h"
2695b7b453SJohn Marino 
2795b7b453SJohn Marino #if USE_UNLOCKED_IO
2895b7b453SJohn Marino # include "unlocked-io.h"
2995b7b453SJohn Marino #endif
3095b7b453SJohn Marino 
3195b7b453SJohn Marino /* Close STREAM.  Return 0 if successful, EOF (setting errno)
3295b7b453SJohn Marino    otherwise.  A failure might set errno to 0 if the error number
3395b7b453SJohn Marino    cannot be determined.
3495b7b453SJohn Marino 
3595b7b453SJohn Marino    A failure with errno set to EPIPE may or may not indicate an error
3695b7b453SJohn Marino    situation worth signaling to the user.  See the documentation of the
3795b7b453SJohn Marino    close_stdout_set_ignore_EPIPE function for details.
3895b7b453SJohn Marino 
3995b7b453SJohn Marino    If a program writes *anything* to STREAM, that program should close
4095b7b453SJohn Marino    STREAM and make sure that it succeeds before exiting.  Otherwise,
4195b7b453SJohn Marino    suppose that you go to the extreme of checking the return status
4295b7b453SJohn Marino    of every function that does an explicit write to STREAM.  The last
4395b7b453SJohn Marino    printf can succeed in writing to the internal stream buffer, and yet
4495b7b453SJohn Marino    the fclose(STREAM) could still fail (due e.g., to a disk full error)
4595b7b453SJohn Marino    when it tries to write out that buffered data.  Thus, you would be
4695b7b453SJohn Marino    left with an incomplete output file and the offending program would
4795b7b453SJohn Marino    exit successfully.  Even calling fflush is not always sufficient,
4895b7b453SJohn Marino    since some file systems (NFS and CODA) buffer written/flushed data
4995b7b453SJohn Marino    until an actual close call.
5095b7b453SJohn Marino 
5195b7b453SJohn Marino    Besides, it's wasteful to check the return value from every call
5295b7b453SJohn Marino    that writes to STREAM -- just let the internal stream state record
5395b7b453SJohn Marino    the failure.  That's what the ferror test is checking below.  */
5495b7b453SJohn Marino 
5595b7b453SJohn Marino int
close_stream(FILE * stream)5695b7b453SJohn Marino close_stream (FILE *stream)
5795b7b453SJohn Marino {
5895b7b453SJohn Marino   const bool some_pending = (__fpending (stream) != 0);
5995b7b453SJohn Marino   const bool prev_fail = (ferror (stream) != 0);
6095b7b453SJohn Marino   const bool fclose_fail = (fclose (stream) != 0);
6195b7b453SJohn Marino 
6295b7b453SJohn Marino   /* Return an error indication if there was a previous failure or if
6395b7b453SJohn Marino      fclose failed, with one exception: ignore an fclose failure if
6495b7b453SJohn Marino      there was no previous error, no data remains to be flushed, and
6595b7b453SJohn Marino      fclose failed with EBADF.  That can happen when a program like cp
66cf28ed85SJohn Marino      is invoked like this 'cp a b >&-' (i.e., with standard output
6795b7b453SJohn Marino      closed) and doesn't generate any output (hence no previous error
6895b7b453SJohn Marino      and nothing to be flushed).  */
6995b7b453SJohn Marino 
7095b7b453SJohn Marino   if (prev_fail || (fclose_fail && (some_pending || errno != EBADF)))
7195b7b453SJohn Marino     {
7295b7b453SJohn Marino       if (! fclose_fail)
7395b7b453SJohn Marino         errno = 0;
7495b7b453SJohn Marino       return EOF;
7595b7b453SJohn Marino     }
7695b7b453SJohn Marino 
7795b7b453SJohn Marino   return 0;
7895b7b453SJohn Marino }
79