1N/A/*
1N/A * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
1N/A * All rights reserved.
1N/A * Copyright (c) 1990, 1993
1N/A * The Regents of the University of California. All rights reserved.
1N/A *
1N/A * This code is derived from software contributed to Berkeley by
1N/A * Chris Torek.
1N/A *
1N/A * By using this file, you agree to the terms and conditions set
1N/A * forth in the LICENSE file which can be found at the top level of
1N/A * the sendmail distribution.
1N/A */
1N/A
1N/A#pragma ident "%Z%%M% %I% %E% SMI"
1N/A
1N/A#include <sm/gen.h>
1N/ASM_RCSID("@(#)$Id: fvwrite.c,v 1.47 2001/08/27 13:02:20 ca Exp $")
1N/A#include <stdlib.h>
1N/A#include <unistd.h>
1N/A#include <string.h>
1N/A#include <errno.h>
1N/A#include <signal.h>
1N/A#include <fcntl.h>
1N/A#include <sm/io.h>
1N/A#include <sm/setjmp.h>
1N/A#include <sm/conf.h>
1N/A#include "local.h"
1N/A#include "fvwrite.h"
1N/A
1N/A/*
1N/A** SM_FVWRITE -- write memory regions and buffer for file pointer
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer to write to
1N/A** timeout -- time length for function to return by
1N/A** uio -- the memory regions to write
1N/A**
1N/A** Returns:
1N/A** Failure: returns SM_IO_EOF and sets errno
1N/A** Success: returns 0 (zero)
1N/A**
1N/A** This routine is large and unsightly, but most of the ugliness due
1N/A** to the different kinds of output buffering handled here.
1N/A*/
1N/A
1N/A#define COPY(n) (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
1N/A#define GETIOV(extra_work) \
1N/A while (len == 0) \
1N/A { \
1N/A extra_work; \
1N/A p = iov->iov_base; \
1N/A len = iov->iov_len; \
1N/A iov++; \
1N/A }
1N/A
1N/Aint
1N/Asm_fvwrite(fp, timeout, uio)
1N/A register SM_FILE_T *fp;
1N/A int timeout;
1N/A register struct sm_uio *uio;
1N/A{
1N/A register size_t len;
1N/A register char *p;
1N/A register struct sm_iov *iov;
1N/A register int w, s;
1N/A char *nl;
1N/A int nlknown, nldist;
1N/A int fd;
1N/A struct timeval to;
1N/A
1N/A if (uio->uio_resid == 0)
1N/A return 0;
1N/A
1N/A /* make sure we can write */
1N/A if (cantwrite(fp))
1N/A {
1N/A errno = EBADF;
1N/A return SM_IO_EOF;
1N/A }
1N/A
1N/A SM_CONVERT_TIME(fp, fd, timeout, &to);
1N/A
1N/A iov = uio->uio_iov;
1N/A p = iov->iov_base;
1N/A len = iov->iov_len;
1N/A iov++;
1N/A if (fp->f_flags & SMNBF)
1N/A {
1N/A /* Unbuffered: write up to BUFSIZ bytes at a time. */
1N/A do
1N/A {
1N/A GETIOV(;);
1N/A errno = 0; /* needed to ensure EOF correctly found */
1N/A w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ));
1N/A if (w <= 0)
1N/A {
1N/A if (w == 0 && errno == 0)
1N/A break; /* EOF found */
1N/A if (IS_IO_ERROR(fd, w, timeout))
1N/A goto err; /* errno set */
1N/A
1N/A /* write would block */
1N/A SM_IO_WR_TIMEOUT(fp, fd, timeout);
1N/A w = 0;
1N/A }
1N/A else
1N/A {
1N/A p += w;
1N/A len -= w;
1N/A }
1N/A } while ((uio->uio_resid -= w) != 0);
1N/A }
1N/A else if ((fp->f_flags & SMLBF) == 0)
1N/A {
1N/A /*
1N/A ** Not SMLBF (line-buffered). Either SMFBF or SMNOW
1N/A ** buffered: fill partially full buffer, if any,
1N/A ** and then flush. If there is no partial buffer, write
1N/A ** one bf._size byte chunk directly (without copying).
1N/A **
1N/A ** String output is a special case: write as many bytes
1N/A ** as fit, but pretend we wrote everything. This makes
1N/A ** snprintf() return the number of bytes needed, rather
1N/A ** than the number used, and avoids its write function
1N/A ** (so that the write function can be invalid).
1N/A */
1N/A
1N/A do
1N/A {
1N/A GETIOV(;);
1N/A if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR))
1N/A || ((fp->f_flags & SMNOW) != 0))
1N/A && (size_t) fp->f_w < len)
1N/A {
1N/A size_t blen = fp->f_p - fp->f_bf.smb_base;
1N/A unsigned char *tbase;
1N/A int tsize;
1N/A
1N/A /* Allocate space exponentially. */
1N/A tsize = fp->f_bf.smb_size;
1N/A do
1N/A {
1N/A tsize = (tsize << 1) + 1;
1N/A } while ((size_t) tsize < blen + len);
1N/A tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base,
1N/A tsize + 1);
1N/A if (tbase == NULL)
1N/A {
1N/A errno = ENOMEM;
1N/A goto err; /* errno set */
1N/A }
1N/A fp->f_w += tsize - fp->f_bf.smb_size;
1N/A fp->f_bf.smb_base = tbase;
1N/A fp->f_bf.smb_size = tsize;
1N/A fp->f_p = tbase + blen;
1N/A }
1N/A w = fp->f_w;
1N/A errno = 0; /* needed to ensure EOF correctly found */
1N/A if (fp->f_flags & SMSTR)
1N/A {
1N/A if (len < (size_t) w)
1N/A w = len;
1N/A COPY(w); /* copy SM_MIN(fp->f_w,len), */
1N/A fp->f_w -= w;
1N/A fp->f_p += w;
1N/A w = len; /* but pretend copied all */
1N/A }
1N/A else if (fp->f_p > fp->f_bf.smb_base
1N/A && len > (size_t) w)
1N/A {
1N/A /* fill and flush */
1N/A COPY(w);
1N/A fp->f_p += w;
1N/A if (sm_flush(fp, &timeout))
1N/A goto err; /* errno set */
1N/A }
1N/A else if (len >= (size_t) (w = fp->f_bf.smb_size))
1N/A {
1N/A /* write directly */
1N/A w = (*fp->f_write)(fp, p, w);
1N/A if (w <= 0)
1N/A {
1N/A if (w == 0 && errno == 0)
1N/A break; /* EOF found */
1N/A if (IS_IO_ERROR(fd, w, timeout))
1N/A goto err; /* errno set */
1N/A
1N/A /* write would block */
1N/A SM_IO_WR_TIMEOUT(fp, fd, timeout);
1N/A w = 0;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A /* fill and done */
1N/A w = len;
1N/A COPY(w);
1N/A fp->f_w -= w;
1N/A fp->f_p += w;
1N/A }
1N/A p += w;
1N/A len -= w;
1N/A } while ((uio->uio_resid -= w) != 0);
1N/A
1N/A if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout))
1N/A goto err; /* errno set */
1N/A }
1N/A else
1N/A {
1N/A /*
1N/A ** Line buffered: like fully buffered, but we
1N/A ** must check for newlines. Compute the distance
1N/A ** to the first newline (including the newline),
1N/A ** or `infinity' if there is none, then pretend
1N/A ** that the amount to write is SM_MIN(len,nldist).
1N/A */
1N/A
1N/A nlknown = 0;
1N/A nldist = 0; /* XXX just to keep gcc happy */
1N/A do
1N/A {
1N/A GETIOV(nlknown = 0);
1N/A if (!nlknown)
1N/A {
1N/A nl = memchr((void *)p, '\n', len);
1N/A nldist = nl != NULL ? nl + 1 - p : len + 1;
1N/A nlknown = 1;
1N/A }
1N/A s = SM_MIN(len, ((size_t) nldist));
1N/A w = fp->f_w + fp->f_bf.smb_size;
1N/A errno = 0; /* needed to ensure EOF correctly found */
1N/A if (fp->f_p > fp->f_bf.smb_base && s > w)
1N/A {
1N/A COPY(w);
1N/A /* fp->f_w -= w; */
1N/A fp->f_p += w;
1N/A if (sm_flush(fp, &timeout))
1N/A goto err; /* errno set */
1N/A }
1N/A else if (s >= (w = fp->f_bf.smb_size))
1N/A {
1N/A w = (*fp->f_write)(fp, p, w);
1N/A if (w <= 0)
1N/A {
1N/A if (w == 0 && errno == 0)
1N/A break; /* EOF found */
1N/A if (IS_IO_ERROR(fd, w, timeout))
1N/A goto err; /* errno set */
1N/A
1N/A /* write would block */
1N/A SM_IO_WR_TIMEOUT(fp, fd, timeout);
1N/A w = 0;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A w = s;
1N/A COPY(w);
1N/A fp->f_w -= w;
1N/A fp->f_p += w;
1N/A }
1N/A if ((nldist -= w) == 0)
1N/A {
1N/A /* copied the newline: flush and forget */
1N/A if (sm_flush(fp, &timeout))
1N/A goto err; /* errno set */
1N/A nlknown = 0;
1N/A }
1N/A p += w;
1N/A len -= w;
1N/A } while ((uio->uio_resid -= w) != 0);
1N/A }
1N/A
1N/A return 0;
1N/A
1N/Aerr:
1N/A /* errno set before goto places us here */
1N/A fp->f_flags |= SMERR;
1N/A return SM_IO_EOF;
1N/A}