/* libsizzle/sig.c - signal handling functions
 
  Copyright (C) 1999, 2000 Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
 
  This is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.
  
  This software is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this package; see the file COPYING.  If not, write to
  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  Boston, MA 02111-1307, USA.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif 

#include <signal.h>

#if ENABLE_NLS
# include <libintl.h>
# define _(String) gettext(String)
#else
# define _(String) (String)
#endif

#include "node.h"
#include "validate.h"
#include "capi.h"
/* sig.h gets included via eval.h.  */

/* The maximum number of signals supported.  */
#define MAX_SIGNALS 64

/* The count of pending signals.  */
int zzz_signal_count;

/* Array counting signals.  */
static int signals_requested[MAX_SIGNALS];

/* A table holding the signal handlers.  If a signal handler is #f,
   the signal is not handled.  */
static zzz_scm_t signal_slots[MAX_SIGNALS];

/* Array for storing the original signal handler on initialization.
   They are used when a Scheme signal handler is deleted and on
   finalization to restore the original setup.  */
static void (*orig_signals[MAX_SIGNALS])(int no);

/* The default signal handler.  Only sets a bit for the signal in our
   signal bitmask and increases the count of pending signals.  */
static void
zzz_default_signal_handler (int sig)
{
  signals_requested[sig]++;
  zzz_signal_count++;
  signal (sig, zzz_default_signal_handler);
}


/* The internal signal handler.  Checks for pending signals and
   executes the Scheme signal handler, if any is set.  */
result_t
zzz_internal_handle_signals (zzz_scm_t * result)
{
  int i;
  result_t err;

  for (i = 0; i < MAX_SIGNALS; i++)
    if (signals_requested[i] > 0)
      {
	signals_requested[i]--;
	zzz_signal_count--;
	if (!zzz_eq (signal_slots[i], zzz_false_value))
	  {
	    err = zzz_apply (zzz_interaction_environment, signal_slots[i],
			     zzz_cons (zzz_make_integer (i), NULL),
			     result);
	    if (err != RESULT_SUCCESS)
	      return err;
	  }
      }
  return RESULT_SUCCESS;
}

/*:doc
  signal
  (signal sig proc) => *unspecified*
  Install a signal handler.  After installing a signal handler with this
  function, @var{proc} is called whenever the signal @var{no} is sent to
  the Sizzle process.
  doc:*/
#define FUNC_NAME "signal"
static result_t
signal_func (zzz_scm_t env, zzz_scm_t sig, zzz_scm_t proc, zzz_scm_t * result)
{
  static char * s_func_name = FUNC_NAME;

  CHECK_TYPE (1, integer_p (sig), zzz_integer_type_name);
  CHECK_TYPE (2, bool_p (proc) || procedure_p (proc),
	      zzz_boolean_or_procedure_type_name);
  if (integer_val (sig) < 0 || integer_val (sig) >= MAX_SIGNALS)
    {
      *result = zzz_make_wta_exception
	(zzz_make_list
	 (zzz_make_string
	  (_("invalid signal number in call to `"), -1),
	  zzz_make_string (FUNC_NAME, -1),
	  zzz_make_string ("'", -1),
	  NULL));
      return RESULT_EXCEPTION;
    }
  if (bool_p (proc))
    {
      signal_slots[integer_val (sig)] = zzz_false_value;
      signal (integer_val (sig), orig_signals[integer_val (sig)]);
    }
  else
    {
      signal_slots[integer_val (sig)] = proc;
      signal (integer_val (sig), zzz_default_signal_handler);
    }
  *result = zzz_unspecified;
  return RESULT_SUCCESS;
}
#undef FUNC_NAME

