/*
 * Copyright notice from original mutt:
 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
 *
 * This file is part of mutt-ng, see http://www.muttng.org/.
 * It's licensed under the GNU General Public License,
 * please see the file GPL in the top level source directory.
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "mutt.h"
#include "mutt_curses.h"

#include "lib/intl.h"

#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>

static sigset_t Sigset;
static sigset_t SigsetSys;
static struct sigaction SysOldInt;
static struct sigaction SysOldQuit;
static int IsEndwin = 0;

/* Attempt to catch "ordinary" signals and shut down gracefully. */
RETSIGTYPE exit_handler (int sig)
{
  curs_set (1);
  endwin ();                    /* just to be safe */
#if SYS_SIGLIST_DECLARED
  printf (_("%s...  Exiting.\n"), sys_siglist[sig]);
#else
#if (__sun__ && __svr4__)
  printf (_("Caught %s...  Exiting.\n"), _sys_siglist[sig]);
#else
#if (__alpha && __osf__)
  printf (_("Caught %s...  Exiting.\n"), __sys_siglist[sig]);
#else
  printf (_("Caught signal %d...  Exiting.\n"), sig);
#endif
#endif
#endif
  exit (0);
}

RETSIGTYPE chld_handler (int sig)
{
  /* empty */
}

RETSIGTYPE sighandler (int sig)
{
  int save_errno = errno;

  switch (sig) {
  case SIGTSTP:                /* user requested a suspend */
    if (!option (OPTSUSPEND))
      break;
    IsEndwin = isendwin ();
    curs_set (1);
    if (!IsEndwin)
      endwin ();
    kill (0, SIGSTOP);

  case SIGCONT:
    if (!IsEndwin)
      refresh ();
    mutt_curs_set (-1);
#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
    /* We don't receive SIGWINCH when suspended; however, no harm is done by
     * just assuming we received one, and triggering the 'resize' anyway. */
    SigWinch = 1;
#endif
    break;

#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
  case SIGWINCH:
    SigWinch = 1;
    break;
#endif

  case SIGINT:
    SigInt = 1;
    break;

  }
  errno = save_errno;
}

#ifdef USE_SLANG_CURSES
int mutt_intr_hook (void)
{
  return (-1);
}
#endif /* USE_SLANG_CURSES */

void mutt_signal_init (void)
{
  struct sigaction act;

  sigemptyset (&act.sa_mask);
  act.sa_flags = 0;
  act.sa_handler = SIG_IGN;
  sigaction (SIGPIPE, &act, NULL);

  act.sa_handler = exit_handler;
  sigaction (SIGTERM, &act, NULL);
  sigaction (SIGHUP, &act, NULL);
  sigaction (SIGQUIT, &act, NULL);

  /* we want to avoid race conditions */
  sigaddset (&act.sa_mask, SIGTSTP);

  act.sa_handler = sighandler;

  /* we want SIGALRM to abort the current syscall, so we do this before
   * setting the SA_RESTART flag below.  currently this is only used to
   * timeout on a connect() call in a reasonable amout of time.
   */
  sigaction (SIGALRM, &act, NULL);

  /* we also don't want to mess with interrupted system calls */
#ifdef SA_RESTART
  act.sa_flags = SA_RESTART;
#endif

  sigaction (SIGCONT, &act, NULL);
  sigaction (SIGTSTP, &act, NULL);
  sigaction (SIGINT, &act, NULL);
#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
  sigaction (SIGWINCH, &act, NULL);
#endif

  /* POSIX doesn't allow us to ignore SIGCHLD,
   * so we just install a dummy handler for it
   */
  act.sa_handler = chld_handler;
  /* don't need to block any other signals here */
  sigemptyset (&act.sa_mask);
  /* we don't want to mess with stopped children */
  act.sa_flags |= SA_NOCLDSTOP;
  sigaction (SIGCHLD, &act, NULL);

#ifdef USE_SLANG_CURSES
  /* This bit of code is required because of the implementation of
   * SLcurses_wgetch().  If a signal is received (like SIGWINCH) when we
   * are in blocking mode, SLsys_getkey() will not return an error unless
   * a handler function is defined and it returns -1.  This is needed so
   * that if the user resizes the screen while at a prompt, it will just
   * abort and go back to the main-menu.
   */
  SLang_getkey_intr_hook = mutt_intr_hook;
#endif
}

/* signals which are important to block while doing critical ops */
void mutt_block_signals (void)
{
  if (!option (OPTSIGNALSBLOCKED)) {
    sigemptyset (&Sigset);
    sigaddset (&Sigset, SIGTERM);
    sigaddset (&Sigset, SIGHUP);
    sigaddset (&Sigset, SIGTSTP);
    sigaddset (&Sigset, SIGINT);
#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
    sigaddset (&Sigset, SIGWINCH);
#endif
    sigprocmask (SIG_BLOCK, &Sigset, 0);
    set_option (OPTSIGNALSBLOCKED);
  }
}

/* restore the previous signal mask */
void mutt_unblock_signals (void)
{
  if (option (OPTSIGNALSBLOCKED)) {
    sigprocmask (SIG_UNBLOCK, &Sigset, 0);
    unset_option (OPTSIGNALSBLOCKED);
  }
}

void mutt_block_signals_system (void)
{
  struct sigaction sa;

  if (!option (OPTSYSSIGNALSBLOCKED)) {
    /* POSIX: ignore SIGINT and SIGQUIT & block SIGCHLD  before exec */
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    sigemptyset (&sa.sa_mask);
    sigaction (SIGINT, &sa, &SysOldInt);
    sigaction (SIGQUIT, &sa, &SysOldQuit);

    sigemptyset (&SigsetSys);
    sigaddset (&SigsetSys, SIGCHLD);
    sigprocmask (SIG_BLOCK, &SigsetSys, 0);
    set_option (OPTSYSSIGNALSBLOCKED);
  }
}

void mutt_unblock_signals_system (int catch)
{
  if (option (OPTSYSSIGNALSBLOCKED)) {
    sigprocmask (SIG_UNBLOCK, &SigsetSys, NULL);
    if (catch) {
      sigaction (SIGQUIT, &SysOldQuit, NULL);
      sigaction (SIGINT, &SysOldInt, NULL);
    }
    else {
      struct sigaction sa;

      sa.sa_handler = SIG_DFL;
      sigemptyset (&sa.sa_mask);
      sa.sa_flags = 0;
      sigaction (SIGQUIT, &sa, NULL);
      sigaction (SIGINT, &sa, NULL);
    }

    unset_option (OPTSYSSIGNALSBLOCKED);
  }
}

void mutt_allow_interrupt (int disposition)
{
  struct sigaction sa;

  memset (&sa, 0, sizeof sa);
  sa.sa_handler = sighandler;
#ifdef SA_RESTART
  if (disposition == 0)
    sa.sa_flags |= SA_RESTART;
#endif
  sigaction (SIGINT, &sa, NULL);
}


syntax highlighted by Code2HTML, v. 0.9.1