/*
 * Copyright (c) 2000-2002, 2004 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.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sm/gen.h>
SM_RCSID("@(#)$Id: fclose.c,v 1.44 2005/06/14 23:07:20 ca Exp $")
#include <errno.h>
#include <stdlib.h>
#include <sm/time.h>
#include <setjmp.h>
#include <sm/io.h>
#include <sm/assert.h>
#include <sm/heap.h>
#include <sm/signal.h>
#include <sm/conf.h>
#include <sm/clock.h>
#include "local.h"

static void	closealrm __P((int));
static jmp_buf CloseTimeOut;

/*
**  CLOSEALRM -- handler when timeout activated for sm_io_close()
**
**	Returns flow of control to where setjmp(CloseTimeOut) was set.
**
**	Parameters:
**		sig -- unused
**
**	Returns:
**		does not return
**
**	Side Effects:
**		returns flow of control to setjmp(CloseTimeOut).
**
**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
**		DOING.
*/

/* ARGSUSED0 */
static void
closealrm(sig)
	int sig;
{
	longjmp(CloseTimeOut, 1);
}

/*
**  SM_IO_CLOSE -- close a file handle/pointer
**
**	Parameters:
**		fp -- file pointer to be closed
**		timeout -- maximum time allowed to perform the close (millisecs)
**
**	Returns:
**		0 on success
**		-1 on failure and sets errno
**
**	Side Effects:
**		file pointer 'fp' will no longer be valid.
*/

int
sm_io_close(fp, timeout)
	register SM_FILE_T *fp;
	int SM_NONVOLATILE timeout;
{
	register int SM_NONVOLATILE r;
	SM_EVENT *evt = NULL;

	if (fp == NULL)
	{
		errno = EBADF;
		return SM_IO_EOF;
	}

	SM_REQUIRE_ISA(fp, SmFileMagic);

	/* XXX this won't be reached if above macro is active */
	if (fp->sm_magic == NULL)
	{
		/* not open! */
		errno = EBADF;
		return SM_IO_EOF;
	}
	if (fp->f_close == NULL)
	{
		/* no close function! */
		errno = ENODEV;
		return SM_IO_EOF;
	}
	if (fp->f_dup_cnt > 0)
	{
		/* decrement file pointer open count */
		fp->f_dup_cnt--;
		return 0;
	}

	/*  Okay, this is where we set the timeout.  */
	if (timeout == SM_TIME_DEFAULT)
		timeout = fp->f_timeout;
	if (timeout == SM_TIME_IMMEDIATE)
	{
		errno = EAGAIN;
		return -1;
	}

	/* No more duplicates of file pointer. Flush buffer and close */
	r = fp->f_flags & SMWR ? sm_flush(fp, (int *) &timeout) : 0;

	/* sm_flush() has updated to.it_value for the time it's used */
	if (timeout != SM_TIME_FOREVER)
	{
		if (setjmp(CloseTimeOut) != 0)
		{
			errno = EAGAIN;
			return SM_IO_EOF;
		}
		evt = sm_seteventm(timeout, closealrm, 0);
	}
	if ((*fp->f_close)(fp) < 0)
		r = SM_IO_EOF;

	/*  We're back. So undo our timeout and handler */
	if (evt != NULL)
		sm_clrevent(evt);
	if (fp->f_flags & SMMBF)
	{
		sm_free((char *)fp->f_bf.smb_base);
		fp->f_bf.smb_base = NULL;
	}
	if (HASUB(fp))
		FREEUB(fp);
	fp->f_flags = 0;	/* clear flags */
	fp->sm_magic = NULL;	/* Release this SM_FILE_T for reuse. */
	fp->f_r = fp->f_w = 0;	/* Mess up if reaccessed. */
	return r;
}