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