1155192Srwatson/*
2155192Srwatson * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
3155192Srwatson *      All rights reserved.
4155192Srwatson * Copyright (c) 1990, 1993
5155192Srwatson *	The Regents of the University of California.  All rights reserved.
6155192Srwatson *
7155192Srwatson * This code is derived from software contributed to Berkeley by
8155192Srwatson * Chris Torek.
9155192Srwatson *
10155192Srwatson * By using this file, you agree to the terms and conditions set
11155192Srwatson * forth in the LICENSE file which can be found at the top level of
12155192Srwatson * the sendmail distribution.
13155192Srwatson */
14155192Srwatson
15155192Srwatson#include <sm/gen.h>
16155192SrwatsonSM_RCSID("@(#)$Id: fvwrite.c,v 1.49 2001/09/11 04:04:48 gshapiro Exp $")
17155192Srwatson#include <stdlib.h>
18155192Srwatson#include <unistd.h>
19155192Srwatson#include <string.h>
20155192Srwatson#include <errno.h>
21155192Srwatson#include <signal.h>
22155192Srwatson#include <fcntl.h>
23155192Srwatson#include <sm/io.h>
24155192Srwatson#include <sm/setjmp.h>
25155192Srwatson#include <sm/conf.h>
26155192Srwatson#include "local.h"
27155192Srwatson#include "fvwrite.h"
28155192Srwatson
29155192Srwatson/*
30178186Srwatson**  SM_FVWRITE -- write memory regions and buffer for file pointer
31178186Srwatson**
32178186Srwatson**	Parameters:
33155192Srwatson**		fp -- the file pointer to write to
34155192Srwatson**		timeout -- time length for function to return by
35155192Srwatson**		uio -- the memory regions to write
36155192Srwatson**
37155192Srwatson**	Returns:
38155192Srwatson**		Failure: returns SM_IO_EOF and sets errno
39155192Srwatson**		Success: returns 0 (zero)
40155192Srwatson**
41155192Srwatson**	This routine is large and unsightly, but most of the ugliness due
42159277Srwatson**	to the different kinds of output buffering handled here.
43155192Srwatson*/
44155192Srwatson
45155192Srwatson#define COPY(n)	  (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
46155192Srwatson#define GETIOV(extra_work)		\
47155192Srwatson	while (len == 0)		\
48155192Srwatson	{				\
49155192Srwatson		extra_work;		\
50155192Srwatson		p = iov->iov_base;	\
51155192Srwatson		len = iov->iov_len;	\
52155192Srwatson		iov++;			\
53155192Srwatson	}
54155192Srwatson
55170196Srwatsonint
56170196Srwatsonsm_fvwrite(fp, timeout, uio)
57170196Srwatson	register SM_FILE_T *fp;
58170196Srwatson	int timeout;
59155192Srwatson	register struct sm_uio *uio;
60155192Srwatson{
61155192Srwatson	register size_t len;
62155192Srwatson	register char *p;
63155192Srwatson	register struct sm_iov *iov;
64170585Srwatson	register int w, s;
65155192Srwatson	char *nl;
66155192Srwatson	int nlknown, nldist;
67155192Srwatson	int fd;
68155192Srwatson	struct timeval to;
69155192Srwatson
70155192Srwatson	if (uio->uio_resid == 0)
71155192Srwatson		return 0;
72155192Srwatson
73155192Srwatson	/* make sure we can write */
74155192Srwatson	if (cantwrite(fp))
75155192Srwatson	{
76155192Srwatson		errno = EBADF;
77155192Srwatson		return SM_IO_EOF;
78155192Srwatson	}
79155192Srwatson
80155192Srwatson	SM_CONVERT_TIME(fp, fd, timeout, &to);
81155192Srwatson
82155192Srwatson	iov = uio->uio_iov;
83155192Srwatson	p = iov->iov_base;
84155192Srwatson	len = iov->iov_len;
85155192Srwatson	iov++;
86155192Srwatson	if (fp->f_flags & SMNBF)
87155192Srwatson	{
88155192Srwatson		/* Unbuffered: write up to BUFSIZ bytes at a time. */
89155192Srwatson		do
90155192Srwatson		{
91155192Srwatson			GETIOV(;);
92155192Srwatson			errno = 0; /* needed to ensure EOF correctly found */
93155192Srwatson			w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ));
94155192Srwatson			if (w <= 0)
95155192Srwatson			{
96155192Srwatson				if (w == 0 && errno == 0)
97155192Srwatson					break; /* EOF found */
98155192Srwatson				if (IS_IO_ERROR(fd, w, timeout))
99155192Srwatson					goto err; /* errno set */
100155192Srwatson
101155192Srwatson				/* write would block */
102155192Srwatson				SM_IO_WR_TIMEOUT(fp, fd, timeout);
103155192Srwatson				w = 0;
104155192Srwatson			}
105155192Srwatson			else
106155192Srwatson			{
107155192Srwatson				p += w;
108155192Srwatson				len -= w;
109155192Srwatson			}
110155192Srwatson		} while ((uio->uio_resid -= w) != 0);
111155192Srwatson	}
112155192Srwatson	else if ((fp->f_flags & SMLBF) == 0)
113155192Srwatson	{
114155192Srwatson		/*
115155192Srwatson		**  Not SMLBF (line-buffered). Either SMFBF or SMNOW
116155192Srwatson		**  buffered: fill partially full buffer, if any,
117155192Srwatson		**  and then flush.  If there is no partial buffer, write
118155192Srwatson		**  one bf._size byte chunk directly (without copying).
119155192Srwatson		**
120155192Srwatson		**  String output is a special case: write as many bytes
121155192Srwatson		**  as fit, but pretend we wrote everything.  This makes
122155192Srwatson		**  snprintf() return the number of bytes needed, rather
123155192Srwatson		**  than the number used, and avoids its write function
124155192Srwatson		**  (so that the write function can be invalid).
125155192Srwatson		*/
126155192Srwatson
127155192Srwatson		do
128155192Srwatson		{
129155192Srwatson			GETIOV(;);
130155192Srwatson			if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR))
131155192Srwatson			    || ((fp->f_flags & SMNOW) != 0))
132155192Srwatson			    && (size_t) fp->f_w < len)
133155192Srwatson			{
134155192Srwatson				size_t blen = fp->f_p - fp->f_bf.smb_base;
135155192Srwatson				unsigned char *tbase;
136155192Srwatson				int tsize;
137155192Srwatson
138155192Srwatson				/* Allocate space exponentially. */
139155192Srwatson				tsize = fp->f_bf.smb_size;
140155192Srwatson				do
141155192Srwatson				{
142155192Srwatson					tsize = (tsize << 1) + 1;
143155192Srwatson				} while ((size_t) tsize < blen + len);
144155192Srwatson				tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base,
145155192Srwatson								     tsize + 1);
146155192Srwatson				if (tbase == NULL)
147155192Srwatson				{
148155192Srwatson					errno = ENOMEM;
149155192Srwatson					goto err; /* errno set */
150155192Srwatson				}
151155192Srwatson				fp->f_w += tsize - fp->f_bf.smb_size;
152155192Srwatson				fp->f_bf.smb_base = tbase;
153155192Srwatson				fp->f_bf.smb_size = tsize;
154155192Srwatson				fp->f_p = tbase + blen;
155155192Srwatson			}
156155192Srwatson			w = fp->f_w;
157155192Srwatson			errno = 0; /* needed to ensure EOF correctly found */
158155192Srwatson			if (fp->f_flags & SMSTR)
159155192Srwatson			{
160155192Srwatson				if (len < (size_t) w)
161155192Srwatson					w = len;
162155192Srwatson				COPY(w);	/* copy SM_MIN(fp->f_w,len), */
163155192Srwatson				fp->f_w -= w;
164155192Srwatson				fp->f_p += w;
165155192Srwatson				w = len;	/* but pretend copied all */
166155192Srwatson			}
167155192Srwatson			else if (fp->f_p > fp->f_bf.smb_base
168155192Srwatson				 && len > (size_t) w)
169155192Srwatson			{
170155192Srwatson				/* fill and flush */
171155192Srwatson				COPY(w);
172155192Srwatson				fp->f_p += w;
173155192Srwatson				if (sm_flush(fp, &timeout))
174155192Srwatson					goto err; /* errno set */
175155192Srwatson			}
176155192Srwatson			else if (len >= (size_t) (w = fp->f_bf.smb_size))
177155192Srwatson			{
178155192Srwatson				/* write directly */
179155192Srwatson				w = (*fp->f_write)(fp, p, w);
180155192Srwatson				if (w <= 0)
181155192Srwatson				{
182155192Srwatson					if (w == 0 && errno == 0)
183155192Srwatson						break; /* EOF found */
184155192Srwatson					if (IS_IO_ERROR(fd, w, timeout))
185155192Srwatson						goto err; /* errno set */
186155192Srwatson
187155192Srwatson					/* write would block */
188155192Srwatson					SM_IO_WR_TIMEOUT(fp, fd, timeout);
189155192Srwatson					w = 0;
190155192Srwatson				}
191155192Srwatson			}
192155192Srwatson			else
193155192Srwatson			{
194155192Srwatson				/* fill and done */
195155192Srwatson				w = len;
196155192Srwatson				COPY(w);
197155192Srwatson				fp->f_w -= w;
198155192Srwatson				fp->f_p += w;
199155192Srwatson			}
200155192Srwatson			p += w;
201155192Srwatson			len -= w;
202155192Srwatson		} while ((uio->uio_resid -= w) != 0);
203155192Srwatson
204155192Srwatson		if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout))
205155192Srwatson			goto err; /* errno set */
206155192Srwatson	}
207155192Srwatson	else
208155192Srwatson	{
209155192Srwatson		/*
210155192Srwatson		**  Line buffered: like fully buffered, but we
211155192Srwatson		**  must check for newlines.  Compute the distance
212155192Srwatson		**  to the first newline (including the newline),
213155192Srwatson		**  or `infinity' if there is none, then pretend
214155192Srwatson		**  that the amount to write is SM_MIN(len,nldist).
215155192Srwatson		*/
216155192Srwatson
217155192Srwatson		nlknown = 0;
218155192Srwatson		nldist = 0;	/* XXX just to keep gcc happy */
219155192Srwatson		do
220155192Srwatson		{
221155192Srwatson			GETIOV(nlknown = 0);
222155192Srwatson			if (!nlknown)
223155192Srwatson			{
224155192Srwatson				nl = memchr((void *)p, '\n', len);
225155192Srwatson				nldist = nl != NULL ? nl + 1 - p : len + 1;
226155192Srwatson				nlknown = 1;
227155192Srwatson			}
228155192Srwatson			s = SM_MIN(len, ((size_t) nldist));
229155192Srwatson			w = fp->f_w + fp->f_bf.smb_size;
230155192Srwatson			errno = 0; /* needed to ensure EOF correctly found */
231155192Srwatson			if (fp->f_p > fp->f_bf.smb_base && s > w)
232155192Srwatson			{
233155192Srwatson				COPY(w);
234155192Srwatson				/* fp->f_w -= w; */
235155192Srwatson				fp->f_p += w;
236155192Srwatson				if (sm_flush(fp, &timeout))
237155192Srwatson					goto err; /* errno set */
238155192Srwatson			}
239155192Srwatson			else if (s >= (w = fp->f_bf.smb_size))
240155192Srwatson			{
241155192Srwatson				w = (*fp->f_write)(fp, p, w);
242155192Srwatson				if (w <= 0)
243155192Srwatson				{
244155192Srwatson					if (w == 0 && errno == 0)
245155192Srwatson						break; /* EOF found */
246155192Srwatson					if (IS_IO_ERROR(fd, w, timeout))
247155192Srwatson						goto err; /* errno set */
248155192Srwatson
249155192Srwatson					/* write would block */
250155192Srwatson					SM_IO_WR_TIMEOUT(fp, fd, timeout);
251155192Srwatson					w = 0;
252155192Srwatson				}
253155192Srwatson			}
254155192Srwatson			else
255155192Srwatson			{
256155192Srwatson				w = s;
257155192Srwatson				COPY(w);
258155192Srwatson				fp->f_w -= w;
259155192Srwatson				fp->f_p += w;
260155192Srwatson			}
261155192Srwatson			if ((nldist -= w) == 0)
262155192Srwatson			{
263155192Srwatson				/* copied the newline: flush and forget */
264155192Srwatson				if (sm_flush(fp, &timeout))
265155192Srwatson					goto err; /* errno set */
266155192Srwatson				nlknown = 0;
267155192Srwatson			}
268155192Srwatson			p += w;
269155192Srwatson			len -= w;
270155192Srwatson		} while ((uio->uio_resid -= w) != 0);
271155192Srwatson	}
272155192Srwatson
273155192Srwatson	return 0;
274155192Srwatson
275155192Srwatsonerr:
276155192Srwatson	/* errno set before goto places us here */
277155192Srwatson	fp->f_flags |= SMERR;
278155192Srwatson	return SM_IO_EOF;
279155192Srwatson}
280155192Srwatson