/*
 * Copyright (c) 2000-2002, 2004, 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: makebuf.c,v 1.7 2006/07/08 05:30:25 ca Exp $")
#include "sm/io.h"
#include "sm/heap.h"
#include "io-int.h"

/*
**  SM_WHATBUF -- determine proper buffer for a file (internal)
**
**  fills in 'bufsize' for recommended buffer size.
**
**	Parameters:
**		fp -- file pointer to be buffered
**		bufsize -- new buffer size (a return)
**
**	Returns:
**		SMNPT -- not seek opimized
**		SMOPT -- seek opimized
*/

sm_f_flags_T
sm_whatbuf(sm_file_T *fp, size_t *bufsize)
{
	struct stat st;

	if (f_fd(*fp) < 0 || fstat(f_fd(*fp), &st) < 0)
	{
		*bufsize = SM_IO_BUFSIZ;
		return SMNPT;
	}

	if (st.st_blksize == 0)
	{
		*bufsize = SM_IO_BUFSIZ;
		return SMNPT;
	}

#if SM_IO_MAX_BUF_FILE > 0
	if (S_ISREG(st.st_mode) && st.st_blksize > SM_IO_MAX_BUF_FILE)
		st.st_blksize = SM_IO_MAX_BUF_FILE;
#endif

#if SM_IO_MAX_BUF > 0 || SM_IO_MIN_BUF > 0
	if (!S_ISREG(st.st_mode))
	{
# if SM_IO_MAX_BUF > 0
		if (st.st_blksize > SM_IO_MAX_BUF)
			st.st_blksize = SM_IO_MAX_BUF;
#  if SM_IO_MIN_BUF > 0
		else
#  endif
# endif /* SM_IO_MAX_BUF > 0 */
# if SM_IO_MIN_BUF > 0
		if (st.st_blksize < SM_IO_MIN_BUF)
			st.st_blksize = SM_IO_MIN_BUF;
# endif
	}
#endif /* SM_IO_MAX_BUF > 0 || SM_IO_MIN_BUF > 0 */

	/*
	**  Optimise fseek() only if it is a regular file.  (The test for
	**  sm_std_seek is mainly paranoia.)  It is safe to set _blksize
	**  unconditionally; it will only be used if SMOPT is also set.
	*/

	if ((f_flags(*fp) & SMSTR) == 0)
	{
		*bufsize = st.st_blksize;
		fp->f_blksize = st.st_blksize;
	}
	else
		*bufsize = SM_IO_BUFSIZ;
	if ((st.st_mode & S_IFMT) == S_IFREG && f_seek(*fp) == sm_stdseek)
		return SMOPT;
	else
		return SMNPT;
}

/*
**  SM_MAKEFILEBUF -- make a buffer for the file
**
**	Parameters:
**		fp -- the file to be buffered
**
**	Returns:
**		allocation succeeded?
**
**	Allocate a file buffer, or switch to unbuffered I/O.
*/

sm_ret_T
sm_makefilebuf(sm_file_T *fp)
{
	void *p;
	sm_f_flags_T flags;
	size_t size;

	/* this may have been set when the file was opened... */
	if (f_flags(*fp) & SMNBF)
	{
		/* unbuffered: use "buffer" of size 1 */
		f_bfbase(*fp) = f_p(*fp) = fp->f_nbuf;
		f_bfsize(*fp) = sizeof(fp->f_nbuf);
		return SM_SUCCESS;
	}
	flags = sm_whatbuf(fp, &size);
	p = sm_malloc(size);
	if (p == NULL)
	{
		/*
		**  todo: We could check this in the application and
		**  maybe terminate the connection
		*/

		/* no memory: set unbuffered, use "buffer" of size 1 */
		f_flags(*fp) |= SMNBF;
		f_bfbase(*fp) = f_p(*fp) = fp->f_nbuf;
		f_bfsize(*fp) = sizeof(fp->f_nbuf);
		return sm_error_warn(SM_EM_IO, ENOMEM);
	}
	flags |= SMMBF;
	f_bfbase(*fp) = f_p(*fp) = p;
	f_bfsize(*fp) = size;
	f_flags(*fp) |= flags;
	return SM_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1