Coverage Report

Created: 2026-08-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wget2/lib/fflush.c
Line
Count
Source
1
/* fflush.c -- allow flushing input streams
2
   Copyright (C) 2007-2026 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
# if !defined __HAIKU__
37
0
#  define fp_ fp
38
# endif
39
40
/* Clear the stream's ungetc buffer, preserving the value of ftello (fp).  */
41
static void
42
clear_ungetc_buffer_preserving_position (FILE *fp)
43
0
{
44
0
  if (fp_->_flags & _IO_IN_BACKUP)
45
    /* _IO_free_backup_area is a bit complicated.  Simply call fseek.  */
46
0
    fseeko (fp, 0, SEEK_CUR);
47
0
}
48
49
#else
50
51
/* Clear the stream's ungetc buffer.  May modify the value of ftello (fp).  */
52
static void
53
clear_ungetc_buffer (FILE *fp)
54
{
55
# if defined __sferror || defined __DragonFly__ || defined __ANDROID__
56
  /* FreeBSD, NetBSD, OpenBSD <= 7.7, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
57
  if (HASUB (fp))
58
    {
59
      fp_->_p += fp_->_r;
60
      fp_->_r = 0;
61
    }
62
# elif defined __EMX__              /* emx+gcc */
63
  if (fp->_ungetc_count > 0)
64
    {
65
      fp->_ungetc_count = 0;
66
      fp->_rcount = - fp->_rcount;
67
    }
68
# elif defined _IOERR               /* Minix, AIX, HP-UX, Solaris, OpenServer, UnixWare, mingw, MSVC, NonStop Kernel, OpenVMS */
69
  /* Nothing to do.  */
70
# else                              /* other implementations */
71
  fseeko (fp, 0, SEEK_CUR);
72
# endif
73
}
74
75
#endif
76
77
#if ! (defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1)
78
/* GNU libc, BeOS, Haiku, Linux libc5 */
79
80
# if (defined __sferror || defined __DragonFly__ || defined __ANDROID__) && defined __SNPT
81
/* FreeBSD, NetBSD, OpenBSD <= 7.7, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
82
83
static int
84
disable_seek_optimization (FILE *fp)
85
{
86
  int saved_flags = fp_->_flags & (__SOPT | __SNPT);
87
  fp_->_flags = (fp_->_flags & ~__SOPT) | __SNPT;
88
  return saved_flags;
89
}
90
91
static void
92
restore_seek_optimization (FILE *fp, int saved_flags)
93
{
94
  fp_->_flags = (fp_->_flags & ~(__SOPT | __SNPT)) | saved_flags;
95
}
96
97
# else
98
99
static void
100
update_fpos_cache (_GL_ATTRIBUTE_MAYBE_UNUSED FILE *fp,
101
                   _GL_ATTRIBUTE_MAYBE_UNUSED off_t pos)
102
{
103
#  if defined __sferror || defined __DragonFly__ || defined __ANDROID__
104
  /* FreeBSD, NetBSD, OpenBSD <= 7.7, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
105
#   if defined __CYGWIN__ || defined __ANDROID__
106
  /* fp_->_offset is typed as an integer.  */
107
  fp_->_offset = pos;
108
#   else
109
  /* fp_->_offset is an fpos_t.  */
110
  /* Use a union, since on NetBSD, the compilation flags determine
111
     whether fpos_t is typedef'd to off_t or a struct containing a
112
     single off_t member.  */
113
  union
114
    {
115
      fpos_t f;
116
      off_t o;
117
    } u;
118
  u.o = pos;
119
  fp_->_offset = u.f;
120
#   endif
121
  fp_->_flags |= __SOFF;
122
#  endif
123
}
124
# endif
125
#endif
126
127
/* Flush all pending data on STREAM according to POSIX rules.  Both
128
   output and seekable input streams are supported.  */
