/* @(#)handlecond.c	1.24 06/10/05 Copyright 1985-2004 J. Schilling */
/*
 *	setup/clear a condition handler for a software signal
 */
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file CDDL.Schily.txt in this distribution for details.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */
/*
 *	A procedure frame is marked to have handlers if the
 *	previous freme pointer for this procedure is odd.
 *	The even base value, in this case actually points to a SIGBLK which
 *	holds the saved "real" frame pointer.
 *	The SIGBLK mentioned above may me the start of a chain of SIGBLK's,
 *	containing different handlers.
 *
 *	This will work on processors which support a frame pointer chain
 *	on the stack.
 *	On a processor which doesn't support this I think of a method
 *	where handlecond() has an own chain of frames, holding chains of
 *	SIGBLK's.
 *	In this case, a parameter has to be added to handlecond() and
 *	unhandlecond(). This parameter will be an opaque cookie which is zero
 *	on the first call to handlecond() in a procedure.
 *	A new cookie will be returned by handlecond() which must be used on
 *	each subsequent call to handlecond() and unhandlecond() in the same
 *	procedure.
 *
 *	Copyright (c) 1985-2004 J. Schilling
 */
#include <schily/mconfig.h>
#include <schily/sigblk.h>
#include <schily/standard.h>
#include <schily/stdlib.h>
#include <schily/string.h>
#include <schily/avoffset.h>
#include <schily/utypes.h>
#include <schily/schily.h>

#if	!defined(AV_OFFSET) || !defined(FP_INDIR)
#	ifdef	HAVE_SCANSTACK
#	undef	HAVE_SCANSTACK
#	endif
#endif

#ifdef	HAVE_SCANSTACK
#include <schily/stkframe.h>
#else
extern	SIGBLK	*__roothandle;
#endif	/* HAVE_SCANSTACK */

#define	is_even(p)	((((long)(p)) & 1) == 0)
#define	even(p)		(((long)(p)) & ~1L)
#if defined(__sun) && defined(__i386)
/*
 * Solaris x86 has a broken frame.h which defines the frame ptr to int.
 */
#define	odd(p)		(((Intptr_t)(p)) | 1)
#else
#define	odd(p)		(void *)(((Intptr_t)(p)) | 1)
#endif

#ifdef	__future__
#define	even(p)		(((long)(p)) - 1) /* will this work with 64 bit ?? */
#endif

EXPORT	void	starthandlecond	__PR((SIGBLK *sp));

#ifdef	PROTOTYPES
EXPORT void
handlecond(const char	*signame,
		register SIGBLK	*sp,
		int		(*func)(const char *, long, long),
		long		arg1)
#else
EXPORT void
handlecond(signame, sp, func, arg1)
		char	*signame;
	register SIGBLK *sp;
		BOOL	(*func)();
		long	arg1;
