/* NAME:
 *      sigact.c - fake sigaction(2)
 *
 * SYNOPSIS:
 *      #include "sigact.h"
 * 
 *      int sigaction(int sig, struct sigaction *act, 
 *                      struct sigaction *oact);
 *      int sigaddset(sigset_t *mask, int sig);
 *      int sigdelset(sigset_t *mask, int sig);
 *      int sigemptyset(sigset_t *mask);
 *      int sigfillset(sigset_t *mask);
 *      int sigismember(sigset_t *mask, int sig);
 *      int sigpending(sigset_t *set);
 *      int sigprocmask(int how, sigset_t *set, sigset_t *oset);
 *      int sigsuspend(sigset_t *mask);
 *      
 *      SIG_HDLR (*Signal(int sig, SIG_HDLR (*disp)(int)))(int);
 *
 * DESCRIPTION:
 *      This is a fake sigaction implementation.  It uses 
 *      sigsetmask(2) et al or sigset(2) and friends if 
 *      available, otherwise it just uses signal(2).  If it 
 *      thinks sigaction(2) really exists it compiles to "almost" 
 *      nothing. 
 *      
 *      In any case it provides a Signal() function that is 
 *      implemented in terms of sigaction().
 *      If not using signal(2) as part of the underlying 
 *      implementation (USE_SIGNAL or USE_SIGMASK), and 
 *      NO_SIGNAL is not defined, it also provides a signal() 
 *      function that calls Signal(). 
 *
 *      The need for all this mucking about is the problems 
 *      caused by mixing various signal handling mechanisms in 
 *      the one process.  This module allows for a consistent 
 *      POSIX compliant interface to whatever is actually 
 *      available. 
 *      
 *      sigaction() allows the caller to examine and/or set the 
 *      action to be associated with a given signal. "act" and 
 *      "oact" are pointers to 'sigaction structs':
 *.nf
 * 
 *      struct sigaction 
 *      {
 *             SIG_HDLR  (*sa_handler)();
 *             sigset_t  sa_mask;
 *             int       sa_flags;
 *      };
 *.fi
 * 
 *      SIG_HDLR is normally 'void' in the POSIX implementation 
 *      and for most current systems.  On some older UNIX 
 *      systems, signal handlers do not return 'void', so  
 *      this implementation keeps 'sa_handler' inline with the 
 *      hosts normal signal handling conventions.
 *      'sa_mask' controls which signals will be blocked while 
 *      the selected signal handler is active.  It is not used 
 *      in this implementation.
 *      'sa_flags' controls various semantics such as whether 
 *      system calls should be automagically restarted 
 *      (SA_RESTART) etc.  It is not used in this 
 *      implementation. 
 *      Either "act" or "oact" may be NULL in which case the 
 *      appropriate operation is skipped.
 *      
 *      sigaddset() adds "sig" to the sigset_t pointed to by "mask".
 *      
 *      sigdelset() removes "sig" from the sigset_t pointed to 
 *      by "mask". 
 *      
 *      sigemptyset() makes the sigset_t pointed to by "mask" empty.
 *      
 *      sigfillset() makes the sigset_t pointed to by "mask" 
 *      full ie. match all signals.
 *      
 *      sigismember() returns true if "sig" is found in "*mask".
 *      
 *      sigpending() is supposed to return "set" loaded with the 
 *      set of signals that are blocked and pending for the 
 *      calling process.  It does nothing in this impementation.
 *      
 *      sigprocmask() is used to examine and/or change the 
 *      signal mask for the calling process.  Either "set" or 
 *      "oset" may be NULL in which case the appropriate 
 *      operation is skipped.  "how" may be one of SIG_BLOCK, 
 *      SIG_UNBLOCK or SIG_SETMASK.  If this package is built 
 *      with USE_SIGNAL, then this routine achieves nothing.
 *      
 *      sigsuspend() sets the signal mask to "*mask" and waits 
 *      for a signal to be delivered after which the previous 
 *      mask is restored.
 *      
 *      
 * RETURN VALUE:
 *      0==success, -1==failure
 *
 * BUGS:
 *      Since we fake most of this, don't expect fancy usage to 
 *      work.
 *
 * AUTHOR:
 *      Simon J. Gerraty <sjg@zen.void.oz.au>
 */     
/* COPYRIGHT:
 *      @(#)Copyright (c) 1992 Simon J. Gerraty
 *
 *      This is free software.  It comes with NO WARRANTY.
 *      Permission to use, modify and distribute this source code
 *      is granted subject to the following conditions.
 *      1/ that that the above copyright notice and this notice 
 *      are preserved in all copies and that due credit be given 
 *      to the author.  
 *      2/ that any changes to this code are clearly commented 
 *      as such so that the author does get blamed for bugs 
 *      other than his own.
 *      
 *      Please send copies of changes and bug-fixes to:
 *      sjg@zen.void.oz.au
 *
 */
#ifndef lint
static char  *RCSid = "$Id: sigact.c,v 1.10 93/05/08 16:00:06 sjg Exp $";
#endif

#include <signal.h>

#ifndef __P
# if defined(__STDC__) || defined(__cplusplus)
#   define	__P(p)	p
# else
#   define	__P(p)	p
# endif
#endif


/*
 * some systems have a faulty sigaction() implementation!
 * Allow us to bypass it.
 * Or they may have installed sigact.h as signal.h which is why 
 * we have SA_NOCLDSTOP defined.
 */
#if !defined(SA_NOCLDSTOP) || defined(_SIGACT_H) || defined(USE_SIGNAL) || defined(USE_SIGSET) || defined(USE_SIGMASK)

