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