diff options
Diffstat (limited to 'gl/fflush.c')
-rw-r--r-- | gl/fflush.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/gl/fflush.c b/gl/fflush.c new file mode 100644 index 0000000..f3689b3 --- /dev/null +++ b/gl/fflush.c | |||
@@ -0,0 +1,233 @@ | |||
1 | /* fflush.c -- allow flushing input streams | ||
2 | Copyright (C) 2007-2023 Free Software Foundation, Inc. | ||
3 | |||
4 | This file is free software: you can redistribute it and/or modify | ||
5 | it under the terms of the GNU Lesser General Public License as | ||
6 | published by the Free Software Foundation; either version 2.1 of the | ||
7 | License, or (at your option) any later version. | ||
8 | |||
9 | This file is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public License | ||
15 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ | ||
16 | |||
17 | /* Written by Eric Blake. */ | ||
18 | |||
19 | #include <config.h> | ||
20 | |||
21 | /* Specification. */ | ||
22 | #include <stdio.h> | ||
23 | |||
24 | #include <errno.h> | ||
25 | #include <unistd.h> | ||
26 | |||
27 | #include "freading.h" | ||
28 | |||
29 | #include "stdio-impl.h" | ||
30 | |||
31 | #undef fflush | ||
32 | |||
33 | |||
34 | #if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 | ||
35 | /* GNU libc, BeOS, Haiku, Linux libc5 */ | ||
36 | |||
37 | /* Clear the stream's ungetc buffer, preserving the value of ftello (fp). */ | ||
38 | static void | ||
39 | clear_ungetc_buffer_preserving_position (FILE *fp) | ||
40 | { | ||
41 | if (fp->_flags & _IO_IN_BACKUP) | ||
42 | /* _IO_free_backup_area is a bit complicated. Simply call fseek. */ | ||
43 | fseeko (fp, 0, SEEK_CUR); | ||
44 | } | ||
45 | |||
46 | #else | ||
47 | |||
48 | /* Clear the stream's ungetc buffer. May modify the value of ftello (fp). */ | ||
49 | static void | ||
50 | clear_ungetc_buffer (FILE *fp) | ||
51 | { | ||
52 | # if defined __sferror || defined __DragonFly__ || defined __ANDROID__ | ||
53 | /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ | ||
54 | if (HASUB (fp)) | ||
55 | { | ||
56 | fp_->_p += fp_->_r; | ||
57 | fp_->_r = 0; | ||
58 | } | ||
59 | # elif defined __EMX__ /* emx+gcc */ | ||
60 | if (fp->_ungetc_count > 0) | ||
61 | { | ||
62 | fp->_ungetc_count = 0; | ||
63 | fp->_rcount = - fp->_rcount; | ||
64 | } | ||
65 | # elif defined _IOERR /* Minix, AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, UnixWare, mingw, MSVC, NonStop Kernel, OpenVMS */ | ||
66 | /* Nothing to do. */ | ||
67 | # else /* other implementations */ | ||
68 | fseeko (fp, 0, SEEK_CUR); | ||
69 | # endif | ||
70 | } | ||
71 | |||
72 | #endif | ||
73 | |||
74 | #if ! (defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1) | ||
75 | /* GNU libc, BeOS, Haiku, Linux libc5 */ | ||
76 | |||
77 | # if (defined __sferror || defined __DragonFly__ || defined __ANDROID__) && defined __SNPT | ||
78 | /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ | ||
79 | |||
80 | static int | ||
81 | disable_seek_optimization (FILE *fp) | ||
82 | { | ||
83 | int saved_flags = fp_->_flags & (__SOPT | __SNPT); | ||
84 | fp_->_flags = (fp_->_flags & ~__SOPT) | __SNPT; | ||
85 | return saved_flags; | ||
86 | } | ||
87 | |||
88 | static void | ||
89 | restore_seek_optimization (FILE *fp, int saved_flags) | ||
90 | { | ||
91 | fp_->_flags = (fp_->_flags & ~(__SOPT | __SNPT)) | saved_flags; | ||
92 | } | ||
93 | |||
94 | # else | ||
95 | |||
96 | static void | ||
97 | update_fpos_cache (_GL_ATTRIBUTE_MAYBE_UNUSED FILE *fp, | ||
98 | _GL_ATTRIBUTE_MAYBE_UNUSED off_t pos) | ||
99 | { | ||
100 | # if defined __sferror || defined __DragonFly__ || defined __ANDROID__ | ||
101 | /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ | ||
102 | # if defined __CYGWIN__ || defined __ANDROID__ | ||
103 | /* fp_->_offset is typed as an integer. */ | ||
104 | fp_->_offset = pos; | ||
105 | # else | ||
106 | /* fp_->_offset is an fpos_t. */ | ||
107 | /* Use a union, since on NetBSD, the compilation flags determine | ||
108 | whether fpos_t is typedef'd to off_t or a struct containing a | ||
109 | single off_t member. */ | ||
110 | union | ||
111 | { | ||
112 | fpos_t f; | ||
113 | off_t o; | ||
114 | } u; | ||
115 | u.o = pos; | ||
116 | fp_->_offset = u.f; | ||
117 | # endif | ||
118 | fp_->_flags |= __SOFF; | ||
119 | # endif | ||
120 | } | ||
121 | # endif | ||
122 | #endif | ||
123 | |||
124 | /* Flush all pending data on STREAM according to POSIX rules. Both | ||
125 | output and seekable input streams are supported. */ | ||
126 | int | ||
127 | rpl_fflush (FILE *stream) | ||
128 | { | ||
129 | /* When stream is NULL, POSIX and C99 only require flushing of "output | ||
130 | streams and update streams in which the most recent operation was not | ||
131 | input", and all implementations do this. | ||
132 | |||
133 | When stream is "an output stream or an update stream in which the most | ||
134 | recent operation was not input", POSIX and C99 requires that fflush | ||
135 | writes out any buffered data, and all implementations do this. | ||
136 | |||
137 | When stream is, however, an input stream or an update stream in | ||
138 | which the most recent operation was input, C99 specifies nothing, | ||
139 | and POSIX only specifies behavior if the stream is seekable. | ||
140 | mingw, in particular, drops the input buffer, leaving the file | ||
141 | descriptor positioned at the end of the input buffer. I.e. ftell | ||
142 | (stream) is lost. We don't want to call the implementation's | ||
143 | fflush in this case. | ||
144 | |||
145 | We test ! freading (stream) here, rather than fwriting (stream), because | ||
146 | what we need to know is whether the stream holds a "read buffer", and on | ||
147 | mingw this is indicated by _IOREAD, regardless of _IOWRT. */ | ||
148 | if (stream == NULL || ! freading (stream)) | ||
149 | return fflush (stream); | ||
150 | |||
151 | #if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 | ||
152 | /* GNU libc, BeOS, Haiku, Linux libc5 */ | ||
153 | |||
154 | clear_ungetc_buffer_preserving_position (stream); | ||
155 | |||
156 | return fflush (stream); | ||
157 | |||
158 | #else | ||
159 | { | ||
160 | /* What POSIX says: | ||
161 | 1) About the file-position indicator (-> fseeko, ftello): | ||
162 | The file position indicator is incremented by fgetc() and decremented | ||
163 | by ungetc(): | ||
164 | <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetc.html> | ||
165 | "... the fgetc() function shall ... advance the associated file | ||
166 | position indicator for the stream ..." | ||
167 | <https://pubs.opengroup.org/onlinepubs/9699919799/functions/ungetc.html> | ||
168 | "The file-position indicator is decremented by each successful | ||
169 | call to ungetc()..." | ||
170 | 2) fflush discards bytes pushed back by ungetc: | ||
171 | <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html> | ||
172 | "...any characters pushed back onto the stream by ungetc() | ||
173 | or ungetwc() that have not subsequently been read from the | ||
174 | stream shall be discarded..." | ||
175 | This implies implicitly: fflush does not change the file position | ||
176 | indicator. | ||
177 | 3) Effects on the file descriptor, if the file descriptor is capable of | ||
178 | seeking: | ||
179 | <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html> | ||
180 | "...the file offset of the underlying open file description shall | ||
181 | be set to the file position of the stream..." */ | ||
182 | |||
183 | /* POSIX does not specify fflush behavior for non-seekable input | ||
184 | streams. Some implementations purge unread data, some return | ||
185 | EBADF, some do nothing. */ | ||
186 | off_t pos = ftello (stream); | ||
187 | if (pos == -1) | ||
188 | { | ||
189 | errno = EBADF; | ||
190 | return EOF; | ||
191 | } | ||
192 | |||
193 | /* Clear the ungetc buffer. */ | ||
194 | clear_ungetc_buffer (stream); | ||
195 | |||
196 | /* To get here, we must be flushing a seekable input stream, so the | ||
197 | semantics of fpurge are now appropriate to clear the buffer. To | ||
198 | avoid losing data, the lseek is also necessary. */ | ||
199 | { | ||
200 | int result = fpurge (stream); | ||
201 | if (result != 0) | ||
202 | return result; | ||
203 | } | ||
204 | |||
205 | # if (defined __sferror || defined __DragonFly__ || defined __ANDROID__) && defined __SNPT | ||
206 | /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ | ||
207 | |||
208 | { | ||
209 | /* Disable seek optimization for the next fseeko call. This tells the | ||
210 | following fseeko call to seek to the desired position directly, rather | ||
211 | than to seek to a block-aligned boundary. */ | ||
212 | int saved_flags = disable_seek_optimization (stream); | ||
213 | int result = fseeko (stream, pos, SEEK_SET); | ||
214 | |||
215 | restore_seek_optimization (stream, saved_flags); | ||
216 | return result; | ||
217 | } | ||
218 | |||
219 | # else | ||
220 | |||
221 | pos = lseek (fileno (stream), pos, SEEK_SET); | ||
222 | if (pos == -1) | ||
223 | return EOF; | ||
224 | /* After a successful lseek, update the file descriptor's position cache | ||
225 | in the stream. */ | ||
226 | update_fpos_cache (stream, pos); | ||
227 | |||
228 | return 0; | ||
229 | |||
230 | # endif | ||
231 | } | ||
232 | #endif | ||
233 | } | ||