190792Sgshapiro/*
2261194Sgshapiro * Copyright (c) 2000-2001 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *      All rights reserved.
490792Sgshapiro * Copyright (c) 1990, 1993
590792Sgshapiro *	The Regents of the University of California.  All rights reserved.
690792Sgshapiro *
790792Sgshapiro * This code is derived from software contributed to Berkeley by
890792Sgshapiro * Chris Torek.
990792Sgshapiro *
1090792Sgshapiro * By using this file, you agree to the terms and conditions set
1190792Sgshapiro * forth in the LICENSE file which can be found at the top level of
1290792Sgshapiro * the sendmail distribution.
1390792Sgshapiro */
1490792Sgshapiro
1590792Sgshapiro#include <sm/gen.h>
16266527SgshapiroSM_RCSID("@(#)$Id: setvbuf.c,v 1.33 2013-11-22 20:51:43 ca Exp $")
1790792Sgshapiro#include <stdlib.h>
1890792Sgshapiro#include <errno.h>
1990792Sgshapiro#include <fcntl.h>
2090792Sgshapiro#include <sm/io.h>
2190792Sgshapiro#include <sm/heap.h>
2290792Sgshapiro#include <sm/assert.h>
2390792Sgshapiro#include <sm/conf.h>
2490792Sgshapiro#include "local.h"
2590792Sgshapiro
2690792Sgshapiro/*
2790792Sgshapiro**  SM_IO_SETVBUF -- set the buffering type for a file
2890792Sgshapiro**
2990792Sgshapiro**  Set one of the different kinds of buffering, optionally including
3090792Sgshapiro**  a buffer.
3190792Sgshapiro**  If 'size' is == 0 then an "optimal" size will be selected.
3290792Sgshapiro**  If 'buf' is == NULL then space will be allocated at 'size'.
3390792Sgshapiro**
3490792Sgshapiro**	Parameters:
3590792Sgshapiro**		fp -- the file that buffering is to be changed for
3690792Sgshapiro**		timeout -- time allowed for completing the function
3790792Sgshapiro**		buf -- buffer to use
3890792Sgshapiro**		mode -- buffering method to use
3990792Sgshapiro**		size -- size of 'buf'
4090792Sgshapiro**
4190792Sgshapiro**	Returns:
4290792Sgshapiro**		Failure: SM_IO_EOF
4390792Sgshapiro**		Success: 0 (zero)
4490792Sgshapiro*/
4590792Sgshapiro
4690792Sgshapiroint
4790792Sgshapirosm_io_setvbuf(fp, timeout, buf, mode, size)
4890792Sgshapiro	SM_FILE_T *fp;
4990792Sgshapiro	int timeout;
5090792Sgshapiro	char *buf;
5190792Sgshapiro	int mode;
5290792Sgshapiro	size_t size;
5390792Sgshapiro{
5490792Sgshapiro	int ret, flags;
5590792Sgshapiro	size_t iosize;
5690792Sgshapiro	int ttyflag;
5790792Sgshapiro	int fd;
5890792Sgshapiro	struct timeval to;
5990792Sgshapiro
6090792Sgshapiro	SM_REQUIRE_ISA(fp, SmFileMagic);
6190792Sgshapiro
6290792Sgshapiro	/*
6390792Sgshapiro	**  Verify arguments.  The `int' limit on `size' is due to this
6490792Sgshapiro	**  particular implementation.  Note, buf and size are ignored
6590792Sgshapiro	**  when setting SM_IO_NBF.
6690792Sgshapiro	*/
6790792Sgshapiro
6890792Sgshapiro	if (mode != SM_IO_NBF)
6990792Sgshapiro		if ((mode != SM_IO_FBF && mode != SM_IO_LBF &&
7090792Sgshapiro		    mode != SM_IO_NOW) || (int) size < 0)
7190792Sgshapiro			return SM_IO_EOF;
7290792Sgshapiro
7390792Sgshapiro	/*
7490792Sgshapiro	**  Write current buffer, if any.  Discard unread input (including
7590792Sgshapiro	**  ungetc data), cancel line buffering, and free old buffer if
7690792Sgshapiro	**  malloc()ed.  We also clear any eof condition, as if this were
7790792Sgshapiro	**  a seek.
7890792Sgshapiro	*/
7990792Sgshapiro
8090792Sgshapiro	ret = 0;
8190792Sgshapiro	SM_CONVERT_TIME(fp, fd, timeout, &to);
8290792Sgshapiro	(void) sm_flush(fp, &timeout);
8390792Sgshapiro	if (HASUB(fp))
8490792Sgshapiro		FREEUB(fp);
8590792Sgshapiro	fp->f_r = fp->f_lbfsize = 0;
8690792Sgshapiro	flags = fp->f_flags;
8790792Sgshapiro	if (flags & SMMBF)
8890792Sgshapiro	{
8990792Sgshapiro		sm_free((void *) fp->f_bf.smb_base);
9090792Sgshapiro		fp->f_bf.smb_base = NULL;
9190792Sgshapiro	}
9290792Sgshapiro	flags &= ~(SMLBF | SMNBF | SMMBF | SMOPT | SMNPT | SMFEOF | SMNOW |
9390792Sgshapiro		   SMFBF);
9490792Sgshapiro
9590792Sgshapiro	/* If setting unbuffered mode, skip all the hard work. */
9690792Sgshapiro	if (mode == SM_IO_NBF)
9790792Sgshapiro		goto nbf;
9890792Sgshapiro
9990792Sgshapiro	/*
10090792Sgshapiro	**  Find optimal I/O size for seek optimization.  This also returns
10190792Sgshapiro	**  a `tty flag' to suggest that we check isatty(fd), but we do not
10290792Sgshapiro	**  care since our caller told us how to buffer.
10390792Sgshapiro	*/
10490792Sgshapiro
10590792Sgshapiro	flags |= sm_whatbuf(fp, &iosize, &ttyflag);
10690792Sgshapiro	if (size == 0)
10790792Sgshapiro	{
10890792Sgshapiro		buf = NULL;	/* force local allocation */
10990792Sgshapiro		size = iosize;
11090792Sgshapiro	}
11190792Sgshapiro
11290792Sgshapiro	/* Allocate buffer if needed. */
11390792Sgshapiro	if (buf == NULL)
11490792Sgshapiro	{
11590792Sgshapiro		if ((buf = sm_malloc(size)) == NULL)
11690792Sgshapiro		{
11790792Sgshapiro			/*
11890792Sgshapiro			**  Unable to honor user's request.  We will return
11990792Sgshapiro			**  failure, but try again with file system size.
12090792Sgshapiro			*/
12190792Sgshapiro
12290792Sgshapiro			ret = SM_IO_EOF;
12390792Sgshapiro			if (size != iosize)
12490792Sgshapiro			{
12590792Sgshapiro				size = iosize;
12690792Sgshapiro				buf = sm_malloc(size);
12790792Sgshapiro			}
12890792Sgshapiro		}
12990792Sgshapiro		if (buf == NULL)
13090792Sgshapiro		{
13190792Sgshapiro			/* No luck; switch to unbuffered I/O. */
13290792Sgshapironbf:
13390792Sgshapiro			fp->f_flags = flags | SMNBF;
13490792Sgshapiro			fp->f_w = 0;
13590792Sgshapiro			fp->f_bf.smb_base = fp->f_p = fp->f_nbuf;
13690792Sgshapiro			fp->f_bf.smb_size = 1;
13790792Sgshapiro			return ret;
13890792Sgshapiro		}
13990792Sgshapiro		flags |= SMMBF;
14090792Sgshapiro	}
14190792Sgshapiro
14290792Sgshapiro	/*
14390792Sgshapiro	**  Kill any seek optimization if the buffer is not the
14490792Sgshapiro	**  right size.
14590792Sgshapiro	**
14690792Sgshapiro	**  SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)?
14790792Sgshapiro	*/
14890792Sgshapiro
14990792Sgshapiro	if (size != iosize)
15090792Sgshapiro		flags |= SMNPT;
15190792Sgshapiro
15290792Sgshapiro	/*
15390792Sgshapiro	**  Fix up the SM_FILE_T fields, and set sm_cleanup for output flush on
15490792Sgshapiro	**  exit (since we are buffered in some way).
15590792Sgshapiro	*/
15690792Sgshapiro
15790792Sgshapiro	if (mode == SM_IO_LBF)
15890792Sgshapiro		flags |= SMLBF;
15990792Sgshapiro	else if (mode == SM_IO_NOW)
16090792Sgshapiro		flags |= SMNOW;
16190792Sgshapiro	else if (mode == SM_IO_FBF)
16290792Sgshapiro		flags |= SMFBF;
16390792Sgshapiro	fp->f_flags = flags;
16490792Sgshapiro	fp->f_bf.smb_base = fp->f_p = (unsigned char *)buf;
16590792Sgshapiro	fp->f_bf.smb_size = size;
16690792Sgshapiro	/* fp->f_lbfsize is still 0 */
16790792Sgshapiro	if (flags & SMWR)
16890792Sgshapiro	{
16990792Sgshapiro		/*
17090792Sgshapiro		**  Begin or continue writing: see sm_wsetup().  Note
17190792Sgshapiro		**  that SMNBF is impossible (it was handled earlier).
17290792Sgshapiro		*/
17390792Sgshapiro
17490792Sgshapiro		if (flags & SMLBF)
17590792Sgshapiro		{
17690792Sgshapiro			fp->f_w = 0;
17790792Sgshapiro			fp->f_lbfsize = -fp->f_bf.smb_size;
17890792Sgshapiro		}
17990792Sgshapiro		else
18090792Sgshapiro			fp->f_w = size;
18190792Sgshapiro	}
18290792Sgshapiro	else
18390792Sgshapiro	{
18490792Sgshapiro		/* begin/continue reading, or stay in intermediate state */
18590792Sgshapiro		fp->f_w = 0;
18690792Sgshapiro	}
18790792Sgshapiro
18890792Sgshapiro	atexit(sm_cleanup);
18990792Sgshapiro	return ret;
19090792Sgshapiro}
191