129
int
130
rpl_fflush (FILE *stream)
131
38.2k
{
132
  /* When stream is NULL, POSIX and C99 only require flushing of "output
133
     streams and update streams in which the most recent operation was not
134
     input", and all implementations do this.
135
136
     When stream is "an output stream or an update stream in which the most
137
     recent operation was not input", POSIX and C99 requires that fflush
138
     writes out any buffered data, and all implementations do this.
139
140
     When stream is, however, an input stream or an update stream in
141
     which the most recent operation was input, C99 specifies nothing,
142
     and POSIX only specifies behavior if the stream is seekable.
143
     mingw, in particular, drops the input buffer, leaving the file
144
     descriptor positioned at the end of the input buffer. I.e. ftell
145
     (stream) is lost.  We don't want to call the implementation's
146
     fflush in this case.
147
148
     We test ! freading (stream) here, rather than fwriting (stream), because
149
     what we need to know is whether the stream holds a "read buffer", and on
150
     mingw this is indicated by _IOREAD, regardless of _IOWRT.  */
151
38.2k
  if (stream == NULL || ! freading (stream))
152
38.2k
    return fflush (stream);
153
154
0
#if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
155
  /* GNU libc, BeOS, Haiku, Linux libc5 */
156
157
0
  clear_ungetc_buffer_preserving_position (stream);
158
159
0
  return fflush (stream);
160
161
#else
162
  {
163
    /* What POSIX says:
164
       1) About the file-position indicator (-> fseeko, ftello):
165
          The file position indicator is incremented by fgetc() and decremented
166
          by ungetc():
167
          <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetc.html>
168
            "... the fgetc() function shall ... advance the associated file
169
             position indicator for the stream ..."
170
          <https://pubs.opengroup.org/onlinepubs/9699919799/functions/ungetc.html>
171
            "The file-position indicator is decremented by each successful
172
             call to ungetc()..."
173
       2) fflush discards bytes pushed back by ungetc:
174
          <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html>
175
            "...any characters pushed back onto the stream by ungetc()
176
             or ungetwc() that have not subsequently been read from the
177
             stream shall be discarded..."
178
          This implies implicitly: fflush does not change the file position
179
          indicator.
180
       3) Effects on the file descriptor, if the file descriptor is capable of
181
          seeking:
182
          <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html>
183
            "...the file offset of the underlying open file description shall
184
             be set to the file position of the stream..."  */
185
186
    /* POSIX does not specify fflush behavior for non-seekable input
187
       streams.  Some implementations purge unread data, some return
188
       EBADF, some do nothing.  */
189
    off_t pos = ftello (stream);
190
    if (pos == -1)
191
      {
192
        errno = EBADF;
193
        return EOF;
194
      }
195
196
    /* Clear the ungetc buffer.  */
197
    clear_ungetc_buffer (stream);
198
199
    /* To get here, we must be flushing a seekable input stream, so the
200
       semantics of fpurge are now appropriate to clear the buffer.  To
201
       avoid losing data, the lseek is also necessary.  */
202
    {
203
      int result = fpurge (stream);
204
      if (result != 0)
205
        return result;
206
    }
207
208
# if (defined __sferror || defined __DragonFly__ || defined __ANDROID__) && defined __SNPT
209
    /* FreeBSD, NetBSD, OpenBSD <= 7.7, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
210
211
    {
212
      /* Disable seek optimization for the next fseeko call.  This tells the
213
         following fseeko call to seek to the desired position directly, rather
214
         than to seek to a block-aligned boundary.  */
215
      int saved_flags = disable_seek_optimization (stream);
216
      int result = fseeko (stream, pos, SEEK_SET);
217
218
      restore_seek_optimization (stream, saved_flags);
219
      return result;
220
    }
221
222
# else
223
224
    pos = lseek (fileno (stream), pos, SEEK_SET);
225
    if (pos == -1)
226
      return EOF;
227
    /* After a successful lseek, update the file descriptor's position cache
228
       in the stream.  */
229
    update_fpos_cache (stream, pos);
230
231
    return 0;
232
233
# endif
234
  }
235
#endif
236
38.2k
}