Import sendmail 8.13.6
[dragonfly.git] / contrib / sendmail-8.13.6 / libsm / fvwrite.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: fvwrite.c,v 1.49 2001/09/11 04:04:48 gshapiro Exp $")
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <signal.h>
22 #include <fcntl.h>
23 #include <sm/io.h>
24 #include <sm/setjmp.h>
25 #include <sm/conf.h>
26 #include "local.h"
27 #include "fvwrite.h"
28
29 /*
30 **  SM_FVWRITE -- write memory regions and buffer for file pointer
31 **
32 **      Parameters:
33 **              fp -- the file pointer to write to
34 **              timeout -- time length for function to return by
35 **              uio -- the memory regions to write
36 **
37 **      Returns:
38 **              Failure: returns SM_IO_EOF and sets errno
39 **              Success: returns 0 (zero)
40 **
41 **      This routine is large and unsightly, but most of the ugliness due
42 **      to the different kinds of output buffering handled here.
43 */
44
45 #define COPY(n)   (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
46 #define GETIOV(extra_work)              \
47         while (len == 0)                \
48         {                               \
49                 extra_work;             \
50                 p = iov->iov_base;      \
51                 len = iov->iov_len;     \
52                 iov++;                  \
53         }
54
55 int
56 sm_fvwrite(fp, timeout, uio)
57         register SM_FILE_T *fp;
58         int timeout;
59         register struct sm_uio *uio;
60 {
61         register size_t len;
62         register char *p;
63         register struct sm_iov *iov;
64         register int w, s;
65         char *nl;
66         int nlknown, nldist;
67         int fd;
68         struct timeval to;
69
70         if (uio->uio_resid == 0)
71                 return 0;
72
73         /* make sure we can write */
74         if (cantwrite(fp))
75         {
76                 errno = EBADF;
77                 return SM_IO_EOF;
78         }
79
80         SM_CONVERT_TIME(fp, fd, timeout, &to);
81
82         iov = uio->uio_iov;
83         p = iov->iov_base;
84         len = iov->iov_len;
85         iov++;
86         if (fp->f_flags & SMNBF)
87         {
88                 /* Unbuffered: write up to BUFSIZ bytes at a time. */
89                 do
90                 {
91                         GETIOV(;);
92                         errno = 0; /* needed to ensure EOF correctly found */
93                         w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ));
94                         if (w <= 0)
95                         {
96                                 if (w == 0 && errno == 0)
97                                         break; /* EOF found */
98                                 if (IS_IO_ERROR(fd, w, timeout))
99                                         goto err; /* errno set */
100
101                                 /* write would block */
102                                 SM_IO_WR_TIMEOUT(fp, fd, timeout);
103                                 w = 0;
104                         }
105                         else
106                         {
107                                 p += w;
108                                 len -= w;
109                         }
110                 } while ((uio->uio_resid -= w) != 0);
111         }
112         else if ((fp->f_flags & SMLBF) == 0)
113         {
114                 /*
115                 **  Not SMLBF (line-buffered). Either SMFBF or SMNOW
116                 **  buffered: fill partially full buffer, if any,
117                 **  and then flush.  If there is no partial buffer, write
118                 **  one bf._size byte chunk directly (without copying).
119                 **
120                 **  String output is a special case: write as many bytes
121                 **  as fit, but pretend we wrote everything.  This makes
122                 **  snprintf() return the number of bytes needed, rather
123                 **  than the number used, and avoids its write function
124                 **  (so that the write function can be invalid).
125                 */
126
127                 do
128                 {
129                         GETIOV(;);
130                         if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR))
131                             || ((fp->f_flags & SMNOW) != 0))
132                             && (size_t) fp->f_w < len)
133                         {
134                                 size_t blen = fp->f_p - fp->f_bf.smb_base;
135                                 unsigned char *tbase;
136                                 int tsize;
137
138                                 /* Allocate space exponentially. */
139                                 tsize = fp->f_bf.smb_size;
140                                 do
141                                 {
142                                         tsize = (tsize << 1) + 1;
143                                 } while ((size_t) tsize < blen + len);
144                                 tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base,
145                                                                      tsize + 1);
146                                 if (tbase == NULL)
147                                 {
148                                         errno = ENOMEM;
149                                         goto err; /* errno set */
150                                 }
151                                 fp->f_w += tsize - fp->f_bf.smb_size;
152                                 fp->f_bf.smb_base = tbase;
153                                 fp->f_bf.smb_size = tsize;
154                                 fp->f_p = tbase + blen;
155                         }
156                         w = fp->f_w;
157                         errno = 0; /* needed to ensure EOF correctly found */
158                         if (fp->f_flags & SMSTR)
159                         {
160                                 if (len < (size_t) w)
161                                         w = len;
162                                 COPY(w);        /* copy SM_MIN(fp->f_w,len), */
163                                 fp->f_w -= w;
164                                 fp->f_p += w;
165                                 w = len;        /* but pretend copied all */
166                         }
167                         else if (fp->f_p > fp->f_bf.smb_base
168                                  && len > (size_t) w)
169                         {
170                                 /* fill and flush */
171                                 COPY(w);
172                                 fp->f_p += w;
173                                 if (sm_flush(fp, &timeout))
174                                         goto err; /* errno set */
175                         }
176                         else if (len >= (size_t) (w = fp->f_bf.smb_size))
177                         {
178                                 /* write directly */
179                                 w = (*fp->f_write)(fp, p, w);
180                                 if (w <= 0)
181                                 {
182                                         if (w == 0 && errno == 0)
183                                                 break; /* EOF found */
184                                         if (IS_IO_ERROR(fd, w, timeout))
185                                                 goto err; /* errno set */
186
187                                         /* write would block */
188                                         SM_IO_WR_TIMEOUT(fp, fd, timeout);
189                                         w = 0;
190                                 }
191                         }
192                         else
193                         {
194                                 /* fill and done */
195                                 w = len;
196                                 COPY(w);
197                                 fp->f_w -= w;
198                                 fp->f_p += w;
199                         }
200                         p += w;
201                         len -= w;
202                 } while ((uio->uio_resid -= w) != 0);
203
204                 if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout))
205                         goto err; /* errno set */
206         }
207         else
208         {
209                 /*
210                 **  Line buffered: like fully buffered, but we
211                 **  must check for newlines.  Compute the distance
212                 **  to the first newline (including the newline),
213                 **  or `infinity' if there is none, then pretend
214                 **  that the amount to write is SM_MIN(len,nldist).
215                 */
216
217                 nlknown = 0;
218                 nldist = 0;     /* XXX just to keep gcc happy */
219                 do
220                 {
221                         GETIOV(nlknown = 0);
222                         if (!nlknown)
223                         {
224                                 nl = memchr((void *)p, '\n', len);
225                                 nldist = nl != NULL ? nl + 1 - p : len + 1;
226                                 nlknown = 1;
227                         }
228                         s = SM_MIN(len, ((size_t) nldist));
229                         w = fp->f_w + fp->f_bf.smb_size;
230                         errno = 0; /* needed to ensure EOF correctly found */
231                         if (fp->f_p > fp->f_bf.smb_base && s > w)
232                         {
233                                 COPY(w);
234                                 /* fp->f_w -= w; */
235                                 fp->f_p += w;
236                                 if (sm_flush(fp, &timeout))
237                                         goto err; /* errno set */
238                         }
239                         else if (s >= (w = fp->f_bf.smb_size))
240                         {
241                                 w = (*fp->f_write)(fp, p, w);
242                                 if (w <= 0)
243                                 {
244                                         if (w == 0 && errno == 0)
245                                                 break; /* EOF found */
246                                         if (IS_IO_ERROR(fd, w, timeout))
247                                                 goto err; /* errno set */
248
249                                         /* write would block */
250                                         SM_IO_WR_TIMEOUT(fp, fd, timeout);
251                                         w = 0;
252                                 }
253                         }
254                         else
255                         {
256                                 w = s;
257                                 COPY(w);
258                                 fp->f_w -= w;
259                                 fp->f_p += w;
260                         }
261                         if ((nldist -= w) == 0)
262                         {
263                                 /* copied the newline: flush and forget */
264                                 if (sm_flush(fp, &timeout))
265                                         goto err; /* errno set */
266                                 nlknown = 0;
267                         }
268                         p += w;
269                         len -= w;
270                 } while ((uio->uio_resid -= w) != 0);
271         }
272
273         return 0;
274
275 err:
276         /* errno set before goto places us here */
277         fp->f_flags |= SMERR;
278         return SM_IO_EOF;
279 }