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

refill.c

/*
 * Copyright (c) 2000-2001, 2005-2006 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: refill.c,v 1.53 2006/02/28 18:48:25 ca Exp $")
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <sm/time.h>
#include <fcntl.h>
#include <string.h>
#include <sm/io.h>
#include <sm/conf.h>
#include <sm/assert.h>
#include "local.h"

static int sm_lflush __P((SM_FILE_T *, int *));

#ifndef WIN32
/*
**  SM_IO_RD_TIMEOUT -- measured timeout for reads
**
**  This #define uses a select() to wait for the 'fd' to become readable.
**  The select() can be active for up to 'To' time. The select() may not
**  use all of the the 'To' time. Hence, the amount of "wall-clock" time is
**  measured to decide how much to subtract from 'To' to update it. On some
**  BSD-based/like systems the timeout for a select() is updated for the
**  amount of time used. On many/most systems this does not happen. Therefore
**  the updating of 'To' must be done ourselves; a copy of 'To' is passed
**  since a BSD-like system will have updated it and we don't want to
**  double the time used!
**  Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
**  sendmail buffered file type in sendmail/bf.c; see use below).
**
**    Parameters
**          fp -- the file pointer for the active file
**          fd -- raw file descriptor (from 'fp') to use for select()
**          to -- struct timeval of the timeout
**          timeout -- the original timeout value
**          sel_ret -- the return value from the select()
**
**    Returns:
**          nothing, flow through code
*/

#define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret)                  \
{                                                     \
      struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff;    \
      fd_set sm_io_to_mask, sm_io_x_mask;                   \
      errno = 0;                                      \
      if (timeout == SM_TIME_IMMEDIATE)                     \
      {                                               \
            errno = EAGAIN;                                 \
            return SM_IO_EOF;                         \
      }                                               \
      if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE)             \
      {                                               \
            errno = EINVAL;                                 \
            return SM_IO_EOF;                         \
      }                                               \
      FD_ZERO(&sm_io_to_mask);                              \
      FD_SET((fd), &sm_io_to_mask);                         \
      FD_ZERO(&sm_io_x_mask);                               \
      FD_SET((fd), &sm_io_x_mask);                          \
      if (gettimeofday(&sm_io_to_before, NULL) < 0)               \
            return SM_IO_EOF;                         \
      do                                              \
      {                                               \
            (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL,    \
                        &sm_io_x_mask, (to));               \
      } while ((sel_ret) < 0 && errno == EINTR);                  \
      if ((sel_ret) < 0)                                    \
      {                                               \
            /* something went wrong, errno set */                 \
            fp->f_r = 0;                                    \
            fp->f_flags |= SMERR;                           \
            return SM_IO_EOF;                         \
      }                                               \
      else if ((sel_ret) == 0)                              \
      {                                               \
            /* timeout */                                   \
            errno = EAGAIN;                                 \
            return SM_IO_EOF;                         \
      }                                               \
      /* calulate wall-clock time used */                   \
      if (gettimeofday(&sm_io_to_after, NULL) < 0)                \
            return SM_IO_EOF;                         \
      timersub(&sm_io_to_after, &sm_io_to_before, &sm_io_to_diff);      \
      timersub((to), &sm_io_to_diff, (to));                       \
}
#endif /* ! WIN32 */

/*
**  SM_LFLUSH -- flush a file if it is line buffered and writable
**
**    Parameters:
**          fp -- file pointer to flush
**          timeout -- original timeout value (in milliseconds)
**
**    Returns:
**          Failure: returns SM_IO_EOF and sets errno
**          Success: returns 0
*/

static int
sm_lflush(fp, timeout)
      SM_FILE_T *fp;
      int *timeout;
{

      if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR))
            return sm_flush(fp, timeout);
      return 0;
}

/*
**  SM_REFILL -- refill a buffer
**
**    Parameters:
**          fp -- file pointer for buffer refill
**          timeout -- time to complete filling the buffer in milliseconds
**
**    Returns:
**          Success: returns 0
**          Failure: returns SM_IO_EOF
*/

