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