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