text: /*	$OpenBSD: lib_tstp.c,v 1.8 1998/07/23 21:19:44 millert Exp $	*/
  
  /****************************************************************************
   * Copyright (c) 1998 Free Software Foundation, Inc.                        *
   *                                                                          *
   * Permission is hereby granted, free of charge, to any person obtaining a  *
   * copy of this software and associated documentation files (the            *
   * "Software"), to deal in the Software without restriction, including      *
   * without limitation the rights to use, copy, modify, merge, publish,      *
   * distribute, distribute with modifications, sublicense, and/or sell       *
   * copies of the Software, and to permit persons to whom the Software is    *
   * furnished to do so, subject to the following conditions:                 *
   *                                                                          *
   * The above copyright notice and this permission notice shall be included  *
   * in all copies or substantial portions of the Software.                   *
   *                                                                          *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
   * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
   * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
   * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
   * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
   * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
   *                                                                          *
   * Except as contained in this notice, the name(s) of the above copyright   *
   * holders shall not be used in advertising or otherwise to promote the     *
   * sale, use or other dealings in this Software without prior written       *
   * authorization.                                                           *
   ****************************************************************************/
  
  /****************************************************************************
   *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
   *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
   ****************************************************************************/
  
  
  /*
  **	lib_tstp.c
  **
  **	The routine _nc_signal_handler().
  **
  */
  
  #include <curses.priv.h>
  
  #include <signal.h>
  #include <SigAction.h>
  
  #if defined(SVR4_ACTION) && !defined(_POSIX_SOURCE)
  #define _POSIX_SOURCE
  #endif
  
  MODULE_ID("$From: lib_tstp.c,v 1.15 1998/02/11 12:13:57 tom Exp $")
  
  /*
   * Note: This code is fragile!  Its problem is that different OSs
   * handle restart of system calls interrupted by signals differently.
   * The ncurses code needs signal-call restart to happen -- otherwise,
   * interrupted wgetch() calls will return FAIL, probably making the
   * application think the input stream has ended and it should
   * terminate.  In particular, you know you have this problem if, when
   * you suspend an ncurses-using lynx with ^Z and resume, it dies
   * immediately.
   *
   * Default behavior of POSIX sigaction(2) is not to restart
   * interrupted system calls, but Linux's sigaction does it anyway (at
   * least, on and after the 1.1.47 I (esr) use).  Thus this code works
   * OK under Linux.  The 4.4BSD sigaction(2) supports a (non-portable)
   * SA_RESTART flag that forces the right behavior.  Thus, this code
   * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it
   * does not).
   *
   * Stock System Vs (and anything else using a strict-POSIX
   * sigaction(2) without SA_RESTART) may have a problem.  Possible
   * solutions:
   *
   *    sigvec      restarts by default (SV_INTERRUPT flag to not restart)
   *    signal      restarts by default in SVr4 (assuming you link with -lucb)
   *                and BSD, but not SVr3.
   *    sigset      restarts, but is only available under SVr4/Solaris.
   *
   * The signal(3) call is mandated by the ANSI standard, and its
   * interaction with sigaction(2) is described in the POSIX standard
   * (3.3.4.2, page 72,line 934).  According to section 8.1, page 191,
   * however, signal(3) itself is not required by POSIX.1.  And POSIX is
   * silent on whether it is required to restart signals.
   *
   * So.  The present situation is, we use sigaction(2) with no
   * guarantee of restart anywhere but on Linux and BSD.  We could
   * switch to signal(3) and collar Linux, BSD, and SVr4.  Any way
   * we slice it, System V UNIXes older than SVr4 will probably lose
   * (this may include XENIX).
   *
   * This implementation will probably be changed to use signal(3) in
   * the future.  If nothing else, it's simpler...
   */
  
  #ifdef SIGTSTP
  static void tstp(int dummy GCC_UNUSED)
  {
  	sigset_t mask, omask;
  	sigaction_t act, oact;
  
  	T(("tstp() called"));
  
  	/*
  	 * The user may have changed the prog_mode tty bits, so save them.
  	 */
  	def_prog_mode();
  
  	/*
  	 * Block window change and timer signals.  The latter
  	 * is because applications use timers to decide when
  	 * to repaint the screen.
  	 */
  	(void)sigemptyset(&mask);
  	(void)sigaddset(&mask, SIGALRM);
  #if USE_SIGWINCH
  	(void)sigaddset(&mask, SIGWINCH);
  #endif
  	(void)sigprocmask(SIG_BLOCK, &mask, &omask);
  
  	/*
  	 * End window mode, which also resets the terminal state to the
  	 * original (pre-curses) modes.
  	 */
  	endwin();
  
  	/* Unblock SIGTSTP. */
  	(void)sigemptyset(&mask);
  	(void)sigaddset(&mask, SIGTSTP);
  	(void)sigprocmask(SIG_UNBLOCK, &mask, NULL);
  
  	/* Now we want to resend SIGSTP to this process and suspend it */
  	act.sa_handler = SIG_DFL;
  	sigemptyset(&act.sa_mask);
  	act.sa_flags = 0;
  #ifdef SA_RESTART
  	act.sa_flags |= SA_RESTART;
  #endif /* SA_RESTART */
  	sigaction(SIGTSTP, &act, &oact);
  	kill(getpid(), SIGTSTP);
  
  	/* Process gets suspended...time passes...process resumes */
  
  	T(("SIGCONT received"));
  	sigaction(SIGTSTP, &oact, NULL);
  	flushinp();
  
  	/*
  	 * If the user modified the tty state while suspended, he wants
  	 * those changes to stick.  So save the new "default" terminal state.
  	 */
  	def_shell_mode();
  
  	/*
  	 * This relies on the fact that doupdate() will restore the
  	 * program-mode tty state, and issue enter_ca_mode if need be.
  	 */
  	doupdate();
  
  	/* Reset the signals. */
  	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
  }
  #endif	/* defined(SIGTSTP) */
  
  static void cleanup(int sig)
  {
  	/*
  	 * Actually, doing any sort of I/O from within an signal handler is
  	 * "unsafe".  But we'll _try_ to clean up the screen and terminal
  	 * settings on the way out.
  	 */
  	if (sig == SIGINT
  	 || sig == SIGQUIT) {
  #if HAVE_SIGACTION || HAVE_SIGVEC
  		sigaction_t act;
  		sigemptyset(&act.sa_mask);
  		act.sa_flags = 0;
  		act.sa_handler = SIG_IGN;
  		if (sigaction(sig, &act, (sigaction_t *)0) == 0)
  #else
  		if (signal(sig, SIG_IGN) != SIG_ERR)
  #endif
  		{
  		    SCREEN *scan = _nc_screen_chain;
  		    while(scan)
  		    {
  			set_term(scan);
  			endwin();
  			SP->_endwin = FALSE; /* in case we have an atexit! */
  			scan = scan->_next_screen;
  		    }
  		}
  	}
  	exit(EXIT_FAILURE);
  }
  
  #if USE_SIGWINCH
  static void sigwinch(int sig GCC_UNUSED)
  {
      SCREEN *scan = _nc_screen_chain;
      while(scan)
      {
  	scan->_sig_winch = TRUE;
  	scan = scan->_next_screen;
      }
  }
  #endif /* USE_SIGWINCH */
  
  /*
   * If the given signal is still in its default state, set it to the given
   * handler.
   */
  #if HAVE_SIGACTION || HAVE_SIGVEC
  static int CatchIfDefault(int sig, sigaction_t *act)
  {
  	sigaction_t old_act;
  
  	if (sigaction(sig, (sigaction_t *)0, &old_act) == 0
  	 && (old_act.sa_handler == SIG_DFL
  #if USE_SIGWINCH
  	    || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN)
  #endif
  	    )) {
  		(void)sigaction(sig, act, (sigaction_t *)0);
  		return TRUE;
  	}
  	return FALSE;
  }
  #else
  static int CatchIfDefault(int sig, RETSIGTYPE (*handler)())
  {
  	void	(*ohandler)();
  
  	ohandler = signal(sig, SIG_IGN);
  	if (ohandler == SIG_DFL
  #if USE_SIGWINCH
  	    || (sig == SIGWINCH && ohandler == SIG_IGN)
  #endif
  	) {
  		signal(sig, handler);
  		return TRUE;
  	} else {
  		signal(sig, ohandler);
  		return FALSE;
  	}
  }
  #endif
  
  /*
   * This is invoked once at the beginning (e.g., from 'initscr()'), to
   * initialize the signal catchers, and thereafter when spawning a shell (and
   * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher.
   *
   * If the application has already set one of the signals, we'll not modify it
   * (during initialization).
   *
   * The XSI document implies that we shouldn't keep the SIGTSTP handler if
   * the caller later changes its mind, but that doesn't seem correct.
   */
  void _nc_signal_handler(bool enable)
  {
  #ifdef SIGTSTP		/* Xenix 2.x doesn't have this */
  static sigaction_t act, oact;
  static int ignore;
  
  	if (!ignore)
  	{
  		if (!enable)
  		{
  			act.sa_handler = SIG_IGN;
  			sigaction(SIGTSTP, &act, &oact);
  		}
  		else if (act.sa_handler)
  		{
  			sigaction(SIGTSTP, &oact, NULL);
  		}
  		else	/*initialize */
  		{
  			sigemptyset(&act.sa_mask);
  			act.sa_flags = 0;
  #if USE_SIGWINCH
  			act.sa_handler = sigwinch;
  			CatchIfDefault(SIGWINCH, &act);
  #endif
  
  #ifdef SA_RESTART
  			act.sa_flags |= SA_RESTART;
  #endif /* SA_RESTART */
  			act.sa_handler = cleanup;
  			CatchIfDefault(SIGINT,  &act);
  			CatchIfDefault(SIGTERM, &act);
  
  			act.sa_handler = tstp;
  			if (!CatchIfDefault(SIGTSTP, &act))
  				ignore = TRUE;
  		}
  	}
  #else /* !SIGTSTP */
  	if (enable)
  	{
  #if HAVE_SIGACTION || HAVE_SIGVEC
  		static sigaction_t act;
  		sigemptyset(&act.sa_mask);
  #if USE_SIGWINCH
  		act.sa_handler = sigwinch;
  		CatchIfDefault(SIGWINCH, &act);
  #endif
  #ifdef SA_RESTART
  		act.sa_flags |= SA_RESTART;
  #endif /* SA_RESTART */
  		act.sa_handler = cleanup;
  		CatchIfDefault(SIGINT,  &act);
  		CatchIfDefault(SIGTERM, &act);
  
  #else /* !(HAVE_SIGACTION || HAVE_SIGVEC) */
  
  		CatchIfDefault(SIGINT,  cleanup);
  		CatchIfDefault(SIGTERM, cleanup);
  #if USE_SIGWINCH
  		CatchIfDefault(SIGWINCH, sigwinch);
  #endif
  #endif /* !(HAVE_SIGACTION || HAVE_SIGVEC) */
  	}
  #endif /* !SIGTSTP */
  }
  