2 * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
10 * By using this file, you agree to the terms and conditions set
11 * forth in the LICENSE file which can be found at the top level of
12 * the sendmail distribution.
15 #pragma ident "%Z%%M% %I% %E% SMI"
18 SM_RCSID("@(#)$Id: fseek.c,v 1.47 2005/06/14 23:07:20 ca Exp $")
19 #include <sys/types.h>
26 #include <sm/signal.h>
28 #include <sm/assert.h>
32 #define POS_ERR (-(off_t)1)
34 static void seekalrm
__P((int));
35 static jmp_buf SeekTimeOut
;
38 ** SEEKALRM -- handler when timeout activated for sm_io_seek()
40 ** Returns flow of control to where setjmp(SeekTimeOut) was set.
49 ** returns flow of control to setjmp(SeekTimeOut).
51 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
52 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
61 longjmp(SeekTimeOut
, 1);
65 ** SM_IO_SEEK -- position the file pointer
68 ** fp -- the file pointer to be seek'd
69 ** timeout -- time to complete seek (milliseconds)
70 ** offset -- seek offset based on 'whence'
71 ** whence -- indicates where seek is relative from.
72 ** One of SM_IO_SEEK_{CUR,SET,END}.
74 ** Failure: returns -1 (minus 1) and sets errno
75 ** Success: returns 0 (zero)
79 sm_io_seek(fp
, timeout
, offset
, whence
)
80 register SM_FILE_T
*fp
;
81 int SM_NONVOLATILE timeout
;
82 long SM_NONVOLATILE offset
;
83 int SM_NONVOLATILE whence
;
91 register off_t (*seekfn
) __P((SM_FILE_T
*, off_t
, int));
93 SM_REQUIRE_ISA(fp
, SmFileMagic
);
95 /* make sure stdio is set up */
99 /* Have to be able to seek. */
100 if ((seekfn
= fp
->f_seek
) == NULL
)
102 errno
= ESPIPE
; /* historic practice */
106 if (timeout
== SM_TIME_DEFAULT
)
107 timeout
= fp
->f_timeout
;
108 if (timeout
== SM_TIME_IMMEDIATE
)
111 ** Filling the buffer will take time and we are wanted to
112 ** return immediately. So...
119 #define SM_SET_ALARM() \
120 if (timeout != SM_TIME_FOREVER) \
122 if (setjmp(SeekTimeOut) != 0) \
127 evt = sm_seteventm(timeout, seekalrm, 0); \
131 ** Change any SM_IO_SEEK_CUR to SM_IO_SEEK_SET, and check `whence'
132 ** argument. After this, whence is either SM_IO_SEEK_SET or
141 ** In order to seek relative to the current stream offset,
142 ** we have to first find the current stream offset a la
143 ** ftell (see ftell for details).
146 /* may adjust seek offset on append stream */
147 sm_flush(fp
, (int *) &timeout
);
149 if (fp
->f_flags
& SMOFF
)
150 curoff
= fp
->f_lseekoff
;
153 curoff
= (*seekfn
)(fp
, (off_t
) 0, SM_IO_SEEK_CUR
);
160 if (fp
->f_flags
& SMRD
)
166 else if (fp
->f_flags
& SMWR
&& fp
->f_p
!= NULL
)
167 curoff
+= fp
->f_p
- fp
->f_bf
.smb_base
;
170 whence
= SM_IO_SEEK_SET
;
177 curoff
= 0; /* XXX just to keep gcc quiet */
187 ** Can only optimise if:
188 ** reading (and not reading-and-writing);
189 ** not unbuffered; and
190 ** this is a `regular' Unix file (and hence seekfn==sm_stdseek).
191 ** We must check SMNBF first, because it is possible to have SMNBF
192 ** and SMSOPT both set.
195 if (fp
->f_bf
.smb_base
== NULL
)
197 if (fp
->f_flags
& (SMWR
| SMRW
| SMNBF
| SMNPT
))
199 if ((fp
->f_flags
& SMOPT
) == 0)
201 if (seekfn
!= sm_stdseek
||
202 fp
->f_file
< 0 || fstat(fp
->f_file
, &st
) ||
203 (st
.st_mode
& S_IFMT
) != S_IFREG
)
205 fp
->f_flags
|= SMNPT
;
208 fp
->f_blksize
= st
.st_blksize
;
209 fp
->f_flags
|= SMOPT
;
213 ** We are reading; we can try to optimise.
214 ** Figure out where we are going and where we are now.
217 if (whence
== SM_IO_SEEK_SET
)
221 if (fstat(fp
->f_file
, &st
))
223 target
= st
.st_size
+ offset
;
228 if (fp
->f_flags
& SMOFF
)
229 curoff
= fp
->f_lseekoff
;
232 curoff
= (*seekfn
)(fp
, (off_t
) 0, SM_IO_SEEK_CUR
);
233 if (curoff
== POS_ERR
)
242 ** Compute the number of bytes in the input buffer (pretending
243 ** that any ungetc() input has been discarded). Adjust current
244 ** offset backwards by this count so that it represents the
245 ** file offset for the first byte in the current input buffer.
250 curoff
+= fp
->f_r
; /* kill off ungetc */
251 n
= fp
->f_up
- fp
->f_bf
.smb_base
;
257 n
= fp
->f_p
- fp
->f_bf
.smb_base
;
263 ** If the target offset is within the current buffer,
264 ** simply adjust the pointers, clear SMFEOF, undo ungetc(),
265 ** and return. (If the buffer was modified, we have to
266 ** skip this; see getln in fget.c.)
269 if (target
>= curoff
&& target
< curoff
+ (off_t
) n
)
271 register int o
= target
- curoff
;
273 fp
->f_p
= fp
->f_bf
.smb_base
+ o
;
277 fp
->f_flags
&= ~SMFEOF
;
283 ** The place we want to get to is not within the current buffer,
284 ** but we can still be kind to the kernel copyout mechanism.
285 ** By aligning the file offset to a block boundary, we can let
286 ** the kernel use the VM hardware to map pages instead of
287 ** copying bytes laboriously. Using a block boundary also
288 ** ensures that we only read one block, rather than two.
291 curoff
= target
& ~(fp
->f_blksize
- 1);
292 if ((*seekfn
)(fp
, curoff
, SM_IO_SEEK_SET
) == POS_ERR
)
295 fp
->f_p
= fp
->f_bf
.smb_base
;
298 fp
->f_flags
&= ~SMFEOF
;
302 /* Note: SM_TIME_FOREVER since fn timeout already set */
303 if (sm_refill(fp
, SM_TIME_FOREVER
) || fp
->f_r
< (int) n
)
311 /* We're back. So undo our timeout and handler */
317 ** We get here if we cannot optimise the seek ... just
318 ** do it. Allow the seek function to change fp->f_bf.smb_base.
321 /* Note: SM_TIME_FOREVER since fn timeout already set */
322 ret
= SM_TIME_FOREVER
;
323 if (sm_flush(fp
, &ret
) != 0 ||
324 (*seekfn
)(fp
, (off_t
) offset
, whence
) == POS_ERR
)
330 /* success: clear SMFEOF indicator and discard ungetc() data */
333 fp
->f_p
= fp
->f_bf
.smb_base
;
335 fp
->f_flags
&= ~SMFEOF
;