Update zoneinfo database.
[dragonfly.git] / contrib / sendmail / libsm / fseek.c
1 /*
2  * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1990, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Chris Torek.
9  *
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.
13  */
14
15 #include <sm/gen.h>
16 SM_RCSID("@(#)$Id: fseek.c,v 1.45 2001/09/11 04:04:48 gshapiro Exp $")
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <setjmp.h>
23 #include <sys/time.h>
24 #include <sm/signal.h>
25 #include <sm/io.h>
26 #include <sm/assert.h>
27 #include <sm/clock.h>
28 #include "local.h"
29
30 #define POS_ERR (-(off_t)1)
31
32 static jmp_buf SeekTimeOut;
33
34 /*
35 **  SEEKALRM -- handler when timeout activated for sm_io_seek()
36 **
37 **  Returns flow of control to where setjmp(SeekTimeOut) was set.
38 **
39 **      Parameters:
40 **              sig -- unused
41 **
42 **      Returns:
43 **              does not return
44 **
45 **      Side Effects:
46 **              returns flow of control to setjmp(SeekTimeOut).
47 **
48 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
49 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
50 **              DOING.
51 */
52
53 /* ARGSUSED0 */
54 static void
55 seekalrm(sig)
56         int sig;
57 {
58         longjmp(SeekTimeOut, 1);
59 }
60
61 /*
62 **  SM_IO_SEEK -- position the file pointer
63 **
64 **      Parameters:
65 **              fp -- the file pointer to be seek'd
66 **              timeout -- time to complete seek (milliseconds)
67 **              offset -- seek offset based on 'whence'
68 **              whence -- indicates where seek is relative from.
69 **                      One of SM_IO_SEEK_{CUR,SET,END}.
70 **      Returns:
71 **              Failure: returns -1 (minus 1) and sets errno
72 **              Success: returns 0 (zero)
73 */
74
75 int
76 sm_io_seek(fp, timeout, offset, whence)
77         register SM_FILE_T *fp;
78         int SM_NONVOLATILE timeout;
79         long SM_NONVOLATILE offset;
80         int SM_NONVOLATILE whence;
81 {
82         bool havepos;
83         off_t target, curoff;
84         size_t n;
85         struct stat st;
86         int ret;
87         SM_EVENT *evt = NULL;
88         register off_t (*seekfn) __P((SM_FILE_T *, off_t, int));
89
90         SM_REQUIRE_ISA(fp, SmFileMagic);
91
92         /* make sure stdio is set up */
93         if (!Sm_IO_DidInit)
94                 sm_init();
95
96         /* Have to be able to seek. */
97         if ((seekfn = fp->f_seek) == NULL)
98         {
99                 errno = ESPIPE;                 /* historic practice */
100                 return -1;
101         }
102
103         if (timeout == SM_TIME_DEFAULT)
104                 timeout = fp->f_timeout;
105         if (timeout == SM_TIME_IMMEDIATE)
106         {
107                 /*
108                 **  Filling the buffer will take time and we are wanted to
109                 **  return immediately. So...
110                 */
111
112                 errno = EAGAIN;
113                 return -1;
114         }
115
116 #define SM_SET_ALARM()                                          \
117         if (timeout != SM_TIME_FOREVER)                         \
118         {                                                       \
119                 if (setjmp(SeekTimeOut) != 0)                   \
120                 {                                               \
121                         errno = EAGAIN;                         \
122                         return -1;                              \
123                 }                                               \
124                 evt = sm_seteventm(timeout, seekalrm, 0);       \
125         }
126
127         /*
128         **  Change any SM_IO_SEEK_CUR to SM_IO_SEEK_SET, and check `whence'
129         **  argument. After this, whence is either SM_IO_SEEK_SET or
130         **  SM_IO_SEEK_END.
131         */
132
133         switch (whence)
134         {
135           case SM_IO_SEEK_CUR:
136
137                 /*
138                 **  In order to seek relative to the current stream offset,
139                 **  we have to first find the current stream offset a la
140                 **  ftell (see ftell for details).
141                 */
142
143                 /* may adjust seek offset on append stream */
144                 sm_flush(fp, (int *) &timeout);
145                 SM_SET_ALARM();
146                 if (fp->f_flags & SMOFF)
147                         curoff = fp->f_lseekoff;
148                 else
149                 {
150                         curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
151                         if (curoff == -1L)
152                         {
153                                 ret = -1;
154                                 goto clean;
155                         }
156                 }
157                 if (fp->f_flags & SMRD)
158                 {
159                         curoff -= fp->f_r;
160                         if (HASUB(fp))
161                                 curoff -= fp->f_ur;
162                 }
163                 else if (fp->f_flags & SMWR && fp->f_p != NULL)
164                         curoff += fp->f_p - fp->f_bf.smb_base;
165
166                 offset += curoff;
167                 whence = SM_IO_SEEK_SET;
168                 havepos = true;
169                 break;
170
171           case SM_IO_SEEK_SET:
172           case SM_IO_SEEK_END:
173                 SM_SET_ALARM();
174                 curoff = 0;             /* XXX just to keep gcc quiet */
175                 havepos = false;
176                 break;
177
178           default:
179                 errno = EINVAL;
180                 return -1;
181         }
182
183         /*
184         **  Can only optimise if:
185         **      reading (and not reading-and-writing);
186         **      not unbuffered; and
187         **      this is a `regular' Unix file (and hence seekfn==sm_stdseek).
188         **  We must check SMNBF first, because it is possible to have SMNBF
189         **  and SMSOPT both set.
190         */
191
192         if (fp->f_bf.smb_base == NULL)
193                 sm_makebuf(fp);
194         if (fp->f_flags & (SMWR | SMRW | SMNBF | SMNPT))
195                 goto dumb;
196         if ((fp->f_flags & SMOPT) == 0)
197         {
198                 if (seekfn != sm_stdseek ||
199                     fp->f_file < 0 || fstat(fp->f_file, &st) ||
200                     (st.st_mode & S_IFMT) != S_IFREG)
201                 {
202                         fp->f_flags |= SMNPT;
203                         goto dumb;
204                 }
205                 fp->f_blksize = st.st_blksize;
206                 fp->f_flags |= SMOPT;
207         }
208
209         /*
210         **  We are reading; we can try to optimise.
211         **  Figure out where we are going and where we are now.
212         */
213
214         if (whence == SM_IO_SEEK_SET)
215                 target = offset;
216         else
217         {
218                 if (fstat(fp->f_file, &st))
219                         goto dumb;
220                 target = st.st_size + offset;
221         }
222
223         if (!havepos)
224         {
225                 if (fp->f_flags & SMOFF)
226                         curoff = fp->f_lseekoff;
227                 else
228                 {
229                         curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
230                         if (curoff == POS_ERR)
231                                 goto dumb;
232                 }
233                 curoff -= fp->f_r;
234                 if (HASUB(fp))
235                         curoff -= fp->f_ur;
236         }
237
238         /*
239         **  Compute the number of bytes in the input buffer (pretending
240         **  that any ungetc() input has been discarded).  Adjust current
241         **  offset backwards by this count so that it represents the
242         **  file offset for the first byte in the current input buffer.
243         */
244
245         if (HASUB(fp))
246         {
247                 curoff += fp->f_r;      /* kill off ungetc */
248                 n = fp->f_up - fp->f_bf.smb_base;
249                 curoff -= n;
250                 n += fp->f_ur;
251         }
252         else
253         {
254                 n = fp->f_p - fp->f_bf.smb_base;
255                 curoff -= n;
256                 n += fp->f_r;
257         }
258
259         /*
260         **  If the target offset is within the current buffer,
261         **  simply adjust the pointers, clear SMFEOF, undo ungetc(),
262         **  and return.  (If the buffer was modified, we have to
263         **  skip this; see getln in fget.c.)
264         */
265
266         if (target >= curoff && target < curoff + (off_t) n)
267         {
268                 register int o = target - curoff;
269
270                 fp->f_p = fp->f_bf.smb_base + o;
271                 fp->f_r = n - o;
272                 if (HASUB(fp))
273                         FREEUB(fp);
274                 fp->f_flags &= ~SMFEOF;
275                 ret = 0;
276                 goto clean;
277         }
278
279         /*
280         **  The place we want to get to is not within the current buffer,
281         **  but we can still be kind to the kernel copyout mechanism.
282         **  By aligning the file offset to a block boundary, we can let
283         **  the kernel use the VM hardware to map pages instead of
284         **  copying bytes laboriously.  Using a block boundary also
285         **  ensures that we only read one block, rather than two.
286         */
287
288         curoff = target & ~(fp->f_blksize - 1);
289         if ((*seekfn)(fp, curoff, SM_IO_SEEK_SET) == POS_ERR)
290                 goto dumb;
291         fp->f_r = 0;
292         fp->f_p = fp->f_bf.smb_base;
293         if (HASUB(fp))
294                 FREEUB(fp);
295         fp->f_flags &= ~SMFEOF;
296         n = target - curoff;
297         if (n)
298         {
299                 /* Note: SM_TIME_FOREVER since fn timeout already set */
300                 if (sm_refill(fp, SM_TIME_FOREVER) || fp->f_r < (int) n)
301                         goto dumb;
302                 fp->f_p += n;
303                 fp->f_r -= n;
304         }
305
306         ret = 0;
307 clean:
308         /*  We're back. So undo our timeout and handler */
309         if (evt != NULL)
310                 sm_clrevent(evt);
311         return ret;
312 dumb:
313         /*
314         **  We get here if we cannot optimise the seek ... just
315         **  do it.  Allow the seek function to change fp->f_bf.smb_base.
316         */
317
318         /* Note: SM_TIME_FOREVER since fn timeout already set */
319         ret = SM_TIME_FOREVER;
320         if (sm_flush(fp, &ret) != 0 ||
321             (*seekfn)(fp, (off_t) offset, whence) == POS_ERR)
322         {
323                 ret = -1;
324                 goto clean;
325         }
326
327         /* success: clear SMFEOF indicator and discard ungetc() data */
328         if (HASUB(fp))
329                 FREEUB(fp);
330         fp->f_p = fp->f_bf.smb_base;
331         fp->f_r = 0;
332         fp->f_flags &= ~SMFEOF;
333         ret = 0;
334         goto clean;
335 }