fseek.c revision 92905
138032Speter/*- 2111826Sgshapiro * Copyright (c) 1990, 1993 364565Sgshapiro * The Regents of the University of California. All rights reserved. 438032Speter * 538032Speter * This code is derived from software contributed to Berkeley by 638032Speter * Chris Torek. 738032Speter * 838032Speter * Redistribution and use in source and binary forms, with or without 938032Speter * modification, are permitted provided that the following conditions 1038032Speter * are met: 1138032Speter * 1. Redistributions of source code must retain the above copyright 12120259Sgshapiro * notice, this list of conditions and the following disclaimer. 1338032Speter * 2. Redistributions in binary form must reproduce the above copyright 1438032Speter * notice, this list of conditions and the following disclaimer in the 1564565Sgshapiro * documentation and/or other materials provided with the distribution. 1664565Sgshapiro * 3. All advertising materials mentioning features or use of this software 17132946Sgshapiro * must display the following acknowledgement: 1864565Sgshapiro * This product includes software developed by the University of 1964565Sgshapiro * California, Berkeley and its contributors. 2064565Sgshapiro * 4. Neither the name of the University nor the names of its contributors 2164565Sgshapiro * may be used to endorse or promote products derived from this software 2264565Sgshapiro * without specific prior written permission. 2338032Speter * 2438032Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2564565Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2664565Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2764565Sgshapiro * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2864565Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2964565Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3090795Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3164565Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3238032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3338032Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3438032Speter * SUCH DAMAGE. 3538032Speter */ 3638032Speter 3738032Speter#if defined(LIBC_SCCS) && !defined(lint) 3838032Speter#if 0 3938032Speterstatic char sccsid[] = "@(#)fseek.c 8.3 (Berkeley) 1/2/94"; 4038032Speter#endif 4138032Speterstatic const char rcsid[] = 4238032Speter "$FreeBSD: head/lib/libc/stdio/fseek.c 92905 2002-03-21 22:49:10Z obrien $"; 4338032Speter#endif /* LIBC_SCCS and not lint */ 4438032Speter 4538032Speter#include "namespace.h" 4638032Speter#include <sys/types.h> 4738032Speter#include <sys/stat.h> 4838032Speter#include <errno.h> 4938032Speter#include <fcntl.h> 5038032Speter#include <limits.h> 5138032Speter#include <stdio.h> 5238032Speter#include <stdlib.h> 5338032Speter#include "un-namespace.h" 5438032Speter#include "local.h" 5538032Speter#include "libc_private.h" 5638032Speter 5738032Speter#define POS_ERR (-(fpos_t)1) 5838032Speter 5938032Speterint 6038032Speterfseek(fp, offset, whence) 6138032Speter FILE *fp; 6238032Speter long offset; 6364565Sgshapiro int whence; 6438032Speter{ 6590795Sgshapiro int ret; 6638032Speter int serrno = errno; 6738032Speter 6838032Speter /* make sure stdio is set up */ 6938032Speter if (!__sdidinit) 7038032Speter __sinit(); 7138032Speter 7238032Speter FLOCKFILE(fp); 7338032Speter ret = _fseeko(fp, (off_t)offset, whence, 1); 7438032Speter FUNLOCKFILE(fp); 7538032Speter if (ret == 0) 7638032Speter errno = serrno; 7738032Speter return (ret); 7838032Speter} 7938032Speter 8038032Speterint 8138032Speterfseeko(fp, offset, whence) 8238032Speter FILE *fp; 8338032Speter off_t offset; 8438032Speter int whence; 8538032Speter{ 8638032Speter int ret; 8738032Speter int serrno = errno; 8838032Speter 8938032Speter /* make sure stdio is set up */ 9038032Speter if (!__sdidinit) 9138032Speter __sinit(); 9238032Speter 9338032Speter FLOCKFILE(fp); 9438032Speter ret = _fseeko(fp, offset, whence, 0); 9538032Speter FUNLOCKFILE(fp); 9638032Speter if (ret == 0) 9738032Speter errno = serrno; 9838032Speter return (ret); 9938032Speter} 10038032Speter 10138032Speter/* 10238032Speter * Seek the given file to the given offset. 10338032Speter * `Whence' must be one of the three SEEK_* macros. 10490795Sgshapiro */ 10538032Speterint 10638032Speter_fseeko(fp, offset, whence, ltest) 10790795Sgshapiro FILE *fp; 10890795Sgshapiro off_t offset; 10938032Speter int whence; 11038032Speter int ltest; 11164565Sgshapiro{ 11290795Sgshapiro fpos_t (*seekfn)(void *, fpos_t, int); 11390795Sgshapiro fpos_t target, curoff, ret; 11438032Speter size_t n; 11538032Speter struct stat st; 11638032Speter int havepos; 11738032Speter 11890795Sgshapiro /* 11938032Speter * Have to be able to seek. 12038032Speter */ 12138032Speter if ((seekfn = fp->_seek) == NULL) { 12238032Speter errno = ESPIPE; /* historic practice */ 12338032Speter return (-1); 12438032Speter } 12538032Speter 12638032Speter /* 12738032Speter * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. 12838032Speter * After this, whence is either SEEK_SET or SEEK_END. 12938032Speter */ 13038032Speter switch (whence) { 13138032Speter 13238032Speter case SEEK_CUR: 13338032Speter /* 13438032Speter * In order to seek relative to the current stream offset, 13538032Speter * we have to first find the current stream offset via 13638032Speter * ftell (see ftell for details). 13738032Speter */ 13838032Speter if (_ftello(fp, &curoff)) 13938032Speter return (-1); 14038032Speter if (curoff < 0) { 14138032Speter /* Unspecified position because of ungetc() at 0 */ 14238032Speter errno = ESPIPE; 14338032Speter return (-1); 14438032Speter } 14538032Speter if (offset > 0 && curoff > OFF_MAX - offset) { 14690795Sgshapiro errno = EOVERFLOW; 14764565Sgshapiro return (-1); 14864565Sgshapiro } 14938032Speter offset += curoff; 15038032Speter if (offset < 0) { 15138032Speter errno = EINVAL; 15238032Speter return (-1); 15338032Speter } 15438032Speter if (ltest && offset > LONG_MAX) { 15538032Speter errno = EOVERFLOW; 15638032Speter return (-1); 15738032Speter } 15838032Speter whence = SEEK_SET; 15938032Speter havepos = 1; 16038032Speter break; 16194337Sgshapiro 16264565Sgshapiro case SEEK_SET: 16364565Sgshapiro if (offset < 0) { 16464565Sgshapiro errno = EINVAL; 16564565Sgshapiro return (-1); 16638032Speter } 16764565Sgshapiro case SEEK_END: 16838032Speter curoff = 0; /* XXX just to keep gcc quiet */ 16964565Sgshapiro havepos = 0; 17064565Sgshapiro break; 17190795Sgshapiro 17238032Speter default: 17338032Speter errno = EINVAL; 17438032Speter return (-1); 17538032Speter } 17638032Speter 17738032Speter /* 17838032Speter * Can only optimise if: 17938032Speter * reading (and not reading-and-writing); 18038032Speter * not unbuffered; and 18190795Sgshapiro * this is a `regular' Unix file (and hence seekfn==__sseek). 18238032Speter * We must check __NBF first, because it is possible to have __NBF 18338032Speter * and __SOPT both set. 18438032Speter */ 18538032Speter if (fp->_bf._base == NULL) 18638032Speter __smakebuf(fp); 18738032Speter if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) 18890795Sgshapiro goto dumb; 18990795Sgshapiro if ((fp->_flags & __SOPT) == 0) { 19038032Speter if (seekfn != __sseek || 19138032Speter fp->_file < 0 || _fstat(fp->_file, &st) || 19238032Speter (st.st_mode & S_IFMT) != S_IFREG) { 19338032Speter fp->_flags |= __SNPT; 19438032Speter goto dumb; 19538032Speter } 19664565Sgshapiro fp->_blksize = st.st_blksize; 19738032Speter fp->_flags |= __SOPT; 19838032Speter } 19938032Speter 20038032Speter /* 20138032Speter * We are reading; we can try to optimise. 20238032Speter * Figure out where we are going and where we are now. 20338032Speter */ 20438032Speter if (whence == SEEK_SET) 20538032Speter target = offset; 20638032Speter else { 20738032Speter if (_fstat(fp->_file, &st)) 20838032Speter goto dumb; 20938032Speter if (offset > 0 && st.st_size > OFF_MAX - offset) { 21038032Speter errno = EOVERFLOW; 21138032Speter return (-1); 21238032Speter } 21338032Speter target = st.st_size + offset; 21490795Sgshapiro if ((off_t)target < 0) { 21590795Sgshapiro errno = EINVAL; 21690795Sgshapiro return (-1); 21738032Speter } 21838032Speter if (ltest && (off_t)target > LONG_MAX) { 21964565Sgshapiro errno = EOVERFLOW; 22090795Sgshapiro return (-1); 22190795Sgshapiro } 22238032Speter } 22364565Sgshapiro 22438032Speter if (!havepos && _ftello(fp, &curoff)) 22538032Speter goto dumb; 22638032Speter 22738032Speter /* 22838032Speter * (If the buffer was modified, we have to 22938032Speter * skip this; see fgetln.c.) 23038032Speter */ 23138032Speter if (fp->_flags & __SMOD) 23238032Speter goto abspos; 23390795Sgshapiro 23438032Speter /* 23590795Sgshapiro * Compute the number of bytes in the input buffer (pretending 23638032Speter * that any ungetc() input has been discarded). Adjust current 23738032Speter * offset backwards by this count so that it represents the 23838032Speter * file offset for the first byte in the current input buffer. 23938032Speter */ 24090795Sgshapiro if (HASUB(fp)) { 24138032Speter curoff += fp->_r; /* kill off ungetc */ 24290795Sgshapiro n = fp->_extra->_up - fp->_bf._base; 24338032Speter curoff -= n; 24438032Speter n += fp->_ur; 24538032Speter } else { 24638032Speter n = fp->_p - fp->_bf._base; 24738032Speter curoff -= n; 24890795Sgshapiro n += fp->_r; 24990795Sgshapiro } 25090795Sgshapiro 25190795Sgshapiro /* 25290795Sgshapiro * If the target offset is within the current buffer, 25390795Sgshapiro * simply adjust the pointers, clear EOF, undo ungetc(), 25438032Speter * and return. 25590795Sgshapiro */ 25690795Sgshapiro if (target >= curoff && target < curoff + n) { 25790795Sgshapiro size_t o = target - curoff; 25890795Sgshapiro 25990795Sgshapiro fp->_p = fp->_bf._base + o; 26090795Sgshapiro fp->_r = n - o; 26190795Sgshapiro if (HASUB(fp)) 26290795Sgshapiro FREEUB(fp); 26390795Sgshapiro fp->_flags &= ~__SEOF; 26490795Sgshapiro return (0); 26538032Speter } 26690795Sgshapiro 26738032Speterabspos: 26838032Speter /* 26938032Speter * The place we want to get to is not within the current buffer, 27090795Sgshapiro * but we can still be kind to the kernel copyout mechanism. 27190795Sgshapiro * By aligning the file offset to a block boundary, we can let 27238032Speter * the kernel use the VM hardware to map pages instead of 27338032Speter * copying bytes laboriously. Using a block boundary also 27438032Speter * ensures that we only read one block, rather than two. 27538032Speter */ 27638032Speter curoff = target & ~(fp->_blksize - 1); 27738032Speter if (_sseek(fp, curoff, SEEK_SET) == POS_ERR) 27838032Speter goto dumb; 27938032Speter fp->_r = 0; 28038032Speter fp->_p = fp->_bf._base; 28138032Speter if (HASUB(fp)) 28238032Speter FREEUB(fp); 28338032Speter n = target - curoff; 28438032Speter if (n) { 28538032Speter if (__srefill(fp) || fp->_r < n) 28638032Speter goto dumb; 28738032Speter fp->_p += n; 28838032Speter fp->_r -= n; 28971348Sgshapiro } 29038032Speter fp->_flags &= ~__SEOF; 29138032Speter return (0); 29271348Sgshapiro 29338032Speter /* 29490795Sgshapiro * We get here if we cannot optimise the seek ... just 29538032Speter * do it. Allow the seek function to change fp->_bf._base. 29690795Sgshapiro */ 29790795Sgshapirodumb: 29890795Sgshapiro if (__sflush(fp) || 29990795Sgshapiro (ret = _sseek(fp, (fpos_t)offset, whence)) == POS_ERR) 30090795Sgshapiro return (-1); 30190795Sgshapiro /* success: clear EOF indicator and discard ungetc() data */ 30290795Sgshapiro if (HASUB(fp)) 30338032Speter FREEUB(fp); 30438032Speter fp->_p = fp->_bf._base; 30538032Speter fp->_r = 0; 30638032Speter /* fp->_w = 0; */ /* unnecessary (I think...) */ 30738032Speter fp->_flags &= ~__SEOF; 30838032Speter if (ltest && ret > LONG_MAX) { 30938032Speter fp->_flags |= __SERR; 31038032Speter errno = EOVERFLOW; 31138032Speter return (-1); 31238032Speter } 31338032Speter return (0); 31438032Speter} 31564565Sgshapiro