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: setvbuf.c,v 1.30 2001/02/28 20:25:18 rodney Exp $")
1N/A#include <stdlib.h>
1N/A#include <errno.h>
1N/A#include <fcntl.h>
1N/A#include <sm/io.h>
1N/A#include <sm/heap.h>
1N/A#include <sm/assert.h>
1N/A#include <sm/conf.h>
1N/A#include "local.h"
1N/A
1N/A/*
1N/A** SM_IO_SETVBUF -- set the buffering type for a file
1N/A**
1N/A** Set one of the different kinds of buffering, optionally including
1N/A** a buffer.
1N/A** If 'size' is == 0 then an "optimal" size will be selected.
1N/A** If 'buf' is == NULL then space will be allocated at 'size'.
1N/A**
1N/A** Parameters:
1N/A** fp -- the file that buffering is to be changed for
1N/A** timeout -- time allowed for completing the function
1N/A** buf -- buffer to use
1N/A** mode -- buffering method to use
1N/A** size -- size of 'buf'
1N/A**
1N/A** Returns:
1N/A** Failure: SM_IO_EOF
1N/A** Success: 0 (zero)
1N/A*/
1N/A
1N/Aint
1N/Asm_io_setvbuf(fp, timeout, buf, mode, size)
1N/A SM_FILE_T *fp;
1N/A int timeout;
1N/A char *buf;
1N/A int mode;
1N/A size_t size;
1N/A{
1N/A int ret, flags;
1N/A size_t iosize;
1N/A int ttyflag;
1N/A int fd;
1N/A struct timeval to;
1N/A
1N/A SM_REQUIRE_ISA(fp, SmFileMagic);
1N/A
1N/A /*
1N/A ** Verify arguments. The `int' limit on `size' is due to this
1N/A ** particular implementation. Note, buf and size are ignored
1N/A ** when setting SM_IO_NBF.
1N/A */
1N/A
1N/A if (mode != SM_IO_NBF)
1N/A if ((mode != SM_IO_FBF && mode != SM_IO_LBF &&
1N/A mode != SM_IO_NOW) || (int) size < 0)
1N/A return SM_IO_EOF;
1N/A
1N/A /*
1N/A ** Write current buffer, if any. Discard unread input (including
1N/A ** ungetc data), cancel line buffering, and free old buffer if
1N/A ** malloc()ed. We also clear any eof condition, as if this were
1N/A ** a seek.
1N/A */
1N/A
1N/A ret = 0;
1N/A SM_CONVERT_TIME(fp, fd, timeout, &to);
1N/A (void) sm_flush(fp, &timeout);
1N/A if (HASUB(fp))
1N/A FREEUB(fp);
1N/A fp->f_r = fp->f_lbfsize = 0;
1N/A flags = fp->f_flags;
1N/A if (flags & SMMBF)
1N/A {
1N/A sm_free((void *) fp->f_bf.smb_base);
1N/A fp->f_bf.smb_base = NULL;
1N/A }
1N/A flags &= ~(SMLBF | SMNBF | SMMBF | SMOPT | SMNPT | SMFEOF | SMNOW |
1N/A SMFBF);
1N/A
1N/A /* If setting unbuffered mode, skip all the hard work. */
1N/A if (mode == SM_IO_NBF)
1N/A goto nbf;
1N/A
1N/A /*
1N/A ** Find optimal I/O size for seek optimization. This also returns
1N/A ** a `tty flag' to suggest that we check isatty(fd), but we do not
1N/A ** care since our caller told us how to buffer.
1N/A */
1N/A
1N/A flags |= sm_whatbuf(fp, &iosize, &ttyflag);
1N/A if (size == 0)
1N/A {
1N/A buf = NULL; /* force local allocation */
1N/A size = iosize;
1N/A }
1N/A
1N/A /* Allocate buffer if needed. */
1N/A if (buf == NULL)
1N/A {
1N/A if ((buf = sm_malloc(size)) == NULL)
1N/A {
1N/A /*
1N/A ** Unable to honor user's request. We will return
1N/A ** failure, but try again with file system size.
1N/A */
1N/A
1N/A ret = SM_IO_EOF;
1N/A if (size != iosize)
1N/A {
1N/A size = iosize;
1N/A buf = sm_malloc(size);
1N/A }
1N/A }
1N/A if (buf == NULL)
1N/A {
1N/A /* No luck; switch to unbuffered I/O. */
1N/Anbf:
1N/A fp->f_flags = flags | SMNBF;
1N/A fp->f_w = 0;
1N/A fp->f_bf.smb_base = fp->f_p = fp->f_nbuf;
1N/A fp->f_bf.smb_size = 1;
1N/A return ret;
1N/A }
1N/A flags |= SMMBF;
1N/A }
1N/A
1N/A /*
1N/A ** Kill any seek optimization if the buffer is not the
1N/A ** right size.
1N/A **
1N/A ** SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)?
1N/A */
1N/A
1N/A if (size != iosize)
1N/A flags |= SMNPT;
1N/A
1N/A /*
1N/A ** Fix up the SM_FILE_T fields, and set sm_cleanup for output flush on
1N/A ** exit (since we are buffered in some way).
1N/A */
1N/A
1N/A if (mode == SM_IO_LBF)
1N/A flags |= SMLBF;
1N/A else if (mode == SM_IO_NOW)
1N/A flags |= SMNOW;
1N/A else if (mode == SM_IO_FBF)
1N/A flags |= SMFBF;
1N/A fp->f_flags = flags;
1N/A fp->f_bf.smb_base = fp->f_p = (unsigned char *)buf;
1N/A fp->f_bf.smb_size = size;
1N/A /* fp->f_lbfsize is still 0 */
1N/A if (flags & SMWR)
1N/A {
1N/A /*
1N/A ** Begin or continue writing: see sm_wsetup(). Note
1N/A ** that SMNBF is impossible (it was handled earlier).
1N/A */
1N/A
1N/A if (flags & SMLBF)
1N/A {
1N/A fp->f_w = 0;
1N/A fp->f_lbfsize = -fp->f_bf.smb_size;
1N/A }
1N/A else
1N/A fp->f_w = size;
1N/A }
1N/A else
1N/A {
1N/A /* begin/continue reading, or stay in intermediate state */
1N/A fp->f_w = 0;
1N/A }
1N/A
1N/A atexit(sm_cleanup);
1N/A return ret;
1N/A}