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