/*
 * 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: fopen.c,v 1.28 2006/07/18 02:45:02 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/varargs.h"
#include "sm/time.h"
#include "sm/heap.h"
#include "sm/limits.h"
#include "sm/io.h"
#include "io-int.h"

/*
**  SM_FLAGS -- translate external (user) flags into internal flags
**
**	Paramters:
**		flags -- user select flags
**
**	Returns:
**		Internal flag value matching user selected flags
*/

/* fixme: Can't we use a simpler (direct) mapping?? */
static int
sm_flags(int flags)
{
	int ret;

	switch (flags)
	{
	  case SM_IO_RDONLY:	/* open for reading */
		ret = SMRD;
		break;

	  case SM_IO_WRONLY:	/* open for writing */
	  case SM_IO_WREXCL:	/* exclusive open for writing */
		ret = SMWR;
		break;

	  case SM_IO_APPEND:	/* open for appending */
		ret = SMWR;
		break;

	  case SM_IO_RDWR:	/* open for read and write */
	  case SM_IO_RDWRCR:
	  case SM_IO_RDWRTR:
	  case SM_IO_RDWRCRX:
		ret = SMRW;
		break;

	  default:
		ret = 0;
		break;
	}
	return ret;
}

/*
**  SM_IO_OPEN -- open a file of a specific type
**
**	Parameters:
**		type -- type of file to open
**		info -- info describing what is to be opened (type dependent)
**		flags -- user selected flags
**		newfp -- pointer to new fp (in/out parameter)
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_io_open(const sm_stream_T *type, const void *info, int flags, sm_file_T **newfp, ...)
{
	sm_ret_T res;
	sm_f_flags_T ioflags;
	sm_file_T *fp;
	va_list ap;

	SM_REQUIRE(newfp != NULL);
	*newfp = NULL;		/* just being friendly... */
	ioflags = sm_flags(flags);
	if (ioflags == 0)
	{
		/* must give some indication/intent */
		return sm_error_perm(SM_EM_IO, EINVAL);
	}

	res = sm_fp(type, ioflags, NULL, &fp);
	if (sm_is_err(res))
		return res;
	/* paranoia? SM_IS_FP(fp); */

	va_start(ap, newfp);
	res = f_open(*fp)(fp, info, flags, ap);
	va_end(ap);
	if (sm_is_err(res))
	{
		fp->sm_magic = SM_MAGIC_NULL;	/* release */
		f_flags(*fp) = 0;	/* release */
		return res;
	}

#if SM_RPOOL
	if (rpool != NULL)
		sm_rpool_attach(rpool, sm_io_fclose, fp);
#endif

	*newfp = fp;
	return SM_SUCCESS;
}

/*
**  SM_IO_DUP -- duplicate a file pointer
**
**	Parameters:
**		fp -- file pointer to duplicate
**
**	Returns:
**		usual sm_error code
**
**	Increments the duplicate counter (dup_cnt) for the open file pointer.
**	The counter counts the number of duplicates. When the duplicate
**	counter is 0 (zero) then the file pointer is the only one left
**	(no duplicates, it is the only one).
*/

sm_ret_T
sm_io_dup(sm_file_T *fp)
{

	SM_IS_FP(fp);

	/* Note: this won't be reached if above macro is active */
	if (fp->sm_magic != SM_FILE_MAGIC)
		return sm_error_perm(SM_EM_IO, EBADF);
	if (fp->f_dup_cnt >= INT_MAX - 1)
	{
		/* Can't let f_dup_cnt wrap! */
		return sm_error_perm(SM_EM_IO, EMFILE);
	}
	fp->f_dup_cnt++;
	return SM_SUCCESS;
}

#if 0
/*
**  SM_IO_REOPEN -- open a new file using the old file pointer
**
**	Parameters:
**		type -- file type to be opened
**		timeout -- time to complete the reopen
**		info -- infomation about what is to be "re-opened" (type dep.)
**		flags -- user flags to map to internal flags
**		fp -- the file pointer to reuse
**		newfp -- pointer to new file pointer
**		rpool -- rpool file to be associated with
**
**	Returns:
**		usual sm_error type
*/

sm_ret_T
sm_io_reopen(const sm_stream_T *type, const void *info, int flags,
	sm_file_T *fp, sm_file_T **newfp, sm_rpool_P rpool)
{
	sm_f_flags_T ioflags;
	sm_ret_T res;
	sm_file_T *fp2;

	ioflags = sm_flags(flags);
	if (ioflags == 0)
	{
		(void) sm_io_close(fp, SM_IO_CF_NONE);
		*newfp = NULL;
		return sm_error_perm(SM_EM_IO, EINVAL);
	}

	/*
	**  There are actually programs that depend on being able to "reopen"
	**  descriptors that weren't originally open.  Keep this from breaking.
	**  Remember whether the stream was open to begin with, and which file
	**  descriptor (if any) was associated with it.  If it was attached to
	**  a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin)
	**  should work.  This is unnecessary if it was not a Unix file.
	*/

	if (fp != NULL)
	{
		if (fp->sm_magic != SM_FILE_MAGIC)
			f_flags(*fp) = SMFEOF;	/* hold on to it */
		else
		{
			/* flush the stream; ANSI doesn't require this. */
			(void) sm_io_flush(fp);
			(void) sm_io_close(fp, SM_IO_CF_NONE);
		}
	}

	res = sm_fp(type, ioflags, NULL, &fp2);
	if (sm_is_err(res))
		return res;
	res = f_open(*fp2)(fp2, info, flags, SM_IO_WHAT_END);
	if (sm_is_err(res))
	{
		f_flags(*fp2) = 0;	/* release */
		fp->sm_magic = SM_MAGIC_NULL;	/* release */
		return res;
	}

	/*
	**  We're not preserving this logic (below) for sm_io because it is now
	**  abstracted at least one "layer" away. By closing and reopening
	**  the 1st fd used should be the just released one (when Unix
	**  behavior followed). Old comment::
	**  If reopening something that was open before on a real file, try
	**  to maintain the descriptor.  Various C library routines (perror)
	**  assume stderr is always fd STDERR_FILENO, even if being reopen'd.
	*/

#if SM_RPOOL
	if (rpool != NULL)
		sm_rpool_attach(rpool, sm_io_close, fp2);
#endif

	*newfp = fp2;
	return SM_SUCCESS;
}
#endif /* 0 */


syntax highlighted by Code2HTML, v. 0.9.1