diff options
author | gshapiro <gshapiro@FreeBSD.org> | 2002-02-17 21:56:45 +0000 |
---|---|---|
committer | gshapiro <gshapiro@FreeBSD.org> | 2002-02-17 21:56:45 +0000 |
commit | 8449595fe97f4474b9b9a7e4edee1ef35dcff393 (patch) | |
tree | e7a33b132264d449a512ddf4a8685df097669c1d /contrib/sendmail/libsm/refill.c | |
parent | 289b381b31415647269c7520d881017e2dcb27f1 (diff) | |
download | FreeBSD-src-8449595fe97f4474b9b9a7e4edee1ef35dcff393.zip FreeBSD-src-8449595fe97f4474b9b9a7e4edee1ef35dcff393.tar.gz |
Import sendmail 8.12.2
Diffstat (limited to 'contrib/sendmail/libsm/refill.c')
-rw-r--r-- | contrib/sendmail/libsm/refill.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/contrib/sendmail/libsm/refill.c b/contrib/sendmail/libsm/refill.c new file mode 100644 index 0000000..10c7cde --- /dev/null +++ b/contrib/sendmail/libsm/refill.c @@ -0,0 +1,294 @@ +/* + * 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: refill.c,v 1.49 2001/09/11 04:04:49 gshapiro Exp $") +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <setjmp.h> +#include <signal.h> +#include <sys/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 *)); + +/* +** 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; \ + } \ + 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; \ + (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL, \ + &sm_io_x_mask, (to)); \ + 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_before, &sm_io_to_after, &sm_io_to_diff); \ + timersub((to), &sm_io_to_diff, (to)); \ +} + +/* +** 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; + struct timeval to; + int fd; + + 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 */ + if (IS_IO_ERROR(fd, r, timeout)) + goto err; /* errno set */ + + /* read would block */ + SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret); + } + } 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; +} |