Merge from vendor branch SENDMAIL:
[dragonfly.git] / contrib / sendmail-8.13.7 / libsm / refill.c
1 /*
2  * Copyright (c) 2000-2001, 2005-2006 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: refill.c,v 1.53 2006/02/28 18:48:25 ca Exp $")
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <setjmp.h>
21 #include <signal.h>
22 #include <sm/time.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <sm/io.h>
26 #include <sm/conf.h>
27 #include <sm/assert.h>
28 #include "local.h"
29
30 static int sm_lflush __P((SM_FILE_T *, int *));
31
32 /*
33 **  SM_IO_RD_TIMEOUT -- measured timeout for reads
34 **
35 **  This #define uses a select() to wait for the 'fd' to become readable.
36 **  The select() can be active for up to 'To' time. The select() may not
37 **  use all of the the 'To' time. Hence, the amount of "wall-clock" time is
38 **  measured to decide how much to subtract from 'To' to update it. On some
39 **  BSD-based/like systems the timeout for a select() is updated for the
40 **  amount of time used. On many/most systems this does not happen. Therefore
41 **  the updating of 'To' must be done ourselves; a copy of 'To' is passed
42 **  since a BSD-like system will have updated it and we don't want to
43 **  double the time used!
44 **  Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
45 **  sendmail buffered file type in sendmail/bf.c; see use below).
46 **
47 **      Parameters
48 **              fp -- the file pointer for the active file
49 **              fd -- raw file descriptor (from 'fp') to use for select()
50 **              to -- struct timeval of the timeout
51 **              timeout -- the original timeout value
52 **              sel_ret -- the return value from the select()
53 **
54 **      Returns:
55 **              nothing, flow through code
56 */
57
58 #define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret)                  \
59 {                                                                       \
60         struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff;  \
61         fd_set sm_io_to_mask, sm_io_x_mask;                             \
62         errno = 0;                                                      \
63         if (timeout == SM_TIME_IMMEDIATE)                               \
64         {                                                               \
65                 errno = EAGAIN;                                         \
66                 return SM_IO_EOF;                                       \
67         }                                                               \
68         if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE)                       \
69         {                                                               \
70                 errno = EINVAL;                                         \
71                 return SM_IO_EOF;                                       \
72         }                                                               \
73         FD_ZERO(&sm_io_to_mask);                                        \
74         FD_SET((fd), &sm_io_to_mask);                                   \
75         FD_ZERO(&sm_io_x_mask);                                         \
76         FD_SET((fd), &sm_io_x_mask);                                    \
77         if (gettimeofday(&sm_io_to_before, NULL) < 0)                   \
78                 return SM_IO_EOF;                                       \
79         do                                                              \
80         {                                                               \
81                 (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL,      \
82                                 &sm_io_x_mask, (to));                   \
83         } while ((sel_ret) < 0 && errno == EINTR);                      \
84         if ((sel_ret) < 0)                                              \
85         {                                                               \
86                 /* something went wrong, errno set */                   \
87                 fp->f_r = 0;                                            \
88                 fp->f_flags |= SMERR;                                   \
89                 return SM_IO_EOF;                                       \
90         }                                                               \
91         else if ((sel_ret) == 0)                                        \
92         {                                                               \
93                 /* timeout */                                           \
94                 errno = EAGAIN;                                         \
95                 return SM_IO_EOF;                                       \
96         }                                                               \
97         /* calulate wall-clock time used */                             \
98         if (gettimeofday(&sm_io_to_after, NULL) < 0)                    \
99                 return SM_IO_EOF;                                       \
100         timersub(&sm_io_to_after, &sm_io_to_before, &sm_io_to_diff);    \
101         timersub((to), &sm_io_to_diff, (to));                           \
102 }
103
104 /*
105 **  SM_LFLUSH -- flush a file if it is line buffered and writable
106 **
107 **      Parameters:
108 **              fp -- file pointer to flush
109 **              timeout -- original timeout value (in milliseconds)
110 **
111 **      Returns:
112 **              Failure: returns SM_IO_EOF and sets errno
113 **              Success: returns 0
114 */
115
116 static int
117 sm_lflush(fp, timeout)
118         SM_FILE_T *fp;
119         int *timeout;
120 {
121
122         if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR))
123                 return sm_flush(fp, timeout);
124         return 0;
125 }
126
127 /*
128 **  SM_REFILL -- refill a buffer
129 **
130 **      Parameters:
131 **              fp -- file pointer for buffer refill
132 **              timeout -- time to complete filling the buffer in milliseconds
133 **
134 **      Returns:
135 **              Success: returns 0
136 **              Failure: returns SM_IO_EOF
137 */
138
139 int
140 sm_refill(fp, timeout)
141         register SM_FILE_T *fp;
142         int timeout;
143 {
144         int ret, r;
145         struct timeval to;
146         int fd;
147
148         if (timeout == SM_TIME_DEFAULT)
149                 timeout = fp->f_timeout;
150         if (timeout == SM_TIME_IMMEDIATE)
151         {
152                 /*
153                 **  Filling the buffer will take time and we are wanted to
154                 **  return immediately. And we're not EOF or ERR really.
155                 **  So... the failure is we couldn't do it in time.
156                 */
157
158                 errno = EAGAIN;
159                 fp->f_r = 0; /* just to be sure */
160                 return 0;
161         }
162
163         /* make sure stdio is set up */
164         if (!Sm_IO_DidInit)
165                 sm_init();
166
167         fp->f_r = 0;            /* largely a convenience for callers */
168
169         if (fp->f_flags & SMFEOF)
170                 return SM_IO_EOF;
171
172         SM_CONVERT_TIME(fp, fd, timeout, &to);
173
174         /* if not already reading, have to be reading and writing */
175         if ((fp->f_flags & SMRD) == 0)
176         {
177                 if ((fp->f_flags & SMRW) == 0)
178                 {
179                         errno = EBADF;
180                         fp->f_flags |= SMERR;
181                         return SM_IO_EOF;
182                 }
183
184                 /* switch to reading */
185                 if (fp->f_flags & SMWR)
186                 {
187                         if (sm_flush(fp, &timeout))
188                                 return SM_IO_EOF;
189                         fp->f_flags &= ~SMWR;
190                         fp->f_w = 0;
191                         fp->f_lbfsize = 0;
192                 }
193                 fp->f_flags |= SMRD;
194         }
195         else
196         {
197                 /*
198                 **  We were reading.  If there is an ungetc buffer,
199                 **  we must have been reading from that.  Drop it,
200                 **  restoring the previous buffer (if any).  If there
201                 **  is anything in that buffer, return.
202                 */
203
204                 if (HASUB(fp))
205                 {
206                         FREEUB(fp);
207                         if ((fp->f_r = fp->f_ur) != 0)
208                         {
209                                 fp->f_p = fp->f_up;
210
211                                 /* revert blocking state */
212                                 return 0;
213                         }
214                 }
215         }
216
217         if (fp->f_bf.smb_base == NULL)
218                 sm_makebuf(fp);
219
220         /*
221         **  Before reading from a line buffered or unbuffered file,
222         **  flush all line buffered output files, per the ANSI C standard.
223         */
224
225         if (fp->f_flags & (SMLBF|SMNBF))
226                 (void) sm_fwalk(sm_lflush, &timeout);
227
228         /*
229         **  If this file is linked to another, and we are going to hang
230         **  on the read, flush the linked file before continuing.
231         */
232
233         if (fp->f_flushfp != NULL &&
234             (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0)
235                 sm_flush(fp->f_flushfp, &timeout);
236
237         fp->f_p = fp->f_bf.smb_base;
238
239         /*
240         **  The do-while loop stops trying to read when something is read
241         **  or it appears that the timeout has expired before finding
242         **  something available to be read (via select()).
243         */
244
245         ret = 0;
246         do
247         {
248                 errno = 0; /* needed to ensure EOF correctly found */
249                 r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size);
250                 if (r <= 0)
251                 {
252                         if (r == 0 && errno == 0)
253                                 break; /* EOF found */
254                         if (IS_IO_ERROR(fd, r, timeout))
255                                 goto err; /* errno set */
256
257                         /* read would block */
258                         SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret);
259                 }
260         } while (r <= 0 && ret > 0);
261
262 err:
263         if (r <= 0)
264         {
265                 if (r == 0)
266                         fp->f_flags |= SMFEOF;
267                 else
268                         fp->f_flags |= SMERR;
269                 fp->f_r = 0;
270                 return SM_IO_EOF;
271         }
272         fp->f_r = r;
273         return 0;
274 }
275
276 /*
277 **  SM_RGET -- refills buffer and returns first character
278 **
279 **  Handle sm_getc() when the buffer ran out:
280 **  Refill, then return the first character in the newly-filled buffer.
281 **
282 **      Parameters:
283 **              fp -- file pointer to work on
284 **              timeout -- time to complete refill
285 **
286 **      Returns:
287 **              Success: first character in refilled buffer as an int
288 **              Failure: SM_IO_EOF
289 */
290
291 int
292 sm_rget(fp, timeout)
293         register SM_FILE_T *fp;
294         int timeout;
295 {
296         if (sm_refill(fp, timeout) == 0)
297         {
298                 fp->f_r--;
299                 return *fp->f_p++;
300         }
301         return SM_IO_EOF;
302 }