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