1 /*
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * and/or other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 /*
19 FUNCTION
20 <<fseek>>, <<fseeko>>---set file position
21 
22 INDEX
23 	fseek
24 INDEX
25 	fseeko
26 INDEX
27 	_fseek_r
28 INDEX
29 	_fseeko_r
30 
31 SYNOPSIS
32 	#include <stdio.h>
33 	int fseek(FILE *<[fp]>, long <[offset]>, int <[whence]>)
34 	int fseeko(FILE *<[fp]>, off_t <[offset]>, int <[whence]>)
35 	int fseek( FILE *<[fp]>,
36 	             long <[offset]>, int <[whence]>)
37 	int fseeko( FILE *<[fp]>,
38 	             off_t <[offset]>, int <[whence]>)
39 
40 DESCRIPTION
41 Objects of type <<FILE>> can have a ``position'' that records how much
42 of the file your program has already read.  Many of the <<stdio>> functions
43 depend on this position, and many change it as a side effect.
44 
45 You can use <<fseek>>/<<fseeko>> to set the position for the file identified by
46 <[fp]>.  The value of <[offset]> determines the new position, in one
47 of three ways selected by the value of <[whence]> (defined as macros
48 in `<<stdio.h>>'):
49 
50 <<SEEK_SET>>---<[offset]> is the absolute file position (an offset
51 from the beginning of the file) desired.  <[offset]> must be positive.
52 
53 <<SEEK_CUR>>---<[offset]> is relative to the current file position.
54 <[offset]> can meaningfully be either positive or negative.
55 
56 <<SEEK_END>>---<[offset]> is relative to the current end of file.
57 <[offset]> can meaningfully be either positive (to increase the size
58 of the file) or negative.
59 
60 See <<ftell>>/<<ftello>> to determine the current file position.
61 
62 RETURNS
63 <<fseek>>/<<fseeko>> return <<0>> when successful.  On failure, the
64 result is <<EOF>>.  The reason for failure is indicated in <<errno>>:
65 either <<ESPIPE>> (the stream identified by <[fp]> doesn't support
66 repositioning) or <<EINVAL>> (invalid file position).
67 
68 PORTABILITY
69 ANSI C requires <<fseek>>.
70 
71 <<fseeko>> is defined by the Single Unix specification.
72 
73 Supporting OS subroutines required: <<close>>, <<fstat>>, <<isatty>>,
74 <<lseek>>, <<read>>, <<sbrk>>, <<write>>.
75 */
76 
77 #define _DEFAULT_SOURCE
78 #include <_ansi.h>
79 #include <stdio.h>
80 #include <string.h>
81 #include <time.h>
82 #include <fcntl.h>
83 #include <stdlib.h>
84 #include <errno.h>
85 #include <sys/stat.h>
86 #include "local.h"
87 
88 #define	POS_ERR	(-(_fpos_t)1)
89 
90 /*
91  * Seek the given file to the given offset.
92  * `Whence' must be one of the three SEEK_* macros.
93  */
94 
95 int
fseeko(register FILE * fp,_off_t offset,int whence)96 fseeko (
97        register FILE *fp,
98        _off_t offset,
99        int whence)
100 {
101   _fpos_t (*seekfn) (void *, _fpos_t, int);
102 #ifdef _FSEEK_OPTIMIZATION
103   _fpos_t target;
104   size_t n;
105 #ifdef __USE_INTERNAL_STAT64
106   struct stat64 st;
107 #else
108   struct stat st;
109 #endif
110 #endif
111   _fpos_t curoff = 0;
112   int havepos;
113 
114   /* Make sure stdio is set up.  */
115 
116   CHECK_INIT (ptr, fp);
117 
118   _newlib_flockfile_start (fp);
119 
120   /* If we've been doing some writing, and we're in append mode
121      then we don't really know where the filepos is.  */
122 
123   if (fp->_flags & __SAPP && fp->_flags & __SWR)
124     {
125       /* So flush the buffer and seek to the end.  */
126       fflush ( fp);
127     }
128 
129   /* Have to be able to seek.  */
130 
131   if ((seekfn = fp->_seek) == NULL)
132     {
133       _REENT_ERRNO(ptr) = ESPIPE;	/* ??? */
134       _newlib_flockfile_exit (fp);
135       return EOF;
136     }
137 
138   /*
139    * Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
140    * After this, whence is either SEEK_SET or SEEK_END.
141    */
142 
143   switch (whence)
144     {
145     case SEEK_CUR:
146       /*
147        * In order to seek relative to the current stream offset,
148        * we have to first find the current stream offset a la
149        * ftell (see ftell for details).
150        */
151       fflush ( fp);   /* may adjust seek offset on append stream */
152       if (fp->_flags & __SOFF)
153 	curoff = fp->_offset;
154       else
155 	{
156 	  curoff = seekfn (fp->_cookie, (_fpos_t) 0, SEEK_CUR);
157 	  if (curoff == -1L)
158 	    {
159 	      _newlib_flockfile_exit (fp);
160 	      return EOF;
161 	    }
162 	}
163       if (fp->_flags & __SRD)
164 	{
165 	  curoff -= fp->_r;
166 	  if (HASUB (fp))
167 	    curoff -= fp->_ur;
168 	}
169       else if (fp->_flags & __SWR && fp->_p != NULL)
170 	curoff += fp->_p - fp->_bf._base;
171 
172       offset += curoff;
173       whence = SEEK_SET;
174       havepos = 1;
175       break;
176 
177     case SEEK_SET:
178     case SEEK_END:
179       havepos = 0;
180       break;
181 
182     default:
183       _REENT_ERRNO(ptr) = EINVAL;
184       _newlib_flockfile_exit (fp);
185       return (EOF);
186     }
187 
188   (void) havepos;
189 
190   /*
191    * Can only optimise if:
192    *	reading (and not reading-and-writing);
193    *	not unbuffered; and
194    *	this is a `regular' Unix file (and hence seekfn==__sseek).
195    * We must check __NBF first, because it is possible to have __NBF
196    * and __SOPT both set.
197    */
198 
199   if (fp->_bf._base == NULL)
200     _smakebuf ( fp);
201 
202 #ifdef _FSEEK_OPTIMIZATION
203   if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT))
204     goto dumb;
205   if ((fp->_flags & __SOPT) == 0)
206     {
207       if (seekfn != __sseek
208 	  || fp->_file < 0
209 #ifdef __USE_INTERNAL_STAT64
210 	  || _fstat64_r (ptr, fp->_file, &st)
211 #else
212 	  || fstat ( fp->_file, &st)
213 #endif
214 	  || (st.st_mode & S_IFMT) != S_IFREG)
215 	{
216 	  fp->_flags |= __SNPT;
217 	  goto dumb;
218 	}
219 #ifdef	HAVE_BLKSIZE
220       fp->_blksize = st.st_blksize;
221 #else
222       fp->_blksize = 1024;
223 #endif
224       fp->_flags |= __SOPT;
225     }
226 
227   /*
228    * We are reading; we can try to optimise.
229    * Figure out where we are going and where we are now.
230    */
231 
232   if (whence == SEEK_SET)
233     target = offset;
234   else
235     {
236 #ifdef __USE_INTERNAL_STAT64
237       if (_fstat64_r (ptr, fp->_file, &st))
238 #else
239       if (fstat ( fp->_file, &st))
240 #endif
241 	goto dumb;
242       target = st.st_size + offset;
243     }
244 
245   if (!havepos)
246     {
247       if (fp->_flags & __SOFF)
248 	curoff = fp->_offset;
249       else
250 	{
251 	  curoff = seekfn (fp->_cookie, 0L, SEEK_CUR);
252 	  if (curoff == POS_ERR)
253 	    goto dumb;
254 	}
255       curoff -= fp->_r;
256       if (HASUB (fp))
257 	curoff -= fp->_ur;
258     }
259 
260   /*
261    * Compute the number of bytes in the input buffer (pretending
262    * that any ungetc() input has been discarded).  Adjust current
263    * offset backwards by this count so that it represents the
264    * file offset for the first byte in the current input buffer.
265    */
266 
267   if (HASUB (fp))
268     {
269       curoff += fp->_r;       /* kill off ungetc */
270       n = fp->_up - fp->_bf._base;
271       curoff -= n;
272       n += fp->_ur;
273     }
274   else
275     {
276       n = fp->_p - fp->_bf._base;
277       curoff -= n;
278       n += fp->_r;
279     }
280 
281   /*
282    * If the target offset is within the current buffer,
283    * simply adjust the pointers, clear EOF, undo ungetc(),
284    * and return.
285    */
286 
287   if (target >= curoff && (size_t) target < (size_t) curoff + n)
288     {
289       register int o = target - curoff;
290 
291       fp->_p = fp->_bf._base + o;
292       fp->_r = n - o;
293       if (HASUB (fp))
294 	FREEUB (ptr, fp);
295       fp->_flags &= ~__SEOF;
296       memset (&fp->_mbstate, 0, sizeof (_mbstate_t));
297       _newlib_flockfile_exit (fp);
298       return 0;
299     }
300 
301   /*
302    * The place we want to get to is not within the current buffer,
303    * but we can still be kind to the kernel copyout mechanism.
304    * By aligning the file offset to a block boundary, we can let
305    * the kernel use the VM hardware to map pages instead of
306    * copying bytes laboriously.  Using a block boundary also
307    * ensures that we only read one block, rather than two.
308    */
309 
310   curoff = target & ~(fp->_blksize - 1);
311   if (seekfn (fp->_cookie, curoff, SEEK_SET) == POS_ERR)
312     goto dumb;
313   fp->_r = 0;
314   fp->_p = fp->_bf._base;
315   if (HASUB (fp))
316     FREEUB (ptr, fp);
317   fp->_flags &= ~__SEOF;
318   n = target - curoff;
319   if (n)
320     {
321       if (_srefill ( fp) || fp->_r < (int) n)
322 	goto dumb;
323       fp->_p += n;
324       fp->_r -= n;
325     }
326   memset (&fp->_mbstate, 0, sizeof (_mbstate_t));
327   _newlib_flockfile_exit (fp);
328   return 0;
329 
330   /*
331    * We get here if we cannot optimise the seek ... just
332    * do it.  Allow the seek function to change fp->_bf._base.
333    */
334 dumb:
335 #endif
336 
337   if (fflush ( fp)
338       || seekfn (fp->_cookie, offset, whence) == POS_ERR)
339     {
340       _newlib_flockfile_exit (fp);
341       return EOF;
342     }
343   /* success: clear EOF indicator and discard ungetc() data */
344   if (HASUB (fp))
345     FREEUB (ptr, fp);
346   fp->_p = fp->_bf._base;
347   fp->_r = 0;
348   /* fp->_w = 0; *//* unnecessary (I think...) */
349   fp->_flags &= ~__SEOF;
350   /* Reset no-optimization flag after successful seek.  The
351      no-optimization flag may be set in the case of a read
352      stream that is flushed which by POSIX/SUSv3 standards,
353      means that a corresponding seek must not optimize.  The
354      optimization is then allowed if no subsequent flush
355      is performed.  */
356   fp->_flags &= ~__SNPT;
357   memset (&fp->_mbstate, 0, sizeof (_mbstate_t));
358   _newlib_flockfile_end (fp);
359   return 0;
360 }
361