SCCS id got lost along the way
[unix-history] / usr / src / lib / libc / stdio / fseek.c
CommitLineData
411867e7
KB
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * 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
2ce81398 11#if defined(LIBC_SCCS) && !defined(lint)
cb631443 12static char sccsid[] = "@(#)fseek.c 5.7 (Berkeley) %G%";
411867e7
KB
13#endif /* LIBC_SCCS and not lint */
14
15#include <sys/types.h>
411867e7 16#include <sys/stat.h>
cb631443 17#include <fcntl.h>
411867e7 18#include <stdio.h>
cb631443 19#include <stdlib.h>
411867e7
KB
20#include <errno.h>
21#include "local.h"
22
23#define POS_ERR (-(fpos_t)1)
b8f253e8 24
a9e59a21 25/*
411867e7
KB
26 * Seek the given file to the given offset.
27 * `Whence' must be one of the three SEEK_* macros.
a9e59a21 28 */
411867e7
KB
29fseek(fp, offset, whence)
30 register FILE *fp;
31 long offset;
32 int whence;
33{
34#if __STDC__
d25ccb19 35 register fpos_t (*seekfn)(void *, fpos_t, int);
411867e7
KB
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();
a9e59a21 47
411867e7
KB
48 /*
49 * Have to be able to seek.
50 */
51 if ((seekfn = fp->_seek) == NULL) {
52 errno = ESPIPE; /* historic practice */
53 return (EOF);
54 }
a9e59a21 55
411867e7
KB
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) {
a9e59a21 61
411867e7
KB
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:
d25ccb19 89 curoff = 0; /* XXX just to keep gcc quiet */
411867e7
KB
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;
d8af6b8b 116 }
411867e7
KB
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;
a9e59a21 131 }
411867e7
KB
132
133 if (!havepos) {
134 if (fp->_flags & __SOFF)
135 curoff = fp->_offset;
136 else {
137 curoff = (*seekfn)(fp->_cookie, 0L, SEEK_CUR);
138 if (curoff == POS_ERR)
139 goto dumb;
d8af6b8b 140 }
411867e7
KB
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 */
208dumb:
5b2b97c1 209 if (__sflush(fp) ||
411867e7
KB
210 (*seekfn)(fp->_cookie, offset, whence) == POS_ERR) {
211 return (EOF);
a9e59a21 212 }
411867e7
KB
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);
a9e59a21 221}