/*
 * Copyright (c) 2000-2002, 2004, 2005 Sendmail, Inc. and its suppliers.
 *      All rights reserved.
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: fvwrite.c,v 1.22 2005/03/15 19:56:07 ca Exp $")
#include "sm/error.h"
#include "sm/time.h"
#include "sm/fcntl.h"
#include "sm/io.h"
#include "sm/setjmp.h"
#include "io-int.h"
#include "fvwrite.h"

/*
**  SM_FVWRITE -- write memory regions and buffer for file pointer
**
**	Parameters:
**		fp -- the file pointer to write to
**		uio -- the memory regions to write
**
**	Returns:
**		usual sm_error and usually modified uio (resid)
**
**	This routine is large and unsightly, but most of the ugliness due
**	to the different kinds of output buffering handled here.
*/

/*
**  XXX what about error handling? what kind of errors are returned
**  by f_write(), esp.: timeout, EAGAIN, EINTR, etc?
*/

#define COPYF_P(n) (void) sm_memcpy((void *)f_p(*fp), (void *)p, (size_t)(n))
#define GETIOV(extra_work)		\
	while (len == 0)		\
	{				\
		extra_work;		\
		p = iov->iov_base;	\
		len = iov->iov_len;	\
		iov++;			\
	}

sm_ret_T
sm_fvwrite(sm_file_T *fp, sm_uio_T *uio)
{
	size_t len;
	uchar *p;
	sm_iov_T *iov;
	int w;
	ssize_t n;
	sm_ret_T res;

	if (uio->uio_resid == 0)
		return SM_SUCCESS;

	/* make sure we can write */
	if (cantwrite(fp))
		return sm_error_perm(SM_EM_IO, EBADF);

	iov = uio->uio_iov;
	p = iov->iov_base;
	len = iov->iov_len;
	iov++;
	res = SM_SUCCESS;
	if (f_flags(*fp) & SMNBF)
	{
		/* Unbuffered: write up to BUFSIZ bytes at a time. */
		do
		{
			GETIOV(;);
			res = (*f_write(*fp))(fp, p, SM_MIN(len, SM_IO_BUFSIZ),
						&n);
			if (sm_is_err(res))
				goto err;
			w = n;

			p += w;
			len -= w;
		} while ((uio->uio_resid -= w) != 0);
	}
	else
	{
		/*
		**  SMFBF buffered: fill partially full buffer, if any,
		**  and then flush.  If there is no partial buffer, write
		**  one f_bf.smb_size byte chunk directly (without copying).
		**
		**  String output is a special case: write as many bytes
		**  as fit, but pretend we wrote everything.  This makes
		**  snprintf() return the number of bytes needed, rather
		**  than the number used, and avoids its write function
		**  (so that the write function can be invalid).
		*/

		do
		{
			GETIOV(;);
			if (((f_flags(*fp) & (SMALC|SMSTR)) == (SMALC|SMSTR))
			    && (size_t) f_w(*fp) < len)
			{
				size_t blen;
				int tsize;
				uchar *tbase;

				/* Allocate space exponentially. */
				tsize = f_bfsize(*fp);
				blen = f_p(*fp) - f_bfbase(*fp);
				do
				{
					tsize = (tsize << 1) + 1;
				} while ((size_t) tsize < blen + len);
				tbase = (uchar *) sm_realloc(f_bfbase(*fp),
							     tsize + 1);
				if (tbase == NULL)
				{
					res = sm_error_temp(SM_EM_IO, ENOMEM);
					goto err;
				}
				f_w(*fp) += tsize - f_bfsize(*fp);
				f_bfbase(*fp) = tbase;
				f_bfsize(*fp) = tsize;
				f_p(*fp) = tbase + blen;
			}
			w = f_w(*fp);
			if (f_flags(*fp) & SMSTR)
			{
				if (len < (size_t) w)
					w = len;
				if (w > 0)
				{
					COPYF_P(w);	/* copy MIN(w,len) */
					f_w(*fp) -= w;
					f_p(*fp) += w;
				}
				w = len;	/* but pretend copied all */
			}
#ifdef SMSTRSTR
			/* str? */
			else if (f_flags(*fp) & SMSTRSTR)
			{
				if (len < (size_t) w)
					w = len;
				if (w > 0)
				{
					res = sm_str_scatn(f_cookie(*fp),
							(char *) p, w);
					if (sm_is_err(res))
						goto err;
					f_w(*fp) -= w;
					f_p(*fp) += w;
				}
				w = len;	/* but pretend copied all */
			}
#endif /* SMSTRSTR */
			else if (f_p(*fp) > f_bfbase(*fp)
				 && len > (size_t) w)
			{
				/* fill and flush */
				COPYF_P(w);
				f_p(*fp) += w;
				res = sm_flush(fp);
				if (sm_is_err(res))
					goto err;
			}
			else if (len >= (size_t) (w = f_bfsize(*fp)))
			{
				/* write directly; it doesn't fit in buffer */
				res = (*f_write(*fp))(fp, p, w, &n);
				if (sm_is_err(res))
					goto err;
				w = n;
			}
			else
			{
				/* fill and done */
				w = len;
				COPYF_P(w);
				f_w(*fp) -= w;
				f_p(*fp) += w;
			}
			p += w;
			len -= w;
		} while ((uio->uio_resid -= w) != 0);
	}
	return SM_SUCCESS;

err:
	/* res set before goto places us here */
	f_flags(*fp) |= SMERR;
#if SM_IO_ERR_VAL
	f_error(*fp) = res;
#endif
	return res;
}


syntax highlighted by Code2HTML, v. 0.9.1