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.47 2001/08/27 13:02:20 ca 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 }
280