fseek.c revision 129583
1/*-
2 * Copyright (c) 1990, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Chris Torek.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#if defined(LIBC_SCCS) && !defined(lint)
38static char sccsid[] = "@(#)fseek.c	8.3 (Berkeley) 1/2/94";
39#endif /* LIBC_SCCS and not lint */
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/lib/libc/stdio/fseek.c 129583 2004-05-22 15:19:41Z tjr $");
42
43#include "namespace.h"
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <errno.h>
47#include <fcntl.h>
48#include <limits.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include "un-namespace.h"
52#include "local.h"
53#include "libc_private.h"
54
55#define	POS_ERR	(-(fpos_t)1)
56
57int
58fseek(fp, offset, whence)
59	FILE *fp;
60	long offset;
61	int whence;
62{
63	int ret;
64	int serrno = errno;
65
66	/* make sure stdio is set up */
67	if (!__sdidinit)
68		__sinit();
69
70	FLOCKFILE(fp);
71	ret = _fseeko(fp, (off_t)offset, whence, 1);
72	FUNLOCKFILE(fp);
73	if (ret == 0)
74		errno = serrno;
75	return (ret);
76}
77
78int
79fseeko(fp, offset, whence)
80	FILE *fp;
81	off_t offset;
82	int whence;
83{
84	int ret;
85	int serrno = errno;
86
87	/* make sure stdio is set up */
88	if (!__sdidinit)
89		__sinit();
90
91	FLOCKFILE(fp);
92	ret = _fseeko(fp, offset, whence, 0);
93	FUNLOCKFILE(fp);
94	if (ret == 0)
95		errno = serrno;
96	return (ret);
97}
98
99/*
100 * Seek the given file to the given offset.
101 * `Whence' must be one of the three SEEK_* macros.
102 */
103int
104_fseeko(fp, offset, whence, ltest)
105	FILE *fp;
106	off_t offset;
107	int whence;
108	int ltest;
109{
110	fpos_t (*seekfn)(void *, fpos_t, int);
111	fpos_t target, curoff, ret;
112	size_t n;
113	struct stat st;
114	int havepos;
115
116	/*
117	 * Have to be able to seek.
118	 */
119	if ((seekfn = fp->_seek) == NULL) {
120		errno = ESPIPE;		/* historic practice */
121		return (-1);
122	}
123
124	/*
125	 * Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
126	 * After this, whence is either SEEK_SET or SEEK_END.
127	 */
128	switch (whence) {
129
130	case SEEK_CUR:
131		/*
132		 * In order to seek relative to the current stream offset,
133		 * we have to first find the current stream offset via
134		 * ftell (see ftell for details).
135		 */
136		if (_ftello(fp, &curoff))
137			return (-1);
138		if (curoff < 0) {
139			/* Unspecified position because of ungetc() at 0 */
140			errno = ESPIPE;
141			return (-1);
142		}
143		if (offset > 0 && curoff > OFF_MAX - offset) {
144			errno = EOVERFLOW;
145			return (-1);
146		}
147		offset += curoff;
148		if (offset < 0) {
149			errno = EINVAL;
150			return (-1);
151		}
152		if (ltest && offset > LONG_MAX) {
153			errno = EOVERFLOW;
154			return (-1);
155		}
156		whence = SEEK_SET;
157		havepos = 1;
158		break;
159
160	case SEEK_SET:
161		if (offset < 0) {
162			errno = EINVAL;
163			return (-1);
164		}
165	case SEEK_END:
166		curoff = 0;		/* XXX just to keep gcc quiet */
167		havepos = 0;
168		break;
169
170	default:
171		errno = EINVAL;
172		return (-1);
173	}
174
175	/*
176	 * Can only optimise if:
177	 *	reading (and not reading-and-writing);
178	 *	not unbuffered; and
179	 *	this is a `regular' Unix file (and hence seekfn==__sseek).
180	 * We must check __NBF first, because it is possible to have __NBF
181	 * and __SOPT both set.
182	 */
183	if (fp->_bf._base == NULL)
184		__smakebuf(fp);
185	if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT))
186		goto dumb;
187	if ((fp->_flags & __SOPT) == 0) {
188		if (seekfn != __sseek ||
189		    fp->_file < 0 || _fstat(fp->_file, &st) ||
190		    (st.st_mode & S_IFMT) != S_IFREG) {
191			fp->_flags |= __SNPT;
192			goto dumb;
193		}
194		fp->_blksize = st.st_blksize;
195		fp->_flags |= __SOPT;
196	}
197
198	/*
199	 * We are reading; we can try to optimise.
200	 * Figure out where we are going and where we are now.
201	 */
202	if (whence == SEEK_SET)
203		target = offset;
204	else {
205		if (_fstat(fp->_file, &st))
206			goto dumb;
207		if (offset > 0 && st.st_size > OFF_MAX - offset) {
208			errno = EOVERFLOW;
209			return (-1);
210		}
211		target = st.st_size + offset;
212		if ((off_t)target < 0) {
213			errno = EINVAL;
214			return (-1);
215		}
216		if (ltest && (off_t)target > LONG_MAX) {
217			errno = EOVERFLOW;
218			return (-1);
219		}
220	}
221
222	if (!havepos && _ftello(fp, &curoff))
223		goto dumb;
224
225	/*
226	 * (If the buffer was modified, we have to
227	 * skip this; see fgetln.c.)
228	 */
229	if (fp->_flags & __SMOD)
230		goto abspos;
231
232	/*
233	 * Compute the number of bytes in the input buffer (pretending
234	 * that any ungetc() input has been discarded).  Adjust current
235	 * offset backwards by this count so that it represents the
236	 * file offset for the first byte in the current input buffer.
237	 */
238	if (HASUB(fp)) {
239		curoff += fp->_r;	/* kill off ungetc */
240		n = fp->_extra->_up - fp->_bf._base;
241		curoff -= n;
242		n += fp->_ur;
243	} else {
244		n = fp->_p - fp->_bf._base;
245		curoff -= n;
246		n += fp->_r;
247	}
248
249	/*
250	 * If the target offset is within the current buffer,
251	 * simply adjust the pointers, clear EOF, undo ungetc(),
252	 * and return.
253	 */
254	if (target >= curoff && target < curoff + n) {
255		size_t o = target - curoff;
256
257		fp->_p = fp->_bf._base + o;
258		fp->_r = n - o;
259		if (HASUB(fp))
260			FREEUB(fp);
261		fp->_flags &= ~__SEOF;
262		memset(&fp->_extra->mbstate, 0, sizeof(mbstate_t));
263		return (0);
264	}
265
266abspos:
267	/*
268	 * The place we want to get to is not within the current buffer,
269	 * but we can still be kind to the kernel copyout mechanism.
270	 * By aligning the file offset to a block boundary, we can let
271	 * the kernel use the VM hardware to map pages instead of
272	 * copying bytes laboriously.  Using a block boundary also
273	 * ensures that we only read one block, rather than two.
274	 */
275	curoff = target & ~(fp->_blksize - 1);
276	if (_sseek(fp, curoff, SEEK_SET) == POS_ERR)
277		goto dumb;
278	fp->_r = 0;
279	fp->_p = fp->_bf._base;
280	if (HASUB(fp))
281		FREEUB(fp);
282	n = target - curoff;
283	if (n) {
284		if (__srefill(fp) || fp->_r < n)
285			goto dumb;
286		fp->_p += n;
287		fp->_r -= n;
288	}
289	fp->_flags &= ~__SEOF;
290	return (0);
291
292	/*
293	 * We get here if we cannot optimise the seek ... just
294	 * do it.  Allow the seek function to change fp->_bf._base.
295	 */
296dumb:
297	if (__sflush(fp) ||
298	    (ret = _sseek(fp, (fpos_t)offset, whence)) == POS_ERR)
299		return (-1);
300	/* success: clear EOF indicator and discard ungetc() data */
301	if (HASUB(fp))
302		FREEUB(fp);
303	fp->_p = fp->_bf._base;
304	fp->_r = 0;
305	/* fp->_w = 0; */	/* unnecessary (I think...) */
306	fp->_flags &= ~__SEOF;
307	memset(&fp->_extra->mbstate, 0, sizeof(mbstate_t));
308	if (ltest && ret > LONG_MAX) {
309		fp->_flags |= __SERR;
310		errno = EOVERFLOW;
311		return (-1);
312	}
313	return (0);
314}
315