Blame lib/fflush.c

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