fseek.c revision 85396
118099Spst/*-
218099Spst * Copyright (c) 1990, 1993
3151497Sru *	The Regents of the University of California.  All rights reserved.
418099Spst *
5151497Sru * This code is derived from software contributed to Berkeley by
6151497Sru * Chris Torek.
718099Spst *
818099Spst * Redistribution and use in source and binary forms, with or without
9104862Sru * modification, are permitted provided that the following conditions
10104862Sru * are met:
11104862Sru * 1. Redistributions of source code must retain the above copyright
1218099Spst *    notice, this list of conditions and the following disclaimer.
1379543Sru * 2. Redistributions in binary form must reproduce the above copyright
1479543Sru *    notice, this list of conditions and the following disclaimer in the
1579543Sru *    documentation and/or other materials provided with the distribution.
1679543Sru * 3. All advertising materials mentioning features or use of this software
17104862Sru *    must display the following acknowledgement:
18104862Sru *	This product includes software developed by the University of
19104862Sru *	California, Berkeley and its contributors.
20151497Sru * 4. Neither the name of the University nor the names of its contributors
21151497Sru *    may be used to endorse or promote products derived from this software
2279543Sru *    without specific prior written permission.
2379543Sru *
24151497Sru * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25151497Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2679543Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27151497Sru * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28151497Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29151497Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30151497Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31151497Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32151497Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33151497Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34151497Sru * SUCH DAMAGE.
35151497Sru */
36151497Sru
37151497Sru#if defined(LIBC_SCCS) && !defined(lint)
38151497Sru#if 0
39151497Srustatic char sccsid[] = "@(#)fseek.c	8.3 (Berkeley) 1/2/94";
40151497Sru#endif
41151497Srustatic const char rcsid[] =
42151497Sru  "$FreeBSD: head/lib/libc/stdio/fseek.c 85396 2001-10-23 23:52:11Z ache $";
43151497Sru#endif /* LIBC_SCCS and not lint */
44151497Sru
45151497Sru#include "namespace.h"
46151497Sru#include <sys/types.h>
47151497Sru#include <sys/stat.h>
48151497Sru#include <errno.h>
49104862Sru#include <fcntl.h>
50151497Sru#include <limits.h>
51104862Sru#include <stdio.h>
52104862Sru#include <stdlib.h>
53104862Sru#include "un-namespace.h"
54104862Sru#include "local.h"
55104862Sru#include "libc_private.h"
56104862Sru
57151497Sru#define	POS_ERR	(-(fpos_t)1)
58151497Sru
59151497Sruint
60151497Srufseek(fp, offset, whence)
61151497Sru	register FILE *fp;
62151497Sru	long offset;
63151497Sru	int whence;
64151497Sru{
65151497Sru	int ret;
66151497Sru	int serrno = errno;
67151497Sru
68151497Sru	/* make sure stdio is set up */
69151497Sru	if (!__sdidinit)
70151497Sru		__sinit();
71151497Sru
72151497Sru	FLOCKFILE(fp);
73151497Sru	ret = _fseeko(fp, (off_t)offset, whence, 1);
74151497Sru	FUNLOCKFILE(fp);
75151497Sru	if (ret == 0)
76151497Sru		errno = serrno;
77151497Sru	return (ret);
78151497Sru}
79151497Sru
80151497Sruint
81104862Srufseeko(fp, offset, whence)
82151497Sru	FILE *fp;
83151497Sru	off_t offset;
84151497Sru	int whence;
85104862Sru{
86104862Sru	int ret;
87104862Sru	int serrno = errno;
88104862Sru
89104862Sru	/* make sure stdio is set up */
90151497Sru	if (!__sdidinit)
91151497Sru		__sinit();
92151497Sru
93151497Sru	FLOCKFILE(fp);
94104862Sru	ret = _fseeko(fp, offset, whence, 0);
95151497Sru	FUNLOCKFILE(fp);
96104862Sru	if (ret == 0)
97104862Sru		errno = serrno;
98151497Sru	return (ret);
99104862Sru}
100104862Sru
101104862Sru/*
102104862Sru * Seek the given file to the given offset.
103104862Sru * `Whence' must be one of the three SEEK_* macros.
104104862Sru */
105104862Sruint
106104862Sru_fseeko(fp, offset, whence, ltest)
107104862Sru	FILE *fp;
108104862Sru	off_t offset;
109104862Sru	int whence;
110104862Sru	int ltest;
111104862Sru{
112104862Sru	register fpos_t (*seekfn) __P((void *, fpos_t, int));
113104862Sru	fpos_t target, curoff;
114104862Sru	size_t n;
115104862Sru	struct stat st;
11679543Sru	int havepos;
117104862Sru
118104862Sru	/*
119104862Sru	 * Have to be able to seek.
120104862Sru	 */
121104862Sru	if ((seekfn = fp->_seek) == NULL) {
122104862Sru		errno = ESPIPE;		/* historic practice */
123104862Sru		return (-1);
124104862Sru	}
125104862Sru
126104862Sru	/*
12779543Sru	 * Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
128104862Sru	 * After this, whence is either SEEK_SET or SEEK_END.
129104862Sru	 */
130151497Sru	switch (whence) {
131151497Sru
132151497Sru	case SEEK_CUR:
133151497Sru		/*
134151497Sru		 * In order to seek relative to the current stream offset,
135104862Sru		 * we have to first find the current stream offset via
136104862Sru		 * ftell (see ftell for details).
137151497Sru		 */
138151497Sru		if (_ftello(fp, &curoff))
139151497Sru			return (-1);
140151497Sru		if (curoff < 0) {
141151497Sru			/* Unspecified position because of ungetc() at 0 */
142151497Sru			errno = ESPIPE;
143151497Sru			return (-1);
144151497Sru		}
145151497Sru		if (offset > 0 && curoff > OFF_MAX - offset) {
146151497Sru			errno = EOVERFLOW;
147151497Sru			return (-1);
148151497Sru		}
149151497Sru		offset += curoff;
150151497Sru		if (offset < 0) {
151151497Sru			errno = EINVAL;
152151497Sru			return (-1);
153104862Sru		}
154151497Sru		if (ltest && offset > LONG_MAX) {
155151497Sru			errno = EOVERFLOW;
156151497Sru			return (-1);
157151497Sru		}
158151497Sru		whence = SEEK_SET;
159151497Sru		havepos = 1;
160151497Sru		break;
161104862Sru
162151497Sru	case SEEK_SET:
163151497Sru		if (offset < 0) {
164151497Sru			errno = EINVAL;
165151497Sru			return (-1);
166151497Sru		}
167151497Sru	case SEEK_END:
168151497Sru		curoff = 0;		/* XXX just to keep gcc quiet */
169151497Sru		havepos = 0;
170151497Sru		break;
171151497Sru
172151497Sru	default:
173151497Sru		errno = EINVAL;
174151497Sru		return (-1);
175151497Sru	}
176151497Sru
177151497Sru	/*
178151497Sru	 * Can only optimise if:
179151497Sru	 *	reading (and not reading-and-writing);
180151497Sru	 *	not unbuffered; and
181151497Sru	 *	this is a `regular' Unix file (and hence seekfn==__sseek).
182151497Sru	 * We must check __NBF first, because it is possible to have __NBF
183151497Sru	 * and __SOPT both set.
184151497Sru	 */
185151497Sru	if (fp->_bf._base == NULL)
186151497Sru		__smakebuf(fp);
187151497Sru	if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT))
188151497Sru		goto dumb;
189151497Sru	if ((fp->_flags & __SOPT) == 0) {
190151497Sru		if (seekfn != __sseek ||
191104862Sru		    fp->_file < 0 || _fstat(fp->_file, &st) ||
192104862Sru		    (st.st_mode & S_IFMT) != S_IFREG) {
193104862Sru			fp->_flags |= __SNPT;
194104862Sru			goto dumb;
195104862Sru		}
196151497Sru		fp->_blksize = st.st_blksize;
197104862Sru		fp->_flags |= __SOPT;
198151497Sru	}
199151497Sru
200151497Sru	/*
201151497Sru	 * We are reading; we can try to optimise.
202151497Sru	 * Figure out where we are going and where we are now.
203151497Sru	 */
204151497Sru	if (whence == SEEK_SET)
205151497Sru		target = offset;
206151497Sru	else {
207151497Sru		if (_fstat(fp->_file, &st))
208151497Sru			goto dumb;
209151497Sru		if (offset > 0 && st.st_size > OFF_MAX - offset) {
210151497Sru			errno = EOVERFLOW;
211151497Sru			return (-1);
212151497Sru		}
213151497Sru		target = st.st_size + offset;
214151497Sru		if ((off_t)target < 0) {
215151497Sru			errno = EINVAL;
216151497Sru			return (-1);
217151497Sru		}
218151497Sru		if (ltest && (off_t)target > LONG_MAX) {
219151497Sru			errno = EOVERFLOW;
220151497Sru			return (-1);
221151497Sru		}
222151497Sru	}
223151497Sru
224151497Sru	if (!havepos && _ftello(fp, &curoff))
225151497Sru		goto dumb;
226151497Sru
227151497Sru	/*
228151497Sru	 * (If the buffer was modified, we have to
229151497Sru	 * skip this; see fgetln.c.)
230151497Sru	 */
231151497Sru	if (fp->_flags & __SMOD)
232151497Sru		goto abspos;
233151497Sru
234151497Sru	/*
235151497Sru	 * Compute the number of bytes in the input buffer (pretending
236151497Sru	 * that any ungetc() input has been discarded).  Adjust current
237151497Sru	 * offset backwards by this count so that it represents the
238151497Sru	 * file offset for the first byte in the current input buffer.
239151497Sru	 */
240151497Sru	if (HASUB(fp)) {
241151497Sru		curoff += fp->_r;	/* kill off ungetc */
242151497Sru		n = fp->_extra->_up - fp->_bf._base;
243151497Sru		curoff -= n;
244151497Sru		n += fp->_ur;
245151497Sru	} else {
246151497Sru		n = fp->_p - fp->_bf._base;
247151497Sru		curoff -= n;
248151497Sru		n += fp->_r;
249151497Sru	}
250151497Sru
251151497Sru	/*
252151497Sru	 * If the target offset is within the current buffer,
253151497Sru	 * simply adjust the pointers, clear EOF, undo ungetc(),
254151497Sru	 * and return.
255151497Sru	 */
256151497Sru	if (target >= curoff && target < curoff + n) {
257151497Sru		size_t o = target - curoff;
258151497Sru
259151497Sru		fp->_p = fp->_bf._base + o;
260151497Sru		fp->_r = n - o;
261151497Sru		if (HASUB(fp))
262151497Sru			FREEUB(fp);
263151497Sru		fp->_flags &= ~__SEOF;
264151497Sru		return (0);
265151497Sru	}
266151497Sru
267151497Sruabspos:
268151497Sru	/*
269151497Sru	 * The place we want to get to is not within the current buffer,
270151497Sru	 * but we can still be kind to the kernel copyout mechanism.
271151497Sru	 * By aligning the file offset to a block boundary, we can let
272151497Sru	 * the kernel use the VM hardware to map pages instead of
273151497Sru	 * copying bytes laboriously.  Using a block boundary also
274151497Sru	 * ensures that we only read one block, rather than two.
275151497Sru	 */
276151497Sru	curoff = target & ~(fp->_blksize - 1);
277151497Sru	if (_sseek(fp, curoff, SEEK_SET) == POS_ERR)
278151497Sru		goto dumb;
279151497Sru	fp->_r = 0;
280151497Sru	fp->_p = fp->_bf._base;
281151497Sru	if (HASUB(fp))
282151497Sru		FREEUB(fp);
283151497Sru	n = target - curoff;
284151497Sru	if (n) {
285151497Sru		if (__srefill(fp) || fp->_r < n)
286104862Sru			goto dumb;
287104862Sru		fp->_p += n;
288104862Sru		fp->_r -= n;
289104862Sru	}
290151497Sru	fp->_flags &= ~__SEOF;
291104862Sru	return (0);
292151497Sru
293151497Sru	/*
294151497Sru	 * We get here if we cannot optimise the seek ... just
295151497Sru	 * do it.  Allow the seek function to change fp->_bf._base.
296151497Sru	 */
297151497Srudumb:
298151497Sru	if (__sflush(fp) || _sseek(fp, (fpos_t)offset, whence) == POS_ERR)
299151497Sru		return (-1);
300151497Sru	if (ltest && fp->_offset > LONG_MAX) {
301151497Sru		fp->_flags |= __SERR;
302151497Sru		errno = EOVERFLOW;
303151497Sru		return (-1);
304151497Sru	}
305151497Sru	/* success: clear EOF indicator and discard ungetc() data */
306151497Sru	if (HASUB(fp))
307151497Sru		FREEUB(fp);
308151497Sru	fp->_p = fp->_bf._base;
309151497Sru	fp->_r = 0;
310151497Sru	/* fp->_w = 0; */	/* unnecessary (I think...) */
311151497Sru	fp->_flags &= ~__SEOF;
312151497Sru	return (0);
313151497Sru}
314151497Sru