maint: update copyright
[gnulib.git] / lib / freadseek.c
index b3f2b18..5301794 100644 (file)
@@ -1,5 +1,5 @@
 /* Skipping input from a FILE stream.
-   Copyright (C) 2007-2008 Free Software Foundation, Inc.
+   Copyright (C) 2007-2014 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <unistd.h>
 
 #include "freadahead.h"
+#include "freadptr.h"
 
-int
-freadseek (FILE *fp, size_t offset)
-{
-  size_t buffered;
-  int fd;
-
-  if (offset == 0)
-    return 0;
+#include "stdio-impl.h"
 
-  /* Increment the in-memory pointer.  This is very cheap (no system calls).  */
-  buffered = freadahead (fp);
-  if (buffered > 0)
-    {
-      size_t increment = (buffered < offset ? buffered : offset);
-
-      /* Keep this code in sync with freadahead and freadptr!  */
-#if defined _IO_ferror_unlocked     /* GNU libc, BeOS */
-      fp->_IO_read_ptr += increment;
-#elif defined __sferror             /* FreeBSD, NetBSD, OpenBSD, MacOS X, Cygwin */
-      fp->_p += increment;
-      fp->_r -= increment;
-#elif defined _IOERR                /* AIX, HP-UX, IRIX, OSF/1, Solaris, mingw */
-# if defined __sun && defined _LP64 /* Solaris/{SPARC,AMD64} 64-bit */
-#  define fp_ ((struct { unsigned char *_ptr; \
-                        unsigned char *_base; \
-                        unsigned char *_end; \
-                        long _cnt; \
-                        int _file; \
-                        unsigned int _flag; \
-                      } *) fp)
-      fp_->_ptr += increment;
-      fp_->_cnt -= increment;
-# else
-      fp->_ptr += increment;
-      fp->_cnt -= increment;
-# endif
+/* Increment the in-memory pointer.  INCREMENT must be at most the buffer size
+   returned by freadptr().
+   This is very cheap (no system calls).  */
+static void
+freadptrinc (FILE *fp, size_t increment)
+{
+  /* Keep this code in sync with freadptr!  */
+#if HAVE___FREADPTRINC              /* musl libc */
+  __freadptrinc (fp, increment);
+#elif defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */
+  fp->_IO_read_ptr += increment;
+#elif defined __sferror || defined __DragonFly__ /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin */
+  fp_->_p += increment;
+  fp_->_r -= increment;
+#elif defined __EMX__               /* emx+gcc */
+  fp->_ptr += increment;
+  fp->_rcount -= increment;
+#elif defined __minix               /* Minix */
+  fp_->_ptr += increment;
+  fp_->_count -= increment;
+#elif defined _IOERR                /* AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, mingw, NonStop Kernel */
+  fp_->_ptr += increment;
+  fp_->_cnt -= increment;
 #elif defined __UCLIBC__            /* uClibc */
 # ifdef __STDIO_BUFFERS
-      fp->__bufpos += increment;
+  fp->__bufpos += increment;
 # else
-      abort ();
+  abort ();
 # endif
 #elif defined __QNX__               /* QNX */
-      fp->_Next += increment;
+  fp->_Next += increment;
+#elif defined __MINT__              /* Atari FreeMiNT */
+  fp->__bufp += increment;
+#elif defined EPLAN9                /* Plan9 */
+  fp->rp += increment;
+#elif defined SLOW_BUT_NO_HACKS     /* users can define this */
 #else
  #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."
 #endif
+}
+
+int
+freadseek (FILE *fp, size_t offset)
+{
+  size_t total_buffered;
+  int fd;
+
+  if (offset == 0)
+    return 0;
 
-      offset -= increment;
+  /* Seek over the already read and buffered input as quickly as possible,
+     without doing any system calls.  */
+  total_buffered = freadahead (fp);
+  /* This loop is usually executed at most twice: once for ungetc buffer (if
+     present) and once for the main buffer.  */
+  while (total_buffered > 0)
+    {
+      size_t buffered;
+
+      if (freadptr (fp, &buffered) != NULL && buffered > 0)
+        {
+          size_t increment = (buffered < offset ? buffered : offset);
+
+          freadptrinc (fp, increment);
+          offset -= increment;
+          if (offset == 0)
+            return 0;
+          total_buffered -= increment;
+          if (total_buffered == 0)
+            break;
+        }
+      /* Read one byte.  If we were reading from the ungetc buffer, this
+         switches the stream back to the main buffer.  */
+      if (fgetc (fp) == EOF)
+        goto eof;
+      offset--;
       if (offset == 0)
-       return 0;
+        return 0;
+      total_buffered--;
     }
 
   /* Test whether the stream is seekable or not.  */
@@ -82,30 +113,31 @@ freadseek (FILE *fp, size_t offset)
   if (fd >= 0 && lseek (fd, 0, SEEK_CUR) >= 0)
     {
       /* FP refers to a regular file.  fseek is most efficient in this case.  */
-      return fseek (fp, offset, SEEK_CUR);
+      return fseeko (fp, offset, SEEK_CUR);
     }
   else
     {
       /* FP is a non-seekable stream, possibly not even referring to a file
-        descriptor.  Read OFFSET bytes explicitly and discard them.  */
+         descriptor.  Read OFFSET bytes explicitly and discard them.  */
       char buf[4096];
 
       do
-       {
-         size_t count = (sizeof (buf) < offset ? sizeof (buf) : offset);
-         if (fread (buf, 1, count, fp) < count)
-           {
-             if (ferror (fp))
-               /* EOF, or error before or while reading.  */
-               return EOF;
-             else
-               /* Encountered EOF.  */
-               return 0;
-           }
-         offset -= count;
-       }
+        {
+          size_t count = (sizeof (buf) < offset ? sizeof (buf) : offset);
+          if (fread (buf, 1, count, fp) < count)
+            goto eof;
+          offset -= count;
+        }
       while (offset > 0);
 
       return 0;
    }
+
+ eof:
+  /* EOF, or error before or while reading.  */
+  if (ferror (fp))
+    return EOF;
+  else
+    /* Encountered EOF.  */
+    return 0;
 }