Blame lib/fflush.c

Packit 8f70b4
/* fflush.c -- allow flushing input streams
Packit 8f70b4
   Copyright (C) 2007-2018 Free Software Foundation, Inc.
Packit 8f70b4
Packit 8f70b4
   This program is free software: you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
   (at your option) any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License
Packit 8f70b4
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
/* Written by Eric Blake. */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
Packit 8f70b4
/* Specification.  */
Packit 8f70b4
#include <stdio.h>
Packit 8f70b4
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
Packit 8f70b4
#include "freading.h"
Packit 8f70b4
Packit 8f70b4
#include "stdio-impl.h"
Packit 8f70b4
Packit 8f70b4
#include "unused-parameter.h"
Packit 8f70b4
Packit 8f70b4
#undef fflush
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
#if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
Packit 8f70b4
/* GNU libc, BeOS, Haiku, Linux libc5 */
Packit 8f70b4
Packit 8f70b4
/* Clear the stream's ungetc buffer, preserving the value of ftello (fp).  */
Packit 8f70b4
static void
Packit 8f70b4
clear_ungetc_buffer_preserving_position (FILE *fp)
Packit 8f70b4
{
Packit 8f70b4
  if (fp->_flags & _IO_IN_BACKUP)
Packit 8f70b4
    /* _IO_free_backup_area is a bit complicated.  Simply call fseek.  */
Packit 8f70b4
    fseeko (fp, 0, SEEK_CUR);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#else
Packit 8f70b4
Packit 8f70b4
/* Clear the stream's ungetc buffer.  May modify the value of ftello (fp).  */
Packit 8f70b4
static void
Packit 8f70b4
clear_ungetc_buffer (FILE *fp)
Packit 8f70b4
{
Packit 8f70b4
# if defined __sferror || defined __DragonFly__ || defined __ANDROID__
Packit 8f70b4
  /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
Packit 8f70b4
  if (HASUB (fp))
Packit 8f70b4
    {
Packit 8f70b4
      fp_->_p += fp_->_r;
Packit 8f70b4
      fp_->_r = 0;
Packit 8f70b4
    }
Packit 8f70b4
# elif defined __EMX__              /* emx+gcc */
Packit 8f70b4
  if (fp->_ungetc_count > 0)
Packit 8f70b4
    {
Packit 8f70b4
      fp->_ungetc_count = 0;
Packit 8f70b4
      fp->_rcount = - fp->_rcount;
Packit 8f70b4
    }
Packit 8f70b4
# elif defined _IOERR               /* Minix, AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, mingw, MSVC, NonStop Kernel, OpenVMS */
Packit 8f70b4
  /* Nothing to do.  */
Packit 8f70b4
# else                              /* other implementations */
Packit 8f70b4
  fseeko (fp, 0, SEEK_CUR);
Packit 8f70b4
# endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#if ! (defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1)
Packit 8f70b4
/* GNU libc, BeOS, Haiku, Linux libc5 */
Packit 8f70b4
Packit 8f70b4
# if (defined __sferror || defined __DragonFly__ || defined __ANDROID__) && defined __SNPT
Packit 8f70b4
/* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
Packit 8f70b4
Packit 8f70b4
static int
Packit 8f70b4
disable_seek_optimization (FILE *fp)
Packit 8f70b4
{
Packit 8f70b4
  int saved_flags = fp_->_flags & (__SOPT | __SNPT);
Packit 8f70b4
  fp_->_flags = (fp_->_flags & ~__SOPT) | __SNPT;
Packit 8f70b4
  return saved_flags;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static void
Packit 8f70b4
restore_seek_optimization (FILE *fp, int saved_flags)
Packit 8f70b4
{
Packit 8f70b4
  fp_->_flags = (fp_->_flags & ~(__SOPT | __SNPT)) | saved_flags;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
# else
Packit 8f70b4
Packit 8f70b4
static void
Packit 8f70b4
update_fpos_cache (FILE *fp _GL_UNUSED_PARAMETER,
Packit 8f70b4
                   off_t pos _GL_UNUSED_PARAMETER)
Packit 8f70b4
{
Packit 8f70b4
#  if defined __sferror || defined __DragonFly__ || defined __ANDROID__
Packit 8f70b4
  /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
Packit 8f70b4
#   if defined __CYGWIN__
Packit 8f70b4
  /* fp_->_offset is typed as an integer.  */
Packit 8f70b4
  fp_->_offset = pos;
Packit 8f70b4
#   else
Packit 8f70b4
  /* fp_->_offset is an fpos_t.  */
Packit 8f70b4
  /* Use a union, since on NetBSD, the compilation flags determine
Packit 8f70b4
     whether fpos_t is typedef'd to off_t or a struct containing a
Packit 8f70b4
     single off_t member.  */
Packit 8f70b4
  union
Packit 8f70b4
    {
Packit 8f70b4
      fpos_t f;
Packit 8f70b4
      off_t o;
Packit 8f70b4
    } u;
Packit 8f70b4
  u.o = pos;
Packit 8f70b4
  fp_->_offset = u.f;
Packit 8f70b4
#   endif
Packit 8f70b4
  fp_->_flags |= __SOFF;
Packit 8f70b4
#  endif
Packit 8f70b4
}
Packit 8f70b4
# endif
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* Flush all pending data on STREAM according to POSIX rules.  Both
Packit 8f70b4
   output and seekable input streams are supported.  */
Packit 8f70b4
int
Packit 8f70b4
rpl_fflush (FILE *stream)
Packit 8f70b4
{
Packit 8f70b4
  /* When stream is NULL, POSIX and C99 only require flushing of "output
Packit 8f70b4
     streams and update streams in which the most recent operation was not
Packit 8f70b4
     input", and all implementations do this.
Packit 8f70b4
Packit 8f70b4
     When stream is "an output stream or an update stream in which the most
Packit 8f70b4
     recent operation was not input", POSIX and C99 requires that fflush
Packit 8f70b4
     writes out any buffered data, and all implementations do this.
Packit 8f70b4
Packit 8f70b4
     When stream is, however, an input stream or an update stream in
Packit 8f70b4
     which the most recent operation was input, C99 specifies nothing,
Packit 8f70b4
     and POSIX only specifies behavior if the stream is seekable.
Packit 8f70b4
     mingw, in particular, drops the input buffer, leaving the file
Packit 8f70b4
     descriptor positioned at the end of the input buffer. I.e. ftell
Packit 8f70b4
     (stream) is lost.  We don't want to call the implementation's
Packit 8f70b4
     fflush in this case.
Packit 8f70b4
Packit 8f70b4
     We test ! freading (stream) here, rather than fwriting (stream), because
Packit 8f70b4
     what we need to know is whether the stream holds a "read buffer", and on
Packit 8f70b4
     mingw this is indicated by _IOREAD, regardless of _IOWRT.  */
Packit 8f70b4
  if (stream == NULL || ! freading (stream))
Packit 8f70b4
    return fflush (stream);
Packit 8f70b4
Packit 8f70b4
#if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
Packit 8f70b4
  /* GNU libc, BeOS, Haiku, Linux libc5 */
Packit 8f70b4
Packit 8f70b4
  clear_ungetc_buffer_preserving_position (stream);
Packit 8f70b4
Packit 8f70b4
  return fflush (stream);
Packit 8f70b4
Packit 8f70b4
#else
Packit 8f70b4
  {
Packit 8f70b4
    /* Notes about the file-position indicator:
Packit 8f70b4
       1) The file position indicator is incremented by fgetc() and decremented
Packit 8f70b4
          by ungetc():
Packit 8f70b4
          <http://www.opengroup.org/susv3/functions/fgetc.html>
Packit 8f70b4
            "... the fgetc() function shall ... advance the associated file
Packit 8f70b4
             position indicator for the stream ..."
Packit 8f70b4
          <http://www.opengroup.org/susv3/functions/ungetc.html>
Packit 8f70b4
            "The file-position indicator is decremented by each successful
Packit 8f70b4
             call to ungetc()..."
Packit 8f70b4
       2) <http://www.opengroup.org/susv3/functions/ungetc.html> says:
Packit 8f70b4
            "The value of the file-position indicator for the stream after
Packit 8f70b4
             reading or discarding all pushed-back bytes shall be the same
Packit 8f70b4
             as it was before the bytes were pushed back."
Packit 8f70b4
          Here we are discarding all pushed-back bytes.  But more specifically,
Packit 8f70b4
       3) <http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt> says:
Packit 8f70b4
            "[After fflush(),] the file offset of the underlying open file
Packit 8f70b4
             description shall be set to the file position of the stream, and
Packit 8f70b4
             any characters pushed back onto the stream by ungetc() ... shall
Packit 8f70b4
             be discarded."  */
Packit 8f70b4
Packit 8f70b4
    /* POSIX does not specify fflush behavior for non-seekable input
Packit 8f70b4
       streams.  Some implementations purge unread data, some return
Packit 8f70b4
       EBADF, some do nothing.  */
Packit 8f70b4
    off_t pos = ftello (stream);
Packit 8f70b4
    if (pos == -1)
Packit 8f70b4
      {
Packit 8f70b4
        errno = EBADF;
Packit 8f70b4
        return EOF;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
    /* Clear the ungetc buffer.  */
Packit 8f70b4
    clear_ungetc_buffer (stream);
Packit 8f70b4
Packit 8f70b4
    /* To get here, we must be flushing a seekable input stream, so the
Packit 8f70b4
       semantics of fpurge are now appropriate to clear the buffer.  To
Packit 8f70b4
       avoid losing data, the lseek is also necessary.  */
Packit 8f70b4
    {
Packit 8f70b4
      int result = fpurge (stream);
Packit 8f70b4
      if (result != 0)
Packit 8f70b4
        return result;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
# if (defined __sferror || defined __DragonFly__ || defined __ANDROID__) && defined __SNPT
Packit 8f70b4
    /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
Packit 8f70b4
Packit 8f70b4
    {
Packit 8f70b4
      /* Disable seek optimization for the next fseeko call.  This tells the
Packit 8f70b4
         following fseeko call to seek to the desired position directly, rather
Packit 8f70b4
         than to seek to a block-aligned boundary.  */
Packit 8f70b4
      int saved_flags = disable_seek_optimization (stream);
Packit 8f70b4
      int result = fseeko (stream, pos, SEEK_SET);
Packit 8f70b4
Packit 8f70b4
      restore_seek_optimization (stream, saved_flags);
Packit 8f70b4
      return result;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
# else
Packit 8f70b4
Packit 8f70b4
    pos = lseek (fileno (stream), pos, SEEK_SET);
Packit 8f70b4
    if (pos == -1)
Packit 8f70b4
      return EOF;
Packit 8f70b4
    /* After a successful lseek, update the file descriptor's position cache
Packit 8f70b4
       in the stream.  */
Packit 8f70b4
    update_fpos_cache (stream, pos);
Packit 8f70b4
Packit 8f70b4
    return 0;
Packit 8f70b4
Packit 8f70b4
# endif
Packit 8f70b4
  }
Packit 8f70b4
#endif
Packit 8f70b4
}