X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Ffwriteerror.c;h=bb881a5bf3eda0b0f5a64704253cab13118804d0;hb=4779b635ef35c7b0bc4044fcb5bc746d06f158c4;hp=6ffdf526d9b112dd67fe85e3f35609f893dc8add;hpb=267a39bafd249d7eb9c37df06dc6defcf41cb343;p=gnulib.git diff --git a/lib/fwriteerror.c b/lib/fwriteerror.c index 6ffdf526d..bb881a5bf 100644 --- a/lib/fwriteerror.c +++ b/lib/fwriteerror.c @@ -1,11 +1,11 @@ /* Detect write error on a stream. - Copyright (C) 2003-2005 Free Software Foundation, Inc. + Copyright (C) 2003-2006, 2008-2013 Free Software Foundation, Inc. Written by Bruno Haible , 2003. - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,12 +13,9 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program. If not, see . */ -#if HAVE_CONFIG_H -# include -#endif +#include /* Specification. */ #include "fwriteerror.h" @@ -26,16 +23,34 @@ #include #include -int -fwriteerror (FILE *fp) +static int +do_fwriteerror (FILE *fp, bool ignore_ebadf) { /* State to allow multiple calls to fwriteerror (stdout). */ static bool stdout_closed = false; - if (fp == stdout && stdout_closed) - return 0; + if (fp == stdout) + { + if (stdout_closed) + return 0; - /* Need to + /* If we are closing stdout, don't attempt to do it later again. */ + stdout_closed = true; + } + + /* This function returns an error indication if there was a previous failure + or if fclose failed, with two exceptions: + - Ignore an fclose failure if there was no previous error, no data + remains to be flushed, and fclose failed with EBADF. That can + happen when a program like cp is invoked like this 'cp a b >&-' + (i.e., with standard output closed) and doesn't generate any + output (hence no previous error and nothing to be flushed). + - Ignore an fclose failure due to EPIPE. That can happen when a + program blocks or ignores SIGPIPE, and the output pipe or socket + has no readers now. The EPIPE tells us that we should stop writing + to this output. That's what we are doing anyway here. + + Need to 1. test the error indicator of the stream, 2. flush the buffers both in userland and in the kernel, through fclose, testing for error again. */ @@ -47,35 +62,70 @@ fwriteerror (FILE *fp) if (ferror (fp)) { if (fflush (fp)) - return -1; /* errno is set here */ + goto close_preserving_errno; /* errno is set here */ /* The stream had an error earlier, but its errno was lost. If the - error was not temporary, we can get the same errno by writing and - flushing one more byte. We can do so because at this point the - stream's contents is garbage anyway. */ + error was not temporary, we can get the same errno by writing and + flushing one more byte. We can do so because at this point the + stream's contents is garbage anyway. */ if (fputc ('\0', fp) == EOF) - return -1; /* errno is set here */ + goto close_preserving_errno; /* errno is set here */ if (fflush (fp)) - return -1; /* errno is set here */ + goto close_preserving_errno; /* errno is set here */ /* Give up on errno. */ errno = 0; - return -1; + goto close_preserving_errno; } - /* If we are closing stdout, don't attempt to do it later again. */ - if (fp == stdout) - stdout_closed = true; - - if (fclose (fp)) - return -1; /* errno is set here */ + if (ignore_ebadf) + { + /* We need an explicit fflush to tell whether some output was already + done on FP. */ + if (fflush (fp)) + goto close_preserving_errno; /* errno is set here */ + if (fclose (fp) && errno != EBADF) + goto got_errno; /* errno is set here */ + } + else + { + if (fclose (fp)) + goto got_errno; /* errno is set here */ + } return 0; + + close_preserving_errno: + /* There's an error. Nevertheless call fclose(fp), for consistency + with the other cases. */ + { + int saved_errno = errno; + fclose (fp); + errno = saved_errno; + } + got_errno: + /* There's an error. Ignore EPIPE. */ + if (errno == EPIPE) + return 0; + else + return -1; +} + +int +fwriteerror (FILE *fp) +{ + return do_fwriteerror (fp, false); +} + +int +fwriteerror_no_ebadf (FILE *fp) +{ + return do_fwriteerror (fp, true); } #if TEST /* Name of a file on which writing fails. On systems without /dev/full, - you can choose a filename on a full filesystem. */ + you can choose a filename on a full file system. */ #define UNWRITABLE_FILE "/dev/full" int @@ -97,32 +147,32 @@ main () size_t size = sizes[i]; for (j = 0; j < 2; j++) - { - /* Run a test depending on i and j: - Write size bytes and then calls fflush if j==1. */ - FILE *stream = fopen (UNWRITABLE_FILE, "w"); - - if (stream == NULL) - { - fprintf (stderr, "Test %u:%u: could not open file\n", i, j); - continue; - } - - fwrite (dummy, 347, 1, stream); - fwrite (dummy, size - 347, 1, stream); - if (j) - fflush (stream); - - if (fwriteerror (stream) == -1) - { - if (errno != ENOSPC) - fprintf (stderr, "Test %u:%u: fwriteerror ok, errno = %d\n", - i, j, errno); - } - else - fprintf (stderr, "Test %u:%u: fwriteerror found no error!\n", - i, j); - } + { + /* Run a test depending on i and j: + Write size bytes and then calls fflush if j==1. */ + FILE *stream = fopen (UNWRITABLE_FILE, "w"); + + if (stream == NULL) + { + fprintf (stderr, "Test %u:%u: could not open file\n", i, j); + continue; + } + + fwrite (dummy, 347, 1, stream); + fwrite (dummy, size - 347, 1, stream); + if (j) + fflush (stream); + + if (fwriteerror (stream) == -1) + { + if (errno != ENOSPC) + fprintf (stderr, "Test %u:%u: fwriteerror ok, errno = %d\n", + i, j, errno); + } + else + fprintf (stderr, "Test %u:%u: fwriteerror found no error!\n", + i, j); + } } return 0;