(root)/
coreutils-9.4/
lib/
freadseek.c
       1  /* Skipping input from a FILE stream.
       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  #include <config.h>
      18  
      19  /* Specification.  */
      20  #include "freadseek.h"
      21  
      22  #include <stdlib.h>
      23  #include <unistd.h>
      24  
      25  #include "freadahead.h"
      26  #include "freadptr.h"
      27  
      28  #include "stdio-impl.h"
      29  
      30  /* Increment the in-memory pointer.  INCREMENT must be at most the buffer size
      31     returned by freadptr().
      32     This is very cheap (no system calls).  */
      33  static void
      34  freadptrinc (FILE *fp, size_t increment)
      35  {
      36    /* Keep this code in sync with freadptr!  */
      37  #if HAVE___FREADPTRINC              /* musl libc */
      38    __freadptrinc (fp, increment);
      39  #elif defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
      40    /* GNU libc, BeOS, Haiku, Linux libc5 */
      41    fp->_IO_read_ptr += increment;
      42  #elif defined __sferror || defined __DragonFly__ || defined __ANDROID__
      43    /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
      44    fp_->_p += increment;
      45    fp_->_r -= increment;
      46  #elif defined __EMX__               /* emx+gcc */
      47    fp->_ptr += increment;
      48    fp->_rcount -= increment;
      49  #elif defined __minix               /* Minix */
      50    fp_->_ptr += increment;
      51    fp_->_count -= increment;
      52  #elif defined _IOERR                /* AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, UnixWare, mingw, MSVC, NonStop Kernel, OpenVMS */
      53    fp_->_ptr += increment;
      54    fp_->_cnt -= increment;
      55  #elif defined __UCLIBC__            /* uClibc */
      56  # ifdef __STDIO_BUFFERS
      57    fp->__bufpos += increment;
      58  # else
      59    abort ();
      60  # endif
      61  #elif defined __QNX__               /* QNX */
      62    fp->_Next += increment;
      63  #elif defined __MINT__              /* Atari FreeMiNT */
      64    fp->__bufp += increment;
      65  #elif defined EPLAN9                /* Plan9 */
      66    fp->rp += increment;
      67  #elif defined SLOW_BUT_NO_HACKS     /* users can define this */
      68  #else
      69   #error "Please port gnulib freadseek.c to your platform! Look at the definition of getc, getc_unlocked on your system, then report this to bug-gnulib."
      70  #endif
      71  }
      72  
      73  int
      74  freadseek (FILE *fp, size_t offset)
      75  {
      76    size_t total_buffered;
      77    int fd;
      78  
      79    if (offset == 0)
      80      return 0;
      81  
      82    /* Seek over the already read and buffered input as quickly as possible,
      83       without doing any system calls.  */
      84    total_buffered = freadahead (fp);
      85    /* This loop is usually executed at most twice: once for ungetc buffer (if
      86       present) and once for the main buffer.  */
      87    while (total_buffered > 0)
      88      {
      89        size_t buffered;
      90  
      91        if (freadptr (fp, &buffered) != NULL && buffered > 0)
      92          {
      93            size_t increment = (buffered < offset ? buffered : offset);
      94  
      95            freadptrinc (fp, increment);
      96            offset -= increment;
      97            if (offset == 0)
      98              return 0;
      99            total_buffered -= increment;
     100            if (total_buffered == 0)
     101              break;
     102          }
     103        /* Read one byte.  If we were reading from the ungetc buffer, this
     104           switches the stream back to the main buffer.  */
     105        if (fgetc (fp) == EOF)
     106          goto eof;
     107        offset--;
     108        if (offset == 0)
     109          return 0;
     110        total_buffered--;
     111      }
     112  
     113    /* Test whether the stream is seekable or not.  */
     114    fd = fileno (fp);
     115    if (fd >= 0 && lseek (fd, 0, SEEK_CUR) >= 0)
     116      {
     117        /* FP refers to a regular file.  fseek is most efficient in this case.  */
     118        return fseeko (fp, offset, SEEK_CUR);
     119      }
     120    else
     121      {
     122        /* FP is a non-seekable stream, possibly not even referring to a file
     123           descriptor.  Read OFFSET bytes explicitly and discard them.  */
     124        char buf[4096];
     125  
     126        do
     127          {
     128            size_t count = (sizeof (buf) < offset ? sizeof (buf) : offset);
     129            if (fread (buf, 1, count, fp) < count)
     130              goto eof;
     131            offset -= count;
     132          }
     133        while (offset > 0);
     134  
     135        return 0;
     136     }
     137  
     138   eof:
     139    /* EOF, or error before or while reading.  */
     140    if (ferror (fp))
     141      return EOF;
     142    else
     143      /* Encountered EOF.  */
     144      return 0;
     145  }