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  * advertising materials, and 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 ANSI_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 TRAD_SYNOPSIS
33 	#include <stdio.h>
34 
35 	int fseeko64(<[fp]>, <[offset]>, <[whence]>)
36 	FILE *<[fp]>;
37 	_off64_t <[offset]>;
38 	int <[whence]>;
39 
40 	int _fseeko64_r (<[ptr]>, <[fp]>, <[offset]>, <[whence]>)
41 	struct _reent *<[ptr]>;
42 	FILE *<[fp]>;
43 	_off64_t <[offset]>;
44 	int <[whence]>;
45 
46 DESCRIPTION
47 Objects of type <<FILE>> can have a ``position'' that records how much
48 of the file your program has already read.  Many of the <<stdio>> functions
49 depend on this position, and many change it as a side effect.
50 
51 You can use <<fseeko64>> to set the position for the file identified by
52 <[fp]> that was opened via <<fopen64>>.  The value of <[offset]> determines
53 the new position, in one of three ways selected by the value of <[whence]>
54 (defined as macros in `<<stdio.h>>'):
55 
56 <<SEEK_SET>>---<[offset]> is the absolute file position (an offset
57 from the beginning of the file) desired.  <[offset]> must be positive.
58 
59 <<SEEK_CUR>>---<[offset]> is relative to the current file position.
60 <[offset]> can meaningfully be either positive or negative.
61 
62 <<SEEK_END>>---<[offset]> is relative to the current end of file.
63 <[offset]> can meaningfully be either positive (to increase the size
64 of the file) or negative.
65 
66 See <<ftello64>> to determine the current file position.
67 
68 RETURNS
69 <<fseeko64>> returns <<0>> when successful.  On failure, the
70 result is <<EOF>>.  The reason for failure is indicated in <<errno>>:
71 either <<ESPIPE>> (the stream identified by <[fp]> doesn't support
72 repositioning or wasn't opened via <<fopen64>>) or <<EINVAL>>
73 (invalid file position).
74 
75 PORTABILITY
76 <<fseeko64>> is a glibc extension.
77 
78 Supporting OS subroutines required: <<close>>, <<fstat64>>, <<isatty>>,
79 <<lseek64>>, <<read>>, <<sbrk>>, <<write>>.
80 */
81 
82 #include <stdio.h>
83 #include <time.h>
84 #include <fcntl.h>
85 #include <stdlib.h>
86 #include <errno.h>
87 #include <sys/types.h>
88 #include <sys/stat.h>
89 #include "local64.h"
90 
91 #define	POS_ERR	(-(_fpos64_t)1)
92 
93 #ifdef __LARGE64_FILES
94 
95 /*
96  * Seek the given file to the given offset.
97  * `Whence' must be one of the three SEEK_* macros.
98  */
99 
100 _off64_t
101 _DEFUN (_fseeko64_r, (ptr, fp, offset, whence),
102      struct _reent *ptr _AND
103      register FILE *fp _AND
104      _off64_t offset _AND
105      int whence)
106 {
107   _fpos64_t _EXFUN ((*seekfn), (void *, _fpos64_t, int));
108   _fpos64_t target, curoff;
109   size_t n;
110 
111   struct stat64 st;
112   int havepos;
113 
114   _flockfile(fp);
115 
116   /* Make sure stdio is set up.  */
117 
118   CHECK_INIT (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 || !(fp->_flags & __SL64))
134     {
135       ptr->_errno = ESPIPE;	/* ??? */
136       _funlockfile(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 	      _funlockfile(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       ptr->_errno = EINVAL;
186       _funlockfile(fp);
187       return (EOF);
188     }
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   if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT))
202     goto dumb;
203   if ((fp->_flags & __SOPT) == 0)
204     {
205       if (seekfn != __sseek64
206 	  || fp->_file < 0
207 	  || _fstat64_r (ptr, fp->_file, &st)
208 	  || (st.st_mode & S_IFMT) != S_IFREG)
209 	{
210 	  fp->_flags |= __SNPT;
211 	  goto dumb;
212 	}
213 #ifdef	HAVE_BLKSIZE
214       fp->_blksize = st.st_blksize;
215 #else
216       fp->_blksize = 1024;
217 #endif
218       fp->_flags |= __SOPT;
219     }
220 
221   /*
222    * We are reading; we can try to optimise.
223    * Figure out where we are going and where we are now.
224    */
225 
226   if (whence == SEEK_SET)
227     target = offset;
228   else
229     {
230       if (_fstat64_r (ptr, fp->_file, &st))
231 	goto dumb;
232       target = st.st_size + offset;
233     }
234 
235   if (!havepos)
236     {
237       if (fp->_flags & __SOFF)
238 	curoff = fp->_offset;
239       else
240 	{
241 	  curoff = (*seekfn) (fp->_cookie, (_fpos64_t)0, SEEK_CUR);
242 	  if (curoff == POS_ERR)
243 	    goto dumb;
244 	}
245       curoff -= fp->_r;
246       if (HASUB (fp))
247 	curoff -= fp->_ur;
248     }
249 
250   /*
251    * Compute the number of bytes in the input buffer (pretending
252    * that any ungetc() input has been discarded).  Adjust current
253    * offset backwards by this count so that it represents the
254    * file offset for the first byte in the current input buffer.
255    */
256 
257   if (HASUB (fp))
258     {
259       curoff += fp->_r;       /* kill off ungetc */
260       n = fp->_up - fp->_bf._base;
261       curoff -= n;
262       n += fp->_ur;
263     }
264   else
265     {
266       n = fp->_p - fp->_bf._base;
267       curoff -= n;
268       n += fp->_r;
269     }
270 
271   /*
272    * If the target offset is within the current buffer,
273    * simply adjust the pointers, clear EOF, undo ungetc(),
274    * and return.  (If the buffer was modified, we have to
275    * skip this; see fgetline.c.)
276    */
277 
278   if ((fp->_flags & __SMOD) == 0 &&
279       target >= curoff && target < curoff + n)
280     {
281       register int o = target - curoff;
282 
283       fp->_p = fp->_bf._base + o;
284       fp->_r = n - o;
285       if (HASUB (fp))
286 	FREEUB (fp);
287       fp->_flags &= ~__SEOF;
288       _funlockfile(fp);
289       return 0;
290     }
291 
292   /*
293    * The place we want to get to is not within the current buffer,
294    * but we can still be kind to the kernel copyout mechanism.
295    * By aligning the file offset to a block boundary, we can let
296    * the kernel use the VM hardware to map pages instead of
297    * copying bytes laboriously.  Using a block boundary also
298    * ensures that we only read one block, rather than two.
299    */
300 
301   curoff = target & ~((_fpos64_t)(fp->_blksize - 1));
302   if ((*seekfn) (fp->_cookie, curoff, SEEK_SET) == POS_ERR)
303     goto dumb;
304   fp->_r = 0;
305   fp->_p = fp->_bf._base;
306   if (HASUB (fp))
307     FREEUB (fp);
308   fp->_flags &= ~__SEOF;
309   n = target - curoff;
310   if (n)
311     {
312       if (__srefill (fp) || fp->_r < n)
313 	goto dumb;
314       fp->_p += n;
315       fp->_r -= n;
316     }
317   _funlockfile(fp);
318   return 0;
319 
320   /*
321    * We get here if we cannot optimise the seek ... just
322    * do it.  Allow the seek function to change fp->_bf._base.
323    */
324 
325 dumb:
326   if (fflush (fp) || (*seekfn) (fp->_cookie, offset, whence) == POS_ERR)
327     {
328       _funlockfile(fp);
329       return EOF;
330     }
331   /* success: clear EOF indicator and discard ungetc() data */
332   if (HASUB (fp))
333     FREEUB (fp);
334   fp->_p = fp->_bf._base;
335   fp->_r = 0;
336   /* fp->_w = 0; *//* unnecessary (I think...) */
337   fp->_flags &= ~__SEOF;
338   _funlockfile(fp);
339   return 0;
340 }
341 
342 #ifndef _REENT_ONLY
343 
344 _off64_t
345 _DEFUN (fseeko64, (fp, offset, whence),
346      register FILE *fp _AND
347      _off64_t offset _AND
348      int whence)
349 {
350   return _fseeko64_r (_REENT, fp, offset, whence);
351 }
352 
353 #endif /* !_REENT_ONLY */
354 
355 #endif /* __LARGE64_FILES */
356