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>
18SM_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
57int
58sm_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
277err:
278	/* errno set before goto places us here */
279	fp->f_flags |= SMERR;
280	return SM_IO_EOF;
281}
282