Change copyright notice from GPLv2+ to GPLv3+.
[gnulib.git] / lib / fwriteerror.c
1 /* Detect write error on a stream.
2    Copyright (C) 2003-2006 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2003.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include "fwriteerror.h"
22
23 #include <errno.h>
24 #include <stdbool.h>
25
26 static int
27 do_fwriteerror (FILE *fp, bool ignore_ebadf)
28 {
29   /* State to allow multiple calls to fwriteerror (stdout).  */
30   static bool stdout_closed = false;
31
32   if (fp == stdout)
33     {
34       if (stdout_closed)
35         return 0;
36
37       /* If we are closing stdout, don't attempt to do it later again.  */
38       stdout_closed = true;
39     }
40
41   /* Need to
42      1. test the error indicator of the stream,
43      2. flush the buffers both in userland and in the kernel, through fclose,
44         testing for error again.  */
45
46   /* Clear errno, so that on non-POSIX systems the caller doesn't see a
47      wrong value of errno when we return -1.  */
48   errno = 0;
49
50   if (ferror (fp))
51     {
52       if (fflush (fp))
53         goto close_preserving_errno; /* errno is set here */
54       /* The stream had an error earlier, but its errno was lost.  If the
55          error was not temporary, we can get the same errno by writing and
56          flushing one more byte.  We can do so because at this point the
57          stream's contents is garbage anyway.  */
58       if (fputc ('\0', fp) == EOF)
59         goto close_preserving_errno; /* errno is set here */
60       if (fflush (fp))
61         goto close_preserving_errno; /* errno is set here */
62       /* Give up on errno.  */
63       errno = 0;
64       goto close_preserving_errno;
65     }
66
67   if (ignore_ebadf)
68     {
69       /* We need an explicit fflush to tell whether some output was already
70          done on FP.  */
71       if (fflush (fp))
72         goto close_preserving_errno; /* errno is set here */
73       if (fclose (fp) && errno != EBADF)
74         return -1; /* errno is set here */
75     }
76   else
77     {
78       if (fclose (fp))
79         return -1; /* errno is set here */
80     }
81
82   return 0;
83
84  close_preserving_errno:
85   /* There's an error.  Nevertheless call fclose(fp), for consistency
86      with the other cases.  */
87   {
88     int saved_errno = errno;
89     fclose (fp);
90     errno = saved_errno;
91     return -1;
92   }
93 }
94
95 int
96 fwriteerror (FILE *fp)
97 {
98   return do_fwriteerror (fp, false);
99 }
100
101 int
102 fwriteerror_no_ebadf (FILE *fp)
103 {
104   return do_fwriteerror (fp, true);
105 }
106
107
108 #if TEST
109
110 /* Name of a file on which writing fails.  On systems without /dev/full,
111    you can choose a filename on a full filesystem.  */
112 #define UNWRITABLE_FILE "/dev/full"
113
114 int
115 main ()
116 {
117   static int sizes[] =
118     {
119        511,  512,  513,
120       1023, 1024, 1025,
121       2047, 2048, 2049,
122       4095, 4096, 4097,
123       8191, 8192, 8193
124     };
125   static char dummy[8193];
126   unsigned int i, j;
127
128   for (i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++)
129     {
130       size_t size = sizes[i];
131
132       for (j = 0; j < 2; j++)
133         {
134           /* Run a test depending on i and j:
135              Write size bytes and then calls fflush if j==1.  */
136           FILE *stream = fopen (UNWRITABLE_FILE, "w");
137
138           if (stream == NULL)
139             {
140               fprintf (stderr, "Test %u:%u: could not open file\n", i, j);
141               continue;
142             }
143
144           fwrite (dummy, 347, 1, stream);
145           fwrite (dummy, size - 347, 1, stream);
146           if (j)
147             fflush (stream);
148
149           if (fwriteerror (stream) == -1)
150             {
151               if (errno != ENOSPC)
152                 fprintf (stderr, "Test %u:%u: fwriteerror ok, errno = %d\n",
153                          i, j, errno);
154             }
155           else
156             fprintf (stderr, "Test %u:%u: fwriteerror found no error!\n",
157                      i, j);
158         }
159     }
160
161   return 0;
162 }
163
164 #endif