fseek.c revision 82588
150276Speter/*- 250276Speter * Copyright (c) 1990, 1993 350276Speter * The Regents of the University of California. All rights reserved. 450276Speter * 550276Speter * This code is derived from software contributed to Berkeley by 650276Speter * Chris Torek. 750276Speter * 850276Speter * Redistribution and use in source and binary forms, with or without 950276Speter * modification, are permitted provided that the following conditions 1050276Speter * are met: 1150276Speter * 1. Redistributions of source code must retain the above copyright 1250276Speter * notice, this list of conditions and the following disclaimer. 1350276Speter * 2. Redistributions in binary form must reproduce the above copyright 1450276Speter * notice, this list of conditions and the following disclaimer in the 1550276Speter * documentation and/or other materials provided with the distribution. 1650276Speter * 3. All advertising materials mentioning features or use of this software 1750276Speter * must display the following acknowledgement: 1850276Speter * This product includes software developed by the University of 1950276Speter * California, Berkeley and its contributors. 2050276Speter * 4. Neither the name of the University nor the names of its contributors 2150276Speter * may be used to endorse or promote products derived from this software 2250276Speter * without specific prior written permission. 2350276Speter * 2450276Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2550276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2650276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2750276Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2850276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2950276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3050276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3150276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3250276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3350276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3450276Speter * SUCH DAMAGE. 3550276Speter */ 3650276Speter 3750276Speter#if defined(LIBC_SCCS) && !defined(lint) 3850276Speter#if 0 3950276Speterstatic char sccsid[] = "@(#)fseek.c 8.3 (Berkeley) 1/2/94"; 4050276Speter#endif 4150276Speterstatic const char rcsid[] = 4250276Speter "$FreeBSD: head/lib/libc/stdio/fseek.c 82588 2001-08-30 19:54:04Z ache $"; 4350276Speter#endif /* LIBC_SCCS and not lint */ 4450276Speter 4550276Speter#include "namespace.h" 4650276Speter#include <sys/types.h> 4750276Speter#include <sys/stat.h> 4850276Speter#include <errno.h> 4950276Speter#include <fcntl.h> 5050276Speter#include <limits.h> 5150276Speter#include <stdio.h> 5250276Speter#include <stdlib.h> 5350276Speter#include "un-namespace.h" 5450276Speter#include "local.h" 5550276Speter#include "libc_private.h" 5650276Speter 5750276Speter#define POS_ERR (-(fpos_t)1) 5850276Speter 5950276Speterint 6050276Speterfseek(fp, offset, whence) 6150276Speter register FILE *fp; 6250276Speter long offset; 6350276Speter int whence; 6450276Speter{ 6550276Speter int ret; 6650276Speter 6750276Speter /* make sure stdio is set up */ 6850276Speter if (!__sdidinit) 6950276Speter __sinit(); 7050276Speter 7150276Speter FLOCKFILE(fp); 7250276Speter ret = _fseeko(fp, (off_t)offset, whence, 1); 7350276Speter FUNLOCKFILE(fp); 7450276Speter return (ret); 7550276Speter} 7650276Speter 7750276Speterint 7850276Speterfseeko(fp, offset, whence) 7950276Speter FILE *fp; 8050276Speter off_t offset; 8150276Speter int whence; 8250276Speter{ 8350276Speter int ret; 8450276Speter 8550276Speter /* make sure stdio is set up */ 8650276Speter if (!__sdidinit) 8750276Speter __sinit(); 8850276Speter 8950276Speter FLOCKFILE(fp); 9050276Speter ret = _fseeko(fp, offset, whence, 0); 9150276Speter FUNLOCKFILE(fp); 9250276Speter return (ret); 9350276Speter} 9450276Speter 9550276Speter/* 9650276Speter * Seek the given file to the given offset. 9750276Speter * `Whence' must be one of the three SEEK_* macros. 9850276Speter */ 9950276Speterint 10050276Speter_fseeko(fp, offset, whence, ltest) 10150276Speter FILE *fp; 10250276Speter off_t offset; 10350276Speter int whence; 10450276Speter int ltest; 10550276Speter{ 10650276Speter register fpos_t (*seekfn) __P((void *, fpos_t, int)); 10750276Speter fpos_t target, curoff; 10850276Speter size_t n; 10950276Speter struct stat st; 11050276Speter int havepos; 11150276Speter 11250276Speter /* 11350276Speter * Have to be able to seek. 11450276Speter */ 11550276Speter if ((seekfn = fp->_seek) == NULL) { 11650276Speter errno = ESPIPE; /* historic practice */ 11750276Speter return (-1); 11850276Speter } 11950276Speter 12050276Speter /* 12150276Speter * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. 12250276Speter * After this, whence is either SEEK_SET or SEEK_END. 12350276Speter */ 12450276Speter switch (whence) { 12550276Speter 12650276Speter case SEEK_CUR: 12750276Speter /* 12850276Speter * In order to seek relative to the current stream offset, 12950276Speter * we have to first find the current stream offset a la 13050276Speter * ftell (see ftell for details). 13150276Speter */ 13250276Speter if (fp->_flags & __SOFF) 13350276Speter curoff = fp->_offset; 13450276Speter else { 13550276Speter curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); 13650276Speter if (curoff == -1) 13750276Speter return (-1); 13850276Speter } 13950276Speter if (fp->_flags & __SRD) { 14050276Speter curoff -= fp->_r; 14150276Speter if (curoff < 0) { 14250276Speter if (HASUB(fp)) { 14350276Speter fp->_p -= curoff; 14450276Speter fp->_r += curoff; 14550276Speter curoff = 0; 14650276Speter } else { 14750276Speter errno = EBADF; 14850276Speter return (-1); 14950276Speter } 15050276Speter } 15150276Speter if (HASUB(fp)) { 15250276Speter curoff -= fp->_ur; 15350276Speter if (curoff < 0) { 15450276Speter errno = EBADF; 15550276Speter return (-1); 15650276Speter } 15750276Speter } 15850276Speter } else if ((fp->_flags & __SWR) && fp->_p != NULL) { 15950276Speter n = fp->_p - fp->_bf._base; 16050276Speter if (curoff > OFF_MAX - n) { 16150276Speter errno = EOVERFLOW; 16250276Speter return (-1); 16350276Speter } 16450276Speter curoff += n; 16550276Speter } 16650276Speter if (offset > 0 && curoff > OFF_MAX - offset) { 16750276Speter errno = EOVERFLOW; 16850276Speter return (-1); 16950276Speter } 17050276Speter offset += curoff; 17150276Speter if (offset < 0) { 17250276Speter errno = EINVAL; 17350276Speter return (-1); 17450276Speter } 17550276Speter if (ltest && offset > LONG_MAX) { 17650276Speter errno = EOVERFLOW; 17750276Speter return (-1); 17850276Speter } 17950276Speter whence = SEEK_SET; 18050276Speter havepos = 1; 18150276Speter break; 18250276Speter 18350276Speter case SEEK_SET: 18450276Speter if (offset < 0) { 18550276Speter errno = EINVAL; 18650276Speter return (-1); 18750276Speter } 18850276Speter case SEEK_END: 18950276Speter curoff = 0; /* XXX just to keep gcc quiet */ 19050276Speter havepos = 0; 19150276Speter break; 19250276Speter 19350276Speter default: 19450276Speter errno = EINVAL; 19550276Speter return (-1); 19650276Speter } 19750276Speter 19850276Speter /* 19950276Speter * Can only optimise if: 20050276Speter * reading (and not reading-and-writing); 20150276Speter * not unbuffered; and 20250276Speter * this is a `regular' Unix file (and hence seekfn==__sseek). 20350276Speter * We must check __NBF first, because it is possible to have __NBF 20450276Speter * and __SOPT both set. 20550276Speter */ 20650276Speter if (fp->_bf._base == NULL) 20750276Speter __smakebuf(fp); 20850276Speter if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) 20950276Speter goto dumb; 21050276Speter if ((fp->_flags & __SOPT) == 0) { 21150276Speter if (seekfn != __sseek || 21250276Speter fp->_file < 0 || _fstat(fp->_file, &st) || 21350276Speter (st.st_mode & S_IFMT) != S_IFREG) { 21450276Speter fp->_flags |= __SNPT; 21550276Speter goto dumb; 21650276Speter } 21750276Speter fp->_blksize = st.st_blksize; 21850276Speter fp->_flags |= __SOPT; 21950276Speter } 22050276Speter 22150276Speter /* 22250276Speter * We are reading; we can try to optimise. 22350276Speter * Figure out where we are going and where we are now. 22450276Speter */ 22550276Speter if (whence == SEEK_SET) 22650276Speter target = offset; 22750276Speter else { 22850276Speter if (_fstat(fp->_file, &st)) 22950276Speter goto dumb; 23050276Speter if (offset > 0 && st.st_size > OFF_MAX - offset) { 23150276Speter errno = EOVERFLOW; 23250276Speter return (-1); 23350276Speter } 23450276Speter target = st.st_size + offset; 23550276Speter if ((off_t)target < 0) { 23650276Speter errno = EINVAL; 23750276Speter return (-1); 23850276Speter } 23950276Speter if (ltest && (off_t)target > LONG_MAX) { 24050276Speter errno = EOVERFLOW; 24150276Speter return (-1); 24250276Speter } 24350276Speter } 24450276Speter 24550276Speter if (!havepos) { 24650276Speter if (fp->_flags & __SOFF) 24750276Speter curoff = fp->_offset; 24850276Speter else { 24950276Speter curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); 25050276Speter if (curoff == POS_ERR) 25150276Speter goto dumb; 25250276Speter } 25350276Speter curoff -= fp->_r; 25450276Speter if (curoff < 0) { 25550276Speter if (HASUB(fp)) { 25650276Speter fp->_p -= curoff; 25750276Speter fp->_r += curoff; 25850276Speter curoff = 0; 25950276Speter } else { 26050276Speter errno = EBADF; 26150276Speter return (-1); 26250276Speter } 26350276Speter } 26450276Speter if (HASUB(fp)) { 26550276Speter curoff -= fp->_ur; 26650276Speter if (curoff < 0) { 26750276Speter errno = EBADF; 26850276Speter return (-1); 26950276Speter } 27050276Speter } 27150276Speter } 27250276Speter 27350276Speter /* 27450276Speter * Compute the number of bytes in the input buffer (pretending 27550276Speter * that any ungetc() input has been discarded). Adjust current 27650276Speter * offset backwards by this count so that it represents the 27750276Speter * file offset for the first byte in the current input buffer. 27850276Speter */ 27950276Speter if (HASUB(fp)) { 28050276Speter if (curoff > OFF_MAX - fp->_r) 28150276Speter goto abspos; 28250276Speter curoff += fp->_r; /* kill off ungetc */ 28350276Speter n = fp->_extra->_up - fp->_bf._base; 28450276Speter curoff -= n; 28550276Speter n += fp->_ur; 28650276Speter } else { 28750276Speter n = fp->_p - fp->_bf._base; 28850276Speter curoff -= n; 28950276Speter n += fp->_r; 29050276Speter } 29150276Speter /* curoff can be negative at this point. */ 29250276Speter 29350276Speter /* 29450276Speter * If the target offset is within the current buffer, 29550276Speter * simply adjust the pointers, clear EOF, undo ungetc(), 29650276Speter * and return. (If the buffer was modified, we have to 29750276Speter * skip this; see fgetln.c.) 29850276Speter */ 29950276Speter if ((fp->_flags & __SMOD) == 0 && 30050276Speter target >= curoff && 30150276Speter (curoff <= 0 || curoff <= OFF_MAX - n) && 30250276Speter target < curoff + n) { 30350276Speter size_t o = target - curoff; 30450276Speter 30550276Speter fp->_p = fp->_bf._base + o; 30650276Speter fp->_r = n - o; 30750276Speter if (HASUB(fp)) 30850276Speter FREEUB(fp); 30950276Speter fp->_flags &= ~__SEOF; 31050276Speter return (0); 31150276Speter } 31250276Speter 31350276Speterabspos: 31450276Speter /* 31550276Speter * The place we want to get to is not within the current buffer, 31650276Speter * but we can still be kind to the kernel copyout mechanism. 31750276Speter * By aligning the file offset to a block boundary, we can let 31850276Speter * the kernel use the VM hardware to map pages instead of 31950276Speter * copying bytes laboriously. Using a block boundary also 32050276Speter * ensures that we only read one block, rather than two. 32150276Speter */ 32250276Speter curoff = target & ~(fp->_blksize - 1); 32350276Speter if ((*seekfn)(fp->_cookie, curoff, SEEK_SET) == POS_ERR) 32450276Speter goto dumb; 32550276Speter fp->_r = 0; 32650276Speter fp->_p = fp->_bf._base; 32750276Speter if (HASUB(fp)) 32850276Speter FREEUB(fp); 32950276Speter fp->_flags &= ~__SEOF; 33050276Speter n = target - curoff; 33150276Speter if (n) { 33250276Speter if (__srefill(fp) || fp->_r < n) 33350276Speter goto dumb; 33450276Speter fp->_p += n; 33550276Speter fp->_r -= n; 33650276Speter } 33750276Speter return (0); 33850276Speter 33950276Speter /* 34050276Speter * We get here if we cannot optimise the seek ... just 34150276Speter * do it. Allow the seek function to change fp->_bf._base. 34250276Speter */ 34350276Speterdumb: 34450276Speter if (__sflush(fp) || 34550276Speter (*seekfn)(fp->_cookie, (fpos_t)offset, whence) == POS_ERR) 34650276Speter return (-1); 34750276Speter if (ltest && fp->_offset > LONG_MAX) { 34850276Speter errno = EOVERFLOW; 34950276Speter return (-1); 35050276Speter } 35150276Speter /* success: clear EOF indicator and discard ungetc() data */ 35250276Speter if (HASUB(fp)) 35350276Speter FREEUB(fp); 35450276Speter fp->_p = fp->_bf._base; 35550276Speter fp->_r = 0; 35650276Speter /* fp->_w = 0; */ /* unnecessary (I think...) */ 35750276Speter fp->_flags &= ~__SEOF; 35850276Speter return (0); 35950276Speter} 36050276Speter