/*
* 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