#endif
{
	register SIGBLK	*this;
	register SIGBLK	*last = (SIGBLK *)NULL;
#ifdef	HAVE_SCANSTACK
	struct frame	*fp   = (struct frame *)NULL;
#endif
		int	slen;

	if (signame == NULL || (slen = strlen(signame)) == 0) {
		raisecond("handle_bad_name", (long)signame);
		abort();
	}

#ifdef	HAVE_SCANSTACK
	fp = (struct frame *)getfp();
	fp = (struct frame *)fp->fr_savfp;	/* point to frame of caller */
	if (is_even(fp->fr_savfp)) {
		/*
		 * Easy case: no handlers yet
		 * save real framepointer in sp->sb_savfp
		 */
		sp->sb_savfp   = (long **)fp->fr_savfp;
		this = (SIGBLK *)NULL;
	} else {
		this = (SIGBLK *)even(fp->fr_savfp);
	}
#else
	this = __roothandle;
#endif

	for (; this; this = this->sb_signext) {
		if (this == sp) {
			/*
			 * If a SIGBLK is reused, the name must not change.
			 */
			if (this->sb_signame != NULL &&
			    !streql(this->sb_signame, signame)) {
				raisecond("handle_reused_block", (long)signame);
				abort();
			}
			sp->sb_sigfun = func;
			sp->sb_sigarg = arg1;
			return;
		}
		if (this->sb_signame != NULL &&
		    streql(this->sb_signame, signame)) {
			if (last == (SIGBLK *)NULL) {
				/*
				 * 'this' is the first entry in chain
				 */
				if (this->sb_signext == (SIGBLK *)NULL) {
					/*
					 * only 'this' entry is in chain, copy
					 * saved real frame pointer into new sp
					 */
					sp->sb_savfp = this->sb_savfp;
				} else {
#ifdef	HAVE_SCANSTACK
					/*
					 * make second entry first link in chain
					 */
					this->sb_signext->sb_savfp =
								this->sb_savfp;
					fp->fr_savfp = odd(this->sb_signext);
#else
					/*
					 * Cannot happen if scanning the stack
					 * is not possible...
					 */
					raisecond("handle_is_empty", (long)0);
					abort();
#endif
				}
				continue;	/* don't trash 'last' ptr */
			} else {
				last->sb_signext = this->sb_signext;
			}
		}
		last = this;
	}
	sp->sb_signext = (SIGBLK *)NULL;
	sp->sb_signame = signame;
	sp->sb_siglen  = slen;
	sp->sb_sigfun  = func;
	sp->sb_sigarg  = arg1;
	/*
	 * If there is a chain append to end of the chain, else make it first
	 */
	if (last)
		last->sb_signext = sp;
#ifdef	HAVE_SCANSTACK
	else
		fp->fr_savfp = odd(sp);
#else
	/*
	 * Cannot happen if scanning the stack is not possible...
	 */
	else {
		raisecond("handle_is_empty", (long)0);
		abort();
	}
#endif
}

EXPORT void
starthandlecond(sp)
	register SIGBLK *sp;
{
#ifdef	HAVE_SCANSTACK
	struct frame	*fp = NULL;

	/*
	 * As the SCO OpenServer C-Compiler has a bug that may cause
	 * the first function call to getfp() been done before the
	 * new stack frame is created, we call getfp() twice.
	 */
	(void) getfp();
#endif

	sp->sb_signext = (SIGBLK *)NULL;
	sp->sb_signame = NULL;
	sp->sb_siglen  = 0;
	sp->sb_sigfun  = (handlefunc_t)NULL;
	sp->sb_sigarg  = 0;

#ifdef	HAVE_SCANSTACK
	fp = (struct frame *)getfp();
	fp = (struct frame *)fp->fr_savfp;	/* point to frame of caller */

	if (is_even(fp->fr_savfp)) {
		/*
		 * Easy case: no handlers yet
		 * save real framepointer in sp
		 */
		sp->sb_savfp = (long **)fp->fr_savfp;
		fp->fr_savfp = odd(sp);
	} else {
		raisecond("handle_not_empty", (long)0);
		abort();
	}
#else
	sp->sb_savfp	= (long **)__roothandle;
	__roothandle	= sp;
#endif
}

EXPORT void
unhandlecond(sp)
	register SIGBLK *sp;
{
#ifdef	HAVE_SCANSTACK
	register struct frame	*fp;
	register SIGBLK		*sps;

	/*
	 * As the SCO OpenServer C-Compiler has a bug that may cause
	 * the first function call to getfp() been done before the
	 * new stack frame is created, we call getfp() twice.
	 */
	(void) getfp();
	fp = (struct frame *)getfp();
	fp = (struct frame *)fp->fr_savfp;	/* point to frame of caller */

	if (!is_even(fp->fr_savfp)) {			/* if handlers	    */
		sps = (SIGBLK *)even(fp->fr_savfp);	/* point to SIGBLK  */
							/* real framepointer */
#if defined(__sun) && defined(__i386)
		fp->fr_savfp = (intptr_t)sps->sb_savfp;
#else
		fp->fr_savfp = (struct frame *)sps->sb_savfp;
#endif
	}
#else
	if (__roothandle == NULL) {
		raisecond("handle_is_empty", (long)0);
		abort();
	}
	/*
	 * Pop top level handler chain.
	 */
	__roothandle = (SIGBLK *)__roothandle->sb_savfp;
#endif
}
