Pass test-freadseek on cygwin.
[gnulib.git] / lib / freadseek.c
1 /* Skipping input from a FILE stream.
2    Copyright (C) 2007-2008 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program 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 General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://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 "freadptr.h"
26
27 int
28 freadseek (FILE *fp, size_t offset)
29 {
30   size_t buffered;
31   int fd;
32
33   if (offset == 0)
34     return 0;
35
36   /* Increment the in-memory pointer.  This is very cheap (no system calls).  */
37   freadptr (fp, &buffered);
38   if (buffered > 0)
39     {
40       size_t increment = (buffered < offset ? buffered : offset);
41
42       /* Keep this code in sync with freadahead and freadptr!  */
43 #if defined _IO_ferror_unlocked     /* GNU libc, BeOS */
44       fp->_IO_read_ptr += increment;
45 #elif defined __sferror             /* FreeBSD, NetBSD, OpenBSD, MacOS X, Cygwin */
46       fp->_p += increment;
47       fp->_r -= increment;
48 #elif defined _IOERR                /* AIX, HP-UX, IRIX, OSF/1, Solaris, mingw */
49 # if defined __sun && defined _LP64 /* Solaris/{SPARC,AMD64} 64-bit */
50 #  define fp_ ((struct { unsigned char *_ptr; \
51                          unsigned char *_base; \
52                          unsigned char *_end; \
53                          long _cnt; \
54                          int _file; \
55                          unsigned int _flag; \
56                        } *) fp)
57       fp_->_ptr += increment;
58       fp_->_cnt -= increment;
59 # else
60       fp->_ptr += increment;
61       fp->_cnt -= increment;
62 # endif
63 #elif defined __UCLIBC__            /* uClibc */
64 # ifdef __STDIO_BUFFERS
65       fp->__bufpos += increment;
66 # else
67       abort ();
68 # endif
69 #elif defined __QNX__               /* QNX */
70       fp->_Next += increment;
71 #else
72  #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."
73 #endif
74
75       offset -= increment;
76       if (offset == 0)
77         return 0;
78     }
79
80   /* Test whether the stream is seekable or not.  */
81   fd = fileno (fp);
82   if (fd >= 0 && lseek (fd, 0, SEEK_CUR) >= 0)
83     {
84       /* FP refers to a regular file.  fseek is most efficient in this case.  */
85       return fseek (fp, offset, SEEK_CUR);
86     }
87   else
88     {
89       /* FP is a non-seekable stream, possibly not even referring to a file
90          descriptor.  Read OFFSET bytes explicitly and discard them.  */
91       char buf[4096];
92
93       do
94         {
95           size_t count = (sizeof (buf) < offset ? sizeof (buf) : offset);
96           if (fread (buf, 1, count, fp) < count)
97             {
98               if (ferror (fp))
99                 /* EOF, or error before or while reading.  */
100                 return EOF;
101               else
102                 /* Encountered EOF.  */
103                 return 0;
104             }
105           offset -= count;
106         }
107       while (offset > 0);
108
109       return 0;
110    }
111 }