/src/gettext-0.26/gettext-tools/libgettextpo/fwriteerror.c
Line | Count | Source |
1 | | /* Detect write error on a stream. |
2 | | Copyright (C) 2003-2006, 2008-2025 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 <https://www.gnu.org/licenses/>. */ |
17 | | |
18 | | #include <config.h> |
19 | | |
20 | | /* Specification. */ |
21 | | #include "fwriteerror.h" |
22 | | |
23 | | #include <errno.h> |
24 | | |
25 | | static int |
26 | | do_fwriteerror (FILE *fp, bool ignore_ebadf) |
27 | 0 | { |
28 | | /* State to allow multiple calls to fwriteerror (stdout). */ |
29 | 0 | static bool stdout_closed = false; |
30 | |
|
31 | 0 | if (fp == stdout) |
32 | 0 | { |
33 | 0 | if (stdout_closed) |
34 | 0 | return 0; |
35 | | |
36 | | /* If we are closing stdout, don't attempt to do it later again. */ |
37 | 0 | stdout_closed = true; |
38 | 0 | } |
39 | | |
40 | | /* This function returns an error indication if there was a previous failure |
41 | | or if fclose failed, with two exceptions: |
42 | | - Ignore an fclose failure if there was no previous error, no data |
43 | | remains to be flushed, and fclose failed with EBADF. That can |
44 | | happen when a program like cp is invoked like this 'cp a b >&-' |
45 | | (i.e., with standard output closed) and doesn't generate any |
46 | | output (hence no previous error and nothing to be flushed). |
47 | | - Ignore an fclose failure due to EPIPE. That can happen when a |
48 | | program blocks or ignores SIGPIPE, and the output pipe or socket |
49 | | has no readers now. The EPIPE tells us that we should stop writing |
50 | | to this output. That's what we are doing anyway here. |
51 | | |
52 | | Need to |
53 | | 1. test the error indicator of the stream, |
54 | | 2. flush the buffers both in userland and in the kernel, through fclose, |
55 | | testing for error again. */ |
56 | | |
57 | | /* Clear errno, so that on non-POSIX systems the caller doesn't see a |
58 | | wrong value of errno when we return -1. */ |
59 | 0 | errno = 0; |
60 | |
|
61 | 0 | if (ferror (fp)) |
62 | 0 | { |
63 | 0 | if (fflush (fp)) |
64 | 0 | goto close_preserving_errno; /* errno is set here */ |
65 | | /* The stream had an error earlier, but its errno was lost. If the |
66 | | error was not temporary, we can get the same errno by writing and |
67 | | flushing one more byte. We can do so because at this point the |
68 | | stream's contents is garbage anyway. */ |
69 | 0 | if (fputc ('\0', fp) == EOF) |
70 | 0 | goto close_preserving_errno; /* errno is set here */ |
71 | 0 | if (fflush (fp)) |
72 | 0 | goto close_preserving_errno; /* errno is set here */ |
73 | | /* Give up on errno. */ |
74 | 0 | errno = 0; |
75 | 0 | goto close_preserving_errno; |
76 | 0 | } |
77 | | |
78 | 0 | if (ignore_ebadf) |
79 | 0 | { |
80 | | /* We need an explicit fflush to tell whether some output was already |
81 | | done on FP. */ |
82 | 0 | if (fflush (fp)) |
83 | 0 | goto close_preserving_errno; /* errno is set here */ |
84 | 0 | if (fclose (fp) && errno != EBADF) |
85 | 0 | goto got_errno; /* errno is set here */ |
86 | 0 | } |
87 | 0 | else |
88 | 0 | { |
89 | 0 | if (fclose (fp)) |
90 | 0 | goto got_errno; /* errno is set here */ |
91 | 0 | } |
92 | | |
93 | 0 | return 0; |
94 | | |
95 | 0 | close_preserving_errno: |
96 | | /* There's an error. Nevertheless call fclose(fp), for consistency |
97 | | with the other cases. */ |
98 | 0 | { |
99 | 0 | int saved_errno = errno; |
100 | 0 | fclose (fp); |
101 | 0 | errno = saved_errno; |
102 | 0 | } |
103 | 0 | got_errno: |
104 | | /* There's an error. Ignore EPIPE. */ |
105 | 0 | if (errno == EPIPE) |
106 | 0 | return 0; |
107 | 0 | else |
108 | 0 | return -1; |
109 | 0 | } |
110 | | |
111 | | int |
112 | | fwriteerror (FILE *fp) |
113 | 0 | { |
114 | 0 | return do_fwriteerror (fp, false); |
115 | 0 | } |
116 | | |
117 | | int |
118 | | fwriteerror_no_ebadf (FILE *fp) |
119 | 0 | { |
120 | 0 | return do_fwriteerror (fp, true); |
121 | 0 | } |
122 | | |
123 | | |
124 | | #if TEST |
125 | | |
126 | | /* Name of a file on which writing fails. On systems without /dev/full, |
127 | | you can choose a filename on a full file system. */ |
128 | | #define UNWRITABLE_FILE "/dev/full" |
129 | | |
130 | | int |
131 | | main () |
132 | | { |
133 | | static int sizes[] = |
134 | | { |
135 | | 511, 512, 513, |
136 | | 1023, 1024, 1025, |
137 | | 2047, 2048, 2049, |
138 | | 4095, 4096, 4097, |
139 | | 8191, 8192, 8193 |
140 | | }; |
141 | | static char dummy[8193]; |
142 | | unsigned int i, j; |
143 | | |
144 | | for (i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) |
145 | | { |
146 | | size_t size = sizes[i]; |
147 | | |
148 | | for (j = 0; j < 2; j++) |
149 | | { |
150 | | /* Run a test depending on i and j: |
151 | | Write size bytes and then calls fflush if j==1. */ |
152 | | FILE *stream = fopen (UNWRITABLE_FILE, "w"); |
153 | | |
154 | | if (stream == NULL) |
155 | | { |
156 | | fprintf (stderr, "Test %u:%u: could not open file\n", i, j); |
157 | | continue; |
158 | | } |
159 | | |
160 | | fwrite (dummy, 347, 1, stream); |
161 | | fwrite (dummy, size - 347, 1, stream); |
162 | | if (j) |
163 | | fflush (stream); |
164 | | |
165 | | if (fwriteerror (stream) == -1) |
166 | | { |
167 | | if (errno != ENOSPC) |
168 | | fprintf (stderr, "Test %u:%u: fwriteerror ok, errno = %d\n", |
169 | | i, j, errno); |
170 | | } |
171 | | else |
172 | | fprintf (stderr, "Test %u:%u: fwriteerror found no error!\n", |
173 | | i, j); |
174 | | } |
175 | | } |
176 | | |
177 | | return 0; |
178 | | } |
179 | | |
180 | | #endif |