ea50d71feb
prevents doing this in one pass.
280 lines
6.2 KiB
C
280 lines
6.2 KiB
C
/*
|
|
* Copyright (c) 2000-2001 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/gen.h>
|
|
SM_RCSID("@(#)$Id: fvwrite.c,v 1.49 2001/09/11 04:04:48 gshapiro Exp $")
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <sm/io.h>
|
|
#include <sm/setjmp.h>
|
|
#include <sm/conf.h>
|
|
#include "local.h"
|
|
#include "fvwrite.h"
|
|
|
|
/*
|
|
** SM_FVWRITE -- write memory regions and buffer for file pointer
|
|
**
|
|
** Parameters:
|
|
** fp -- the file pointer to write to
|
|
** timeout -- time length for function to return by
|
|
** uio -- the memory regions to write
|
|
**
|
|
** Returns:
|
|
** Failure: returns SM_IO_EOF and sets errno
|
|
** Success: returns 0 (zero)
|
|
**
|
|
** This routine is large and unsightly, but most of the ugliness due
|
|
** to the different kinds of output buffering handled here.
|
|
*/
|
|
|
|
#define COPY(n) (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
|
|
#define GETIOV(extra_work) \
|
|
while (len == 0) \
|
|
{ \
|
|
extra_work; \
|
|
p = iov->iov_base; \
|
|
len = iov->iov_len; \
|
|
iov++; \
|
|
}
|
|
|
|
int
|
|
sm_fvwrite(fp, timeout, uio)
|
|
register SM_FILE_T *fp;
|
|
int timeout;
|
|
register struct sm_uio *uio;
|
|
{
|
|
register size_t len;
|
|
register char *p;
|
|
register struct sm_iov *iov;
|
|
register int w, s;
|
|
char *nl;
|
|
int nlknown, nldist;
|
|
int fd;
|
|
struct timeval to;
|
|
|
|
if (uio->uio_resid == 0)
|
|
return 0;
|
|
|
|
/* make sure we can write */
|
|
if (cantwrite(fp))
|
|
{
|
|
errno = EBADF;
|
|
return SM_IO_EOF;
|
|
}
|
|
|
|
SM_CONVERT_TIME(fp, fd, timeout, &to);
|
|
|
|
iov = uio->uio_iov;
|
|
p = iov->iov_base;
|
|
len = iov->iov_len;
|
|
iov++;
|
|
if (fp->f_flags & SMNBF)
|
|
{
|
|
/* Unbuffered: write up to BUFSIZ bytes at a time. */
|
|
do
|
|
{
|
|
GETIOV(;);
|
|
errno = 0; /* needed to ensure EOF correctly found */
|
|
w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ));
|
|
if (w <= 0)
|
|
{
|
|
if (w == 0 && errno == 0)
|
|
break; /* EOF found */
|
|
if (IS_IO_ERROR(fd, w, timeout))
|
|
goto err; /* errno set */
|
|
|
|
/* write would block */
|
|
SM_IO_WR_TIMEOUT(fp, fd, timeout);
|
|
w = 0;
|
|
}
|
|
else
|
|
{
|
|
p += w;
|
|
len -= w;
|
|
}
|
|
} while ((uio->uio_resid -= w) != 0);
|
|
}
|
|
else if ((fp->f_flags & SMLBF) == 0)
|
|
{
|
|
/*
|
|
** Not SMLBF (line-buffered). Either SMFBF or SMNOW
|
|
** buffered: fill partially full buffer, if any,
|
|
** and then flush. If there is no partial buffer, write
|
|
** one bf._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 ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR))
|
|
|| ((fp->f_flags & SMNOW) != 0))
|
|
&& (size_t) fp->f_w < len)
|
|
{
|
|
size_t blen = fp->f_p - fp->f_bf.smb_base;
|
|
unsigned char *tbase;
|
|
int tsize;
|
|
|
|
/* Allocate space exponentially. */
|
|
tsize = fp->f_bf.smb_size;
|
|
do
|
|
{
|
|
tsize = (tsize << 1) + 1;
|
|
} while ((size_t) tsize < blen + len);
|
|
tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base,
|
|
tsize + 1);
|
|
if (tbase == NULL)
|
|
{
|
|
errno = ENOMEM;
|
|
goto err; /* errno set */
|
|
}
|
|
fp->f_w += tsize - fp->f_bf.smb_size;
|
|
fp->f_bf.smb_base = tbase;
|
|
fp->f_bf.smb_size = tsize;
|
|
fp->f_p = tbase + blen;
|
|
}
|
|
w = fp->f_w;
|
|
errno = 0; /* needed to ensure EOF correctly found */
|
|
if (fp->f_flags & SMSTR)
|
|
{
|
|
if (len < (size_t) w)
|
|
w = len;
|
|
COPY(w); /* copy SM_MIN(fp->f_w,len), */
|
|
fp->f_w -= w;
|
|
fp->f_p += w;
|
|
w = len; /* but pretend copied all */
|
|
}
|
|
else if (fp->f_p > fp->f_bf.smb_base
|
|
&& len > (size_t) w)
|
|
{
|
|
/* fill and flush */
|
|
COPY(w);
|
|
fp->f_p += w;
|
|
if (sm_flush(fp, &timeout))
|
|
goto err; /* errno set */
|
|
}
|
|
else if (len >= (size_t) (w = fp->f_bf.smb_size))
|
|
{
|
|
/* write directly */
|
|
w = (*fp->f_write)(fp, p, w);
|
|
if (w <= 0)
|
|
{
|
|
if (w == 0 && errno == 0)
|
|
break; /* EOF found */
|
|
if (IS_IO_ERROR(fd, w, timeout))
|
|
goto err; /* errno set */
|
|
|
|
/* write would block */
|
|
SM_IO_WR_TIMEOUT(fp, fd, timeout);
|
|
w = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* fill and done */
|
|
w = len;
|
|
COPY(w);
|
|
fp->f_w -= w;
|
|
fp->f_p += w;
|
|
}
|
|
p += w;
|
|
len -= w;
|
|
} while ((uio->uio_resid -= w) != 0);
|
|
|
|
if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout))
|
|
goto err; /* errno set */
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
** Line buffered: like fully buffered, but we
|
|
** must check for newlines. Compute the distance
|
|
** to the first newline (including the newline),
|
|
** or `infinity' if there is none, then pretend
|
|
** that the amount to write is SM_MIN(len,nldist).
|
|
*/
|
|
|
|
nlknown = 0;
|
|
nldist = 0; /* XXX just to keep gcc happy */
|
|
do
|
|
{
|
|
GETIOV(nlknown = 0);
|
|
if (!nlknown)
|
|
{
|
|
nl = memchr((void *)p, '\n', len);
|
|
nldist = nl != NULL ? nl + 1 - p : len + 1;
|
|
nlknown = 1;
|
|
}
|
|
s = SM_MIN(len, ((size_t) nldist));
|
|
w = fp->f_w + fp->f_bf.smb_size;
|
|
errno = 0; /* needed to ensure EOF correctly found */
|
|
if (fp->f_p > fp->f_bf.smb_base && s > w)
|
|
{
|
|
COPY(w);
|
|
/* fp->f_w -= w; */
|
|
fp->f_p += w;
|
|
if (sm_flush(fp, &timeout))
|
|
goto err; /* errno set */
|
|
}
|
|
else if (s >= (w = fp->f_bf.smb_size))
|
|
{
|
|
w = (*fp->f_write)(fp, p, w);
|
|
if (w <= 0)
|
|
{
|
|
if (w == 0 && errno == 0)
|
|
break; /* EOF found */
|
|
if (IS_IO_ERROR(fd, w, timeout))
|
|
goto err; /* errno set */
|
|
|
|
/* write would block */
|
|
SM_IO_WR_TIMEOUT(fp, fd, timeout);
|
|
w = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
w = s;
|
|
COPY(w);
|
|
fp->f_w -= w;
|
|
fp->f_p += w;
|
|
}
|
|
if ((nldist -= w) == 0)
|
|
{
|
|
/* copied the newline: flush and forget */
|
|
if (sm_flush(fp, &timeout))
|
|
goto err; /* errno set */
|
|
nlknown = 0;
|
|
}
|
|
p += w;
|
|
len -= w;
|
|
} while ((uio->uio_resid -= w) != 0);
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
/* errno set before goto places us here */
|
|
fp->f_flags |= SMERR;
|
|
return SM_IO_EOF;
|
|
}
|