Logo Search packages:      
Sourcecode: batv-milter version File versions  Download package

fvwrite.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;
#ifndef WIN32
      int fd;
      struct timeval to;
#endif /* !WIN32 */

      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;
}

Generated by  Doxygen 1.6.0   Back to index