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