fvwrite.c revision 182352
1251607Sdim/*
2251607Sdim * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
3251607Sdim *      All rights reserved.
4251607Sdim * Copyright (c) 1990, 1993
5251607Sdim *	The Regents of the University of California.  All rights reserved.
6251607Sdim *
7251607Sdim * This code is derived from software contributed to Berkeley by
8251607Sdim * Chris Torek.
9251607Sdim *
10251607Sdim * By using this file, you agree to the terms and conditions set
11251607Sdim * forth in the LICENSE file which can be found at the top level of
12251607Sdim * the sendmail distribution.
13251607Sdim */
14251607Sdim
15251607Sdim#include <sm/gen.h>
16251607SdimSM_RCSID("@(#)$Id: fvwrite.c,v 1.49 2001/09/11 04:04:48 gshapiro Exp $")
17251607Sdim#include <stdlib.h>
18251607Sdim#include <unistd.h>
19251607Sdim#include <string.h>
20251607Sdim#include <errno.h>
21251607Sdim#include <signal.h>
22251607Sdim#include <fcntl.h>
23251607Sdim#include <sm/io.h>
24251607Sdim#include <sm/setjmp.h>
25251607Sdim#include <sm/conf.h>
26251607Sdim#include "local.h"
27251607Sdim#include "fvwrite.h"
28251607Sdim
29251607Sdim/*
30251607Sdim**  SM_FVWRITE -- write memory regions and buffer for file pointer
31251607Sdim**
32251607Sdim**	Parameters:
33251607Sdim**		fp -- the file pointer to write to
34251607Sdim**		timeout -- time length for function to return by
35251607Sdim**		uio -- the memory regions to write
36251607Sdim**
37251607Sdim**	Returns:
38251607Sdim**		Failure: returns SM_IO_EOF and sets errno
39251607Sdim**		Success: returns 0 (zero)
40251607Sdim**
41251607Sdim**	This routine is large and unsightly, but most of the ugliness due
42251607Sdim**	to the different kinds of output buffering handled here.
43251607Sdim*/
44251607Sdim
45251607Sdim#define COPY(n)	  (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
46251607Sdim#define GETIOV(extra_work)		\
47251607Sdim	while (len == 0)		\
48251607Sdim	{				\
49251607Sdim		extra_work;		\
50251607Sdim		p = iov->iov_base;	\
51251607Sdim		len = iov->iov_len;	\
52251607Sdim		iov++;			\
53251607Sdim	}
54251607Sdim
55251607Sdimint
56251607Sdimsm_fvwrite(fp, timeout, uio)
57251607Sdim	register SM_FILE_T *fp;
58251607Sdim	int timeout;
59251607Sdim	register struct sm_uio *uio;
60251607Sdim{
61251607Sdim	register size_t len;
62251607Sdim	register char *p;
63251607Sdim	register struct sm_iov *iov;
64251607Sdim	register int w, s;
65251607Sdim	char *nl;
66251607Sdim	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