/*
* Copyright (c) 2000-2002, 2005 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/generic.h"
SM_RCSID("@(#)$Id: setvbuf.c,v 1.10 2006/07/14 14:42:25 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/io.h"
#include "sm/fcntl.h"
#include "sm/heap.h"
#include "io-int.h"
/*
** SM_IO_SETVBUF -- set the buffering type for a file
**
** Set one of the different kinds of buffering, optionally including
** a buffer.
** If 'size' is == 0 then an "optimal" size will be selected.
** If 'buf' is == NULL then space will be allocated at 'size'.
**
** Parameters:
** fp -- the file that buffering is to be changed for
** buf -- buffer to use
** mode -- buffering method to use
** size -- size of 'buf'
**
** Returns:
** usual sm_error
*/
sm_ret_T
sm_io_setvbuf(sm_file_T *fp, uchar *buf, int mode, size_t size)
{
sm_f_flags_T flags;
size_t iosize;
sm_ret_T res;
SM_IS_FP(fp);
/*
** Verify arguments. The `int' limit on `size' is due to this
** particular implementation. Note, buf and size are ignored
** when setting SM_IO_NBF.
*/
if (mode != SM_IO_NBF && (mode != SM_IO_FBF || (int) size < 0))
return sm_error_perm(SM_EM_IO, EINVAL);
/*
** Write current buffer, if any. Discard unread input (including
** ungetc data), cancel line buffering, and free old buffer if
** malloc()ed. We also clear any eof condition, as if this were
** a seek.
*/
res = 0;
(void) sm_flush(fp);
f_r(*fp) = 0;
flags = f_flags(*fp);
if (flags & SMMBF)
{
sm_free((void *) f_bfbase(*fp));
f_bfbase(*fp) = NULL;
}
flags &= ~(SMNBF | SMMBF | SMOPT | SMNPT | SMFEOF | SMFBF);
/* If setting unbuffered mode, skip all the hard work. */
if (mode == SM_IO_NBF)
goto nbf;
flags |= sm_whatbuf(fp, &iosize);
if (size == 0)
{
buf = NULL; /* force local allocation */
size = iosize;
}
/* Allocate buffer if needed. */
if (buf == NULL)
{
buf = (uchar *) sm_malloc(size);
if (buf == NULL)
{
/*
** Unable to honor user's request. We will return
** failure, but try again with file system size.
*/
res = sm_error_temp(SM_EM_IO, ENOMEM);
if (size != iosize)
{
size = iosize;
buf = (uchar *) sm_malloc(size);
}
}
if (buf == NULL)
{
/* No luck; switch to unbuffered I/O. */
nbf:
f_flags(*fp) = flags | SMNBF;
f_w(*fp) = 0;
f_bfbase(*fp) = f_p(*fp) = fp->f_nbuf;
f_bfsize(*fp) = 1;
return res;
}
flags |= SMMBF;
}
/*
** Kill any seek optimization if the buffer is not the
** right size.
**
** SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)?
*/
if (size != iosize)
flags |= SMNPT;
/*
** Fix up the sm_file_T fields, and set sm_cleanup for output flush on
** exit (since we are buffered in some way).
*/
if (mode == SM_IO_FBF)
flags |= SMFBF;
f_flags(*fp) = flags;
f_bfbase(*fp) = f_p(*fp) = buf;
f_bfsize(*fp) = size;
if (flags & SMWR)
{
/*
** Begin or continue writing: see sm_wsetup(). Note
** that SMNBF is impossible (it was handled earlier).
*/
f_w(*fp) = size;
}
else
{
/* begin/continue reading, or stay in intermediate state */
f_w(*fp) = 0;
}
return res;
}
syntax highlighted by Code2HTML, v. 0.9.1