fvwrite.c revision 90792
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>
16SM_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
55int
56sm_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
275err:
276	/* errno set before goto places us here */
277	fp->f_flags |= SMERR;
278	return SM_IO_EOF;
279}
280