1*46087Sbostic /*- 2*46087Sbostic * Copyright (c) 1990 The Regents of the University of California. 3*46087Sbostic * All rights reserved. 4*46087Sbostic * 5*46087Sbostic * This code is derived from software contributed to Berkeley by 6*46087Sbostic * Chris Torek. 7*46087Sbostic * 8*46087Sbostic * %sccs.include.redist.c% 9*46087Sbostic */ 10*46087Sbostic 1126653Sdonn #if defined(LIBC_SCCS) && !defined(lint) 12*46087Sbostic static char sccsid[] = "@(#)fseek.c 5.4 (Berkeley) 01/20/91"; 13*46087Sbostic #endif /* LIBC_SCCS and not lint */ 1422134Smckusick 15*46087Sbostic #include <sys/types.h> 16*46087Sbostic #include <sys/file.h> 17*46087Sbostic #include <sys/stat.h> 18*46087Sbostic #include <stdio.h> 19*46087Sbostic #include <errno.h> 20*46087Sbostic #include "local.h" 21*46087Sbostic 22*46087Sbostic #define POS_ERR (-(fpos_t)1) 23*46087Sbostic 242009Swnj /* 25*46087Sbostic * Seek the given file to the given offset. 26*46087Sbostic * `Whence' must be one of the three SEEK_* macros. 272009Swnj */ 28*46087Sbostic fseek(fp, offset, whence) 29*46087Sbostic register FILE *fp; 30*46087Sbostic long offset; 31*46087Sbostic int whence; 32*46087Sbostic { 33*46087Sbostic #if __STDC__ 34*46087Sbostic register fpos_t (*seekfn)(char *, fpos_t, int); 35*46087Sbostic #else 36*46087Sbostic register fpos_t (*seekfn)(); 37*46087Sbostic #endif 38*46087Sbostic fpos_t target, curoff; 39*46087Sbostic size_t n; 40*46087Sbostic struct stat st; 41*46087Sbostic int havepos; 422009Swnj 43*46087Sbostic /* make sure stdio is set up */ 44*46087Sbostic if (!__sdidinit) 45*46087Sbostic __sinit(); 462009Swnj 47*46087Sbostic /* 48*46087Sbostic * Have to be able to seek. 49*46087Sbostic */ 50*46087Sbostic if ((seekfn = fp->_seek) == NULL) { 51*46087Sbostic errno = ESPIPE; /* historic practice */ 52*46087Sbostic return (EOF); 53*46087Sbostic } 542009Swnj 55*46087Sbostic /* 56*46087Sbostic * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. 57*46087Sbostic * After this, whence is either SEEK_SET or SEEK_END. 58*46087Sbostic */ 59*46087Sbostic switch (whence) { 602009Swnj 61*46087Sbostic case SEEK_CUR: 62*46087Sbostic /* 63*46087Sbostic * In order to seek relative to the current stream offset, 64*46087Sbostic * we have to first find the current stream offset a la 65*46087Sbostic * ftell (see ftell for details). 66*46087Sbostic */ 67*46087Sbostic if (fp->_flags & __SOFF) 68*46087Sbostic curoff = fp->_offset; 69*46087Sbostic else { 70*46087Sbostic curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); 71*46087Sbostic if (curoff == -1L) 72*46087Sbostic return (EOF); 733163Stoy } 74*46087Sbostic if (fp->_flags & __SRD) { 75*46087Sbostic curoff -= fp->_r; 76*46087Sbostic if (HASUB(fp)) 77*46087Sbostic curoff -= fp->_ur; 78*46087Sbostic } else if (fp->_flags & __SWR && fp->_p != NULL) 79*46087Sbostic curoff += fp->_p - fp->_bf._base; 80*46087Sbostic 81*46087Sbostic offset += curoff; 82*46087Sbostic whence = SEEK_SET; 83*46087Sbostic havepos = 1; 84*46087Sbostic break; 85*46087Sbostic 86*46087Sbostic case SEEK_SET: 87*46087Sbostic case SEEK_END: 88*46087Sbostic havepos = 0; 89*46087Sbostic break; 90*46087Sbostic 91*46087Sbostic default: 92*46087Sbostic errno = EINVAL; 93*46087Sbostic return (EOF); 942009Swnj } 95*46087Sbostic 96*46087Sbostic /* 97*46087Sbostic * Can only optimise if: 98*46087Sbostic * reading (and not reading-and-writing); 99*46087Sbostic * not unbuffered; and 100*46087Sbostic * this is a `regular' Unix file (and hence seekfn==__sseek). 101*46087Sbostic * We must check __NBF first, because it is possible to have __NBF 102*46087Sbostic * and __SOPT both set. 103*46087Sbostic */ 104*46087Sbostic if (fp->_bf._base == NULL) 105*46087Sbostic __smakebuf(fp); 106*46087Sbostic if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) 107*46087Sbostic goto dumb; 108*46087Sbostic if ((fp->_flags & __SOPT) == 0) { 109*46087Sbostic if (seekfn != __sseek || 110*46087Sbostic fp->_file < 0 || fstat(fp->_file, &st) || 111*46087Sbostic (st.st_mode & S_IFMT) != S_IFREG) { 112*46087Sbostic fp->_flags |= __SNPT; 113*46087Sbostic goto dumb; 1143163Stoy } 115*46087Sbostic fp->_blksize = st.st_blksize; 116*46087Sbostic fp->_flags |= __SOPT; 1172009Swnj } 118*46087Sbostic 119*46087Sbostic /* 120*46087Sbostic * We are reading; we can try to optimise. 121*46087Sbostic * Figure out where we are going and where we are now. 122*46087Sbostic */ 123*46087Sbostic if (whence == SEEK_SET) 124*46087Sbostic target = offset; 125*46087Sbostic else { 126*46087Sbostic if (fstat(fp->_file, &st)) 127*46087Sbostic goto dumb; 128*46087Sbostic target = st.st_size + offset; 129*46087Sbostic } 130*46087Sbostic 131*46087Sbostic if (!havepos) { 132*46087Sbostic if (fp->_flags & __SOFF) 133*46087Sbostic curoff = fp->_offset; 134*46087Sbostic else { 135*46087Sbostic curoff = (*seekfn)(fp->_cookie, 0L, SEEK_CUR); 136*46087Sbostic if (curoff == POS_ERR) 137*46087Sbostic goto dumb; 138*46087Sbostic } 139*46087Sbostic curoff -= fp->_r; 140*46087Sbostic if (HASUB(fp)) 141*46087Sbostic curoff -= fp->_ur; 142*46087Sbostic } 143*46087Sbostic 144*46087Sbostic /* 145*46087Sbostic * Compute the number of bytes in the input buffer (pretending 146*46087Sbostic * that any ungetc() input has been discarded). Adjust current 147*46087Sbostic * offset backwards by this count so that it represents the 148*46087Sbostic * file offset for the first byte in the current input buffer. 149*46087Sbostic */ 150*46087Sbostic if (HASUB(fp)) { 151*46087Sbostic n = fp->_up - fp->_bf._base; 152*46087Sbostic curoff -= n; 153*46087Sbostic n += fp->_ur; 154*46087Sbostic } else { 155*46087Sbostic n = fp->_p - fp->_bf._base; 156*46087Sbostic curoff -= n; 157*46087Sbostic n += fp->_r; 158*46087Sbostic } 159*46087Sbostic 160*46087Sbostic /* 161*46087Sbostic * If the target offset is within the current buffer, 162*46087Sbostic * simply adjust the pointers, clear EOF, undo ungetc(), 163*46087Sbostic * and return. (If the buffer was modified, we have to 164*46087Sbostic * skip this; see fgetline.c.) 165*46087Sbostic */ 166*46087Sbostic if ((fp->_flags & __SMOD) == 0 && 167*46087Sbostic target >= curoff && target < curoff + n) { 168*46087Sbostic register int o = target - curoff; 169*46087Sbostic 170*46087Sbostic fp->_p = fp->_bf._base + o; 171*46087Sbostic fp->_r = n - o; 172*46087Sbostic if (HASUB(fp)) 173*46087Sbostic FREEUB(fp); 174*46087Sbostic fp->_flags &= ~__SEOF; 175*46087Sbostic return (0); 176*46087Sbostic } 177*46087Sbostic 178*46087Sbostic /* 179*46087Sbostic * The place we want to get to is not within the current buffer, 180*46087Sbostic * but we can still be kind to the kernel copyout mechanism. 181*46087Sbostic * By aligning the file offset to a block boundary, we can let 182*46087Sbostic * the kernel use the VM hardware to map pages instead of 183*46087Sbostic * copying bytes laboriously. Using a block boundary also 184*46087Sbostic * ensures that we only read one block, rather than two. 185*46087Sbostic */ 186*46087Sbostic curoff = target & ~(fp->_blksize - 1); 187*46087Sbostic if ((*seekfn)(fp->_cookie, curoff, SEEK_SET) == POS_ERR) 188*46087Sbostic goto dumb; 189*46087Sbostic fp->_r = 0; 190*46087Sbostic if (HASUB(fp)) 191*46087Sbostic FREEUB(fp); 192*46087Sbostic fp->_flags &= ~__SEOF; 193*46087Sbostic n = target - curoff; 194*46087Sbostic if (n) { 195*46087Sbostic if (__srefill(fp) || fp->_r < n) 196*46087Sbostic goto dumb; 197*46087Sbostic fp->_p += n; 198*46087Sbostic fp->_r -= n; 199*46087Sbostic } 200*46087Sbostic return (0); 201*46087Sbostic 202*46087Sbostic /* 203*46087Sbostic * We get here if we cannot optimise the seek ... just 204*46087Sbostic * do it. Allow the seek function to change fp->_bf._base. 205*46087Sbostic */ 206*46087Sbostic dumb: 207*46087Sbostic if (fflush(fp) || 208*46087Sbostic (*seekfn)(fp->_cookie, offset, whence) == POS_ERR) { 209*46087Sbostic return (EOF); 210*46087Sbostic } 211*46087Sbostic /* success: clear EOF indicator and discard ungetc() data */ 212*46087Sbostic if (HASUB(fp)) 213*46087Sbostic FREEUB(fp); 214*46087Sbostic fp->_p = fp->_bf._base; 215*46087Sbostic fp->_r = 0; 216*46087Sbostic /* fp->_w = 0; */ /* unnecessary (I think...) */ 217*46087Sbostic fp->_flags &= ~__SEOF; 218*46087Sbostic return (0); 2192009Swnj } 220