int
sm_refill(fp, timeout)
      register SM_FILE_T *fp;
      int timeout;
{
      int ret, r;
#ifndef WIN32
      struct timeval to;
      int fd;
#endif /* !WIN32 */

      if (timeout == SM_TIME_DEFAULT)
            timeout = fp->f_timeout;
      if (timeout == SM_TIME_IMMEDIATE)
      {
            /*
            **  Filling the buffer will take time and we are wanted to
            **  return immediately. And we're not EOF or ERR really.
            **  So... the failure is we couldn't do it in time.
            */

            errno = EAGAIN;
            fp->f_r = 0; /* just to be sure */
            return 0;
      }

      /* make sure stdio is set up */
      if (!Sm_IO_DidInit)
            sm_init();

      fp->f_r = 0;            /* largely a convenience for callers */

      if (fp->f_flags & SMFEOF)
            return SM_IO_EOF;

      SM_CONVERT_TIME(fp, fd, timeout, &to);

      /* if not already reading, have to be reading and writing */
      if ((fp->f_flags & SMRD) == 0)
      {
            if ((fp->f_flags & SMRW) == 0)
            {
                  errno = EBADF;
                  fp->f_flags |= SMERR;
                  return SM_IO_EOF;
            }

            /* switch to reading */
            if (fp->f_flags & SMWR)
            {
                  if (sm_flush(fp, &timeout))
                        return SM_IO_EOF;
                  fp->f_flags &= ~SMWR;
                  fp->f_w = 0;
                  fp->f_lbfsize = 0;
            }
            fp->f_flags |= SMRD;
      }
      else
      {
            /*
            **  We were reading.  If there is an ungetc buffer,
            **  we must have been reading from that.  Drop it,
            **  restoring the previous buffer (if any).  If there
            **  is anything in that buffer, return.
            */

            if (HASUB(fp))
            {
                  FREEUB(fp);
                  if ((fp->f_r = fp->f_ur) != 0)
                  {
                        fp->f_p = fp->f_up;

                        /* revert blocking state */
                        return 0;
                  }
            }
      }

      if (fp->f_bf.smb_base == NULL)
            sm_makebuf(fp);

      /*
      **  Before reading from a line buffered or unbuffered file,
      **  flush all line buffered output files, per the ANSI C standard.
      */

      if (fp->f_flags & (SMLBF|SMNBF))
            (void) sm_fwalk(sm_lflush, &timeout);

      /*
      **  If this file is linked to another, and we are going to hang
      **  on the read, flush the linked file before continuing.
      */

      if (fp->f_flushfp != NULL &&
          (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0)
            sm_flush(fp->f_flushfp, &timeout);

      fp->f_p = fp->f_bf.smb_base;

      /*
      **  The do-while loop stops trying to read when something is read
      **  or it appears that the timeout has expired before finding
      **  something available to be read (via select()).
      */

      ret = 0;
      do
      {
            errno = 0; /* needed to ensure EOF correctly found */
            r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size);
            if (r <= 0)
            {
                  if (r == 0 && errno == 0)
                        break; /* EOF found */
#ifdef WIN32
                  else
                        goto err;
#else /* WIN32 */
                  if (IS_IO_ERROR(fd, r, timeout))
                        goto err; /* errno set */

                  /* read would block */
                  SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret);
#endif /* WIN32 */
            }
      } while (r <= 0 && ret > 0);

err:
      if (r <= 0)
      {
            if (r == 0)
                  fp->f_flags |= SMFEOF;
            else
                  fp->f_flags |= SMERR;
            fp->f_r = 0;
            return SM_IO_EOF;
      }
      fp->f_r = r;
      return 0;
}

/*
**  SM_RGET -- refills buffer and returns first character
**
**  Handle sm_getc() when the buffer ran out:
**  Refill, then return the first character in the newly-filled buffer.
**
**    Parameters:
**          fp -- file pointer to work on
**          timeout -- time to complete refill
**
**    Returns:
**          Success: first character in refilled buffer as an int
**          Failure: SM_IO_EOF
*/

int
sm_rget(fp, timeout)
      register SM_FILE_T *fp;
      int timeout;
{
      if (sm_refill(fp, timeout) == 0)
      {
            fp->f_r--;
            return *fp->f_p++;
      }
      return SM_IO_EOF;
}

Generated by  Doxygen 1.6.0   Back to index