/*
 * if we haven't been told,
 * try and guess what we should implement with.
 */
#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL)
# if defined(sigmask) || defined(BSD) || defined(_BSD) && !defined(BSD41)
#   define USE_SIGMASK
# else
#   ifndef NO_SIGSET
#     define USE_SIGSET
#   else
#     define USE_SIGNAL
#   endif
# endif
#endif
/*
 * if we still don't know, we're in trouble
 */
#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL)
error must know what to implement with
#endif

#include "sigact.h"

/*
 * in case signal() has been mapped to our Signal().
 */
#undef signal

 
int
sigaction(sig, act, oact)
  int sig;
  struct sigaction *act, *oact;
{
  SIG_HDLR (*oldh)();

  if (act)
  {
#ifdef USE_SIGSET
    oldh = sigset(sig, act->sa_handler);
#else
    oldh = signal(sig, act->sa_handler);
#endif
  }
  else
  {
    if (oact)
    {      
#ifdef USE_SIGSET
      oldh = sigset(sig, SIG_IGN);
#else
      oldh = signal(sig, SIG_IGN);
#endif
      if (oldh != SIG_IGN && oldh !=  SIG_ERR)
      {
#ifdef USE_SIGSET
	(void) sigset(sig, oldh);
#else
	(void) signal(sig, oldh);
#endif
      }
    }
  }
  if (oact)
  {
    oact->sa_handler = oldh;
  }
  return 0;				/* hey we're faking it */
}


int
sigaddset(mask, sig)
  sigset_t *mask;
  int sig;
{
  *mask |= sigmask(sig);
  return 0;
}


int
sigdelset(mask, sig)
  sigset_t *mask;
  int sig;
{
  *mask &= ~(sigmask(sig));
  return 0;
}


int
sigemptyset(mask)
  sigset_t *mask;
{
  *mask = 0;
  return 0;
}


int
sigfillset(mask)
  sigset_t *mask;
{
  *mask = ~0;
  return 0;
}


int
sigismember(mask, sig)
  sigset_t *mask;
  int sig;
{
  return ((*mask) & sigmask(sig));
}


int
sigpending(set)
  sigset_t *set;
{
  return 0;				/* faking it! */
}


int
sigprocmask(how, set, oset)
  int how;
  sigset_t *set, *oset;
{
#ifdef USE_SIGSET
  register int i;
#endif
  static sigset_t sm;
  static int once = 0;

  if (!once)
  {
    /*
     * initally we clear sm,
     * there after, it represents the last
     * thing we did.
     */
    once++;
#ifdef USE_SIGMASK
    sm = sigblock(0);
#else
    sm = 0;
#endif
  }
  
  if (oset)
    *oset = sm;
  if (set)
  {
    switch (how)
    {
    case SIG_BLOCK:
      sm |= *set;
      break;
    case SIG_UNBLOCK:
      sm &= ~(*set);
      break;
    case SIG_SETMASK:
      sm = *set;
      break;
    }
#ifdef USE_SIGMASK
    (void) sigsetmask(sm);
#else
# ifdef USE_SIGSET
    for (i = 1; i < NSIG; i++)
    {
      if (how == SIG_UNBLOCK)
      {
	if (*set & sigmask(i))
	  sigrelse(i);
      }
      else
	if (sm & sigmask(i))
	{
	  sighold(i);
	}
    }
# endif
#endif
  }
  return 0;
}


int
sigsuspend(mask)
  sigset_t *mask;
{
#ifdef USE_SIGMASK
  sigpause(mask);
#else
  register int i;

# ifdef USE_SIGSET

  for (i = 1; i < NSIG; i++)
  {
    if (*mask & sigmask(i))
    {
      /* not the same sigpause() as above! */
      sigpause(i);			
      break;
    }
  }
# else /* signal(2) only */
  SIG_HDLR (*oldh)();

  /*
   * make sure that signals in mask will not
   * be ignored.
   */
  for (i = 1; i < NSIG; i++)
  {
    if (*mask & sigmask(i))
    {
      if ((oldh = signal(i, SIG_DFL)) !=  SIG_ERR &&
	  oldh != SIG_IGN &&
	  oldh != SIG_DFL)
	(void) signal(i, oldh);		/* restore handler */
    }
  }
  pause();				/* wait for a signal */
# endif
#endif
  return 0;
}

#endif /* ! SA_NOCLDSTOP */

#if !defined(SIG_HDLR)
# define SIG_HDLR void
#endif
#if !defined(SIG_ERR)
# define SIG_ERR	(SIG_HDLR (*)())-1
#endif

/*
 * an implementation of signal() using sigaction().
 */

SIG_HDLR (*Signal(sig, handler)) __P((int))
  int sig;
  SIG_HDLR (*handler) __P((int));
{
  struct sigaction act, oact;

  act.sa_handler = handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
  if (sigaction(sig, &act, &oact) < 0)
    return (SIG_ERR);
  return (oact.sa_handler);
}

#if !defined(USE_SIGNAL) && !defined(USE_SIGMASK) && !defined(NO_SIGNAL)
/*
 * ensure we avoid signal mayhem
 */

SIG_HDLR (*signal(sig, handler)) __P((int))
  int sig;
  SIG_HDLR (*handler) __P((int));
{
  return (Signal(sig, handler));
}
#endif

/* This lot (for GNU-Emacs) goes at the end of the file. */
/* 
 * Local Variables:
 * version-control:t
 * comment-column:40
 * End:
 */
