/* Need to define sig2interrupt vector.
** Interrupt-system mutators should probably hold interrupts while they
**   operate.
*/

#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include "cstuff.h"

/* Make sure our exports match up w/the implementation: */
#include "sighandlers1.h"

/* Import the OS-dependent set of signals and their translations
** to S48 vm interrupts.
*/
#include "signals1.h"

extern scheme_value Spending_interruptsS, Sinterrupt_handlersS;

/* Translate Unix signal numbers to S48 interrupt numbers. */

int sig2interrupt(int signal)
{
  return ( signal < 0 || signal > max_sig ) ? -1 : sig2int[signal];
  }


/* Hack the blocked-signal mask.
*******************************************************************************
*/


#include "machine/sigset.h"

int set_procmask(int hi, int lo, int *old_lo_p)
{
    sigset_t mask, old_mask;
    int old_hi;

    make_sigset(&mask, hi, lo);
    
    sigprocmask(SIG_SETMASK, &mask, &old_mask);
    split_sigset(old_mask, &old_hi, old_lo_p);
    return old_hi;
    }


int get_procmask(int *old_lo_p)
{
    sigset_t old_mask;
    int old_hi;

    sigprocmask(SIG_SETMASK, NULL, &old_mask);
    split_sigset(old_mask, &old_hi, old_lo_p);
    return old_hi;
    }


/* Set/Get signal handlers
*******************************************************************************
*/

static void scm_handle_sig(int sig)
{
  /*fprintf(stderr, "scm_handle_sig(%d) = int %d\n", sig, sig2int[sig]);*/
  Spending_interruptsS |= (1<<sig2int[sig]);
  }


/* handler_code: 0 => ignore, 1 => default, 2 => S48 VM */

/* Common code for two functions above. */
static scheme_value scsh_ret_sig(int retval, struct sigaction *oldsa,
				 int *old_hc, int *oflags)
{
    if( retval ) {
	*old_hc = -1;
	*oflags = -1;
	return ENTER_FIXNUM(errno);
	}
    if( oldsa->sa_handler == SIG_IGN )             *old_hc = 0;
    else if( oldsa->sa_handler == SIG_DFL )        *old_hc = 1;
    else if( oldsa->sa_handler == scm_handle_sig ) *old_hc = 2;
    else *old_hc = ENTER_FIXNUM(3); /* Unknown signal handler. */

    *oflags = oldsa->sa_flags;
    return SCHFALSE;
    }
      

scheme_value scsh_set_sig(int sig, int handler_code, int flags,
			  int *old_hc, int *oflags)
{
    struct sigaction new, old;

    sigemptyset(&new.sa_mask); /* WTF */
    new.sa_flags = flags;

    switch( handler_code ) {
    case 0: new.sa_handler = SIG_IGN; break;
    case 1: new.sa_handler = SIG_DFL; break;
    case 2: new.sa_handler = scm_handle_sig; break;
    default:
      fprintf(stderr, "Impossible handler_code in set_sig_handler: %d\n",
	      handler_code);
      exit(-1);
    }

    return scsh_ret_sig(sigaction(sig, &new, &old),
			&old, old_hc, oflags);
    }


scheme_value scsh_get_sig(int signal, int *old_hc, int *oflags)
{
    struct sigaction old;
    return scsh_ret_sig(sigaction(signal, NULL, &old),
			&old, old_hc, oflags);
    }


/* This guy is responsible for making the default action for a
** Unix signal happen. Because S48's signal handler system is
** interposed between delivery-to-the-process and
** delivery-to-the-scheme-handler, when the user sets a signal
** handler to default, we install a Scheme proc that calls this
** guy, instead of just slapping a SIGDFL in as the Unix handler.
** We only have to do this for signals whose default isn't "ignore," i.e.:
**   Posix: SIGALRM SIGHUP SIGINT SIGQUIT SIGTERM SIGUSR1 SIGUSR2
**   Non-Posix: SIGINFO SIGPOLL SIGPROF SIGVTALRM SIGXCPU SIGXFSZ SIGIO
** This way, the S48 signal-blocking mechanism can work.
**
** Weird, I know.
*/
void do_default_sigaction(int signal)
{
  sigset_t ss, old_ss;
  struct sigaction default_action, old_action;

  /* fprintf(stderr, "Doing default for signal %d\n", signal); */

  sigfillset(&ss);				/* Block everyone. */
  sigprocmask(SIG_SETMASK, &ss, &old_ss);

  default_action.sa_handler = SIG_DFL;		/* Set for default. */
  sigemptyset(&default_action.sa_mask);
  default_action.sa_flags = 0;
  sigaction(signal, &default_action, &old_action);

  raise(signal);			      	/* Raise the signal. */
  sigdelset(&ss, signal);
  sigprocmask(SIG_SETMASK, &ss, 0);		/* Handle it. */

  /* Most likely, we'll never get to here, as the default for
  ** the signals we're handling is "terminate," but we'll play it safe.
  */
  sigaction(signal, &old_action, 0);		/* Restore old handler, */
  sigprocmask(SIG_SETMASK, &old_ss, 0);		/* and mask.            */
  }


/* Set up the Unix signal system the way we want it for scsh. */

void install_scsh_handlers(void)
{
  struct sigaction new;
  int i;

  sigemptyset(&new.sa_mask); /* WTF */
  new.sa_handler = scm_handle_sig;
  new.sa_flags = 0;

  for(i=max_sig; i>=0; i--)
    if( sig2int[i] >= 0 ) {
      /* This is a signal we want the S48 interrupt system to handle. */
      sigaction(i, &new, 0);
      }

  /* Turn off SIGPIPE and SIGSYS -- they are handled by synchronous exceptions
  ** triggered by errno returns.
  */
  new.sa_handler = SIG_IGN;
  sigaction(SIGPIPE, &new, 0);
#ifdef SIGSYS
  sigaction(SIGSYS, &new, 0);
#endif
  }

/* Sneak me the S48 interrupt handlers vector. */
scheme_value get_int_handlers(void)
{
  return Sinterrupt_handlersS;
  }