result_t
zzz_init_sig (void)
{
  int i;

  zzz_signal_count = 0;
  for (i = 0; i < MAX_SIGNALS; i++)
    {
      orig_signals[i] = signal (i, SIG_DFL);
      signal (i, orig_signals[i]);
      signals_requested[i] = 0;
      signal_slots[i] = zzz_false_value;
      zzz_protect_global (signal_slots + i);
    }
  zzz_define_function ("signal", (zzz_prim_t) signal_func, argv_2_0_0);

  /* We simply define all signals which are defined as preprocessor
     macros.  */
#ifdef SIGBREAK
  zzz_define_constant ("SIGBREAK", zzz_make_integer (SIGBREAK));
#endif
#ifdef SIGHUP
  zzz_define_constant ("SIGHUP", zzz_make_integer (SIGHUP));
#endif
#ifdef SIGINT
  zzz_define_constant ("SIGINT", zzz_make_integer (SIGINT));
#endif
#ifdef SIGQUIT
  zzz_define_constant ("SIGQUIT", zzz_make_integer (SIGQUIT));
#endif
#ifdef SIGILL
  zzz_define_constant ("SIGILL", zzz_make_integer (SIGILL));
#endif
#ifdef SIGTRAP
  zzz_define_constant ("SIGTRAP", zzz_make_integer (SIGTRAP));
#endif
#ifdef SIGABRT
  zzz_define_constant ("SIGABRT", zzz_make_integer (SIGABRT));
#endif
#ifdef SIGIOT
  zzz_define_constant ("SIGIOT", zzz_make_integer (SIGIOT));
#endif
#ifdef SIGBUS
  zzz_define_constant ("SIGBUS", zzz_make_integer (SIGBUS));
#endif
#ifdef SIGFPE
  zzz_define_constant ("SIGFPE", zzz_make_integer (SIGFPE));
#endif
#ifdef SIGKILL
  zzz_define_constant ("SIGKILL", zzz_make_integer (SIGKILL));
#endif
#ifdef SIGUSR1
  zzz_define_constant ("SIGUSR1", zzz_make_integer (SIGUSR1));
#endif
#ifdef SIGSEGV
  zzz_define_constant ("SIGSEGV", zzz_make_integer (SIGSEGV));
#endif
#ifdef SIGUSR2
  zzz_define_constant ("SIGUSR2", zzz_make_integer (SIGUSR2));
#endif
#ifdef SIGPIPE
  zzz_define_constant ("SIGPIPE", zzz_make_integer (SIGPIPE));
#endif
#ifdef SIGALRM
  zzz_define_constant ("SIGALRM", zzz_make_integer (SIGALRM));
#endif
#ifdef SIGTERM
  zzz_define_constant ("SIGTERM", zzz_make_integer (SIGTERM));
#endif
#ifdef SIGSTKFLT
  zzz_define_constant ("SIGSTKFLT", zzz_make_integer (SIGSTKFLT));
#endif
#ifdef SIGCLD
  zzz_define_constant ("SIGCLD", zzz_make_integer (SIGCLD));
#endif
#ifdef SIGCHLD
  zzz_define_constant ("SIGCHLD", zzz_make_integer (SIGCHLD));
#endif
#ifdef SIGCONT
  zzz_define_constant ("SIGCONT", zzz_make_integer (SIGCONT));
#endif
#ifdef SIGSTOP
  zzz_define_constant ("SIGSTOP", zzz_make_integer (SIGSTOP));
#endif
#ifdef SIGTSTP
  zzz_define_constant ("SIGTSTP", zzz_make_integer (SIGTSTP));
#endif
#ifdef SIGTTIN
  zzz_define_constant ("SIGTTIN", zzz_make_integer (SIGTTIN));
#endif
#ifdef SIGTTOU
  zzz_define_constant ("SIGTTOU", zzz_make_integer (SIGTTOU));
#endif
#ifdef SIGURG
  zzz_define_constant ("SIGURG", zzz_make_integer (SIGURG));
#endif
#ifdef SIGXCPU
  zzz_define_constant ("SIGXCPU", zzz_make_integer (SIGXCPU));
#endif
#ifdef SIGXFSZ
  zzz_define_constant ("SIGXFSZ", zzz_make_integer (SIGXFSZ));
#endif
#ifdef SIGVTALRM
  zzz_define_constant ("SIGVTALRM", zzz_make_integer (SIGVTALRM));
#endif
#ifdef SIPROF
  zzz_define_constant ("SIGPROF", zzz_make_integer (SIGPROF));
#endif
#ifdef SIGWINCH
  zzz_define_constant ("SIGWINCH", zzz_make_integer (SIGWINCH));
#endif
#ifdef SIGPOLL
  zzz_define_constant ("SIGPOLL", zzz_make_integer (SIGPOLL));
#endif
#ifdef SIGIO
  zzz_define_constant ("SIGIO", zzz_make_integer (SIGIO));
#endif
#ifdef SIGPWR
  zzz_define_constant ("SIGPWR", zzz_make_integer (SIGPWR));
#endif
  return RESULT_SUCCESS;
}

result_t
zzz_finalize_sig (void)
{
  int i;

  for (i = 0; i < MAX_SIGNALS; i++)
    signal (i, orig_signals[i]);
  return RESULT_SUCCESS;
}
