Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /* fflush.c -- allow flushing input streams  | 
2  |  |    Copyright (C) 2007-2025 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  | 0  | { | 
41  | 0  |   if (fp->_flags & _IO_IN_BACKUP)  | 
42  |  |     /* _IO_free_backup_area is a bit complicated.  Simply call fseek.  */  | 
43  | 0  |     fseeko (fp, 0, SEEK_CUR);  | 
44  | 0  | }  | 
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  | 0  | { | 
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  | 0  |   if (stream == NULL || ! freading (stream))  | 
149  | 0  |     return fflush (stream);  | 
150  |  |  | 
151  | 0  | #if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1  | 
152  |  |   /* GNU libc, BeOS, Haiku, Linux libc5 */  | 
153  |  |  | 
154  | 0  |   clear_ungetc_buffer_preserving_position (stream);  | 
155  |  | 
  | 
156  | 0  |   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  | 0  | }  |