/* signals.c -- install and maintain signal handlers. $Id: signals.c,v 1.7 2004/04/11 17:56:46 karl Exp $ Copyright (C) 1993, 1994, 1995, 1998, 2002, 2003, 2004 Free Software Foundation, Inc. This program 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 program 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Originally written by Brian Fox (bfox@ai.mit.edu). */ #include "info.h" #include "signals.h" void initialize_info_signal_handler (void); /* **************************************************************** */ /* */ /* Pretending That We Have POSIX Signals */ /* */ /* **************************************************************** */ #if !defined (HAVE_SIGPROCMASK) && defined (HAVE_SIGSETMASK) /* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */ static void sigprocmask (int operation, int *newset, int *oldset) { switch (operation) { case SIG_UNBLOCK: sigsetmask (sigblock (0) & ~(*newset)); break; case SIG_BLOCK: *oldset = sigblock (*newset); break; case SIG_SETMASK: sigsetmask (*newset); break; default: abort (); } } #endif /* !HAVE_SIGPROCMASK && HAVE_SIGSETMASK */ /* **************************************************************** */ /* */ /* Signal Handling for Info */ /* */ /* **************************************************************** */ #if defined (HAVE_SIGACTION) || defined (HAVE_SIGPROCMASK) ||\ defined (HAVE_SIGSETMASK) static void mask_termsig (sigset_t *set) { # if defined (SIGTSTP) sigaddset (set, SIGTSTP); sigaddset (set, SIGTTOU); sigaddset (set, SIGTTIN); # endif # if defined (SIGWINCH) sigaddset (set, SIGWINCH); # endif #if defined (SIGQUIT) sigaddset (set, SIGQUIT); #endif #if defined (SIGINT) sigaddset (set, SIGINT); #endif # if defined (SIGUSR1) sigaddset (set, SIGUSR1); # endif } #endif /* HAVE_SIGACTION || HAVE_SIGPROCMASK || HAVE_SIGSETMASK */ static RETSIGTYPE info_signal_proc (int sig); #if defined (HAVE_SIGACTION) typedef struct sigaction signal_info; signal_info info_signal_handler; static void set_termsig (int sig, signal_info *old) { sigaction (sig, &info_signal_handler, old); } static void restore_termsig (int sig, const signal_info *saved) { sigaction (sig, saved, NULL); } #else /* !HAVE_SIGACTION */ typedef RETSIGTYPE (*signal_info) (); #define set_termsig(sig, old) (void)(*(old) = signal (sig, info_signal_proc)) #define restore_termsig(sig, saved) (void)signal (sig, *(saved)) #define info_signal_handler info_signal_proc static int term_conf_busy = 0; #endif /* !HAVE_SIGACTION */ static signal_info old_TSTP, old_TTOU, old_TTIN; static signal_info old_WINCH, old_INT, old_USR1; static signal_info old_QUIT; void initialize_info_signal_handler (void) { #ifdef SA_NOCLDSTOP /* (Based on info from Paul Eggert found in coreutils.) Don't use HAVE_SIGACTION to decide whether to use the sa_handler, sa_flags, sa_mask members, as some systems (Solaris 7+) don't define them. Use SA_NOCLDSTOP instead; it's been part of POSIX.1 since day 1 (in 1988). */ info_signal_handler.sa_handler = info_signal_proc; info_signal_handler.sa_flags = 0; mask_termsig (&info_signal_handler.sa_mask); #endif /* SA_NOCLDSTOP */ #if defined (SIGTSTP) set_termsig (SIGTSTP, &old_TSTP); set_termsig (SIGTTOU, &old_TTOU); set_termsig (SIGTTIN, &old_TTIN); #endif /* SIGTSTP */ #if defined (SIGWINCH) set_termsig (SIGWINCH, &old_WINCH); #endif #if defined (SIGQUIT) set_termsig (SIGQUIT, &old_QUIT); #endif #if defined (SIGINT) set_termsig (SIGINT, &old_INT); #endif #if defined (SIGUSR1) /* Used by DJGPP to simulate SIGTSTP on Ctrl-Z. */ set_termsig (SIGUSR1, &old_USR1); #endif } static void redisplay_after_signal (void) { terminal_clear_screen (); display_clear_display (the_display); window_mark_chain (windows, W_UpdateWindow); display_update_display (windows); display_cursor_at_point (active_window); fflush (stdout); } static void reset_info_window_sizes (void) { terminal_goto_xy (0, 0); fflush (stdout); terminal_unprep_terminal (); terminal_get_screen_size (); terminal_prep_terminal (); display_initialize_display (screenwidth, screenheight); window_new_screen_size (screenwidth, screenheight); redisplay_after_signal (); } static RETSIGTYPE info_signal_proc (int sig) { signal_info *old_signal_handler = NULL; #if !defined (HAVE_SIGACTION) /* best effort: first increment this counter and later block signals */ if (term_conf_busy) return; term_conf_busy++; #if defined (HAVE_SIGPROCMASK) || defined (HAVE_SIGSETMASK) { sigset_t nvar, ovar; sigemptyset (&nvar); mask_termsig (&nvar); sigprocmask (SIG_BLOCK, &nvar, &ovar); } #endif /* HAVE_SIGPROCMASK || HAVE_SIGSETMASK */ #endif /* !HAVE_SIGACTION */ switch (sig) { #if defined (SIGTSTP) case SIGTSTP: case SIGTTOU: case SIGTTIN: #endif #if defined (SIGQUIT) case SIGQUIT: #endif #if defined (SIGINT) case SIGINT: #endif { #if defined (SIGTSTP) if (sig == SIGTSTP) old_signal_handler = &old_TSTP; if (sig == SIGTTOU) old_signal_handler = &old_TTOU; if (sig == SIGTTIN) old_signal_handler = &old_TTIN; #endif /* SIGTSTP */ #if defined (SIGQUIT) if (sig == SIGQUIT) old_signal_handler = &old_QUIT; #endif /* SIGQUIT */ #if defined (SIGINT) if (sig == SIGINT) old_signal_handler = &old_INT; #endif /* SIGINT */ /* For stop signals, restore the terminal IO, leave the cursor at the bottom of the window, and stop us. */ terminal_goto_xy (0, screenheight - 1); terminal_clear_to_eol (); fflush (stdout); terminal_unprep_terminal (); restore_termsig (sig, old_signal_handler); UNBLOCK_SIGNAL (sig); kill (getpid (), sig); /* The program is returning now. Restore our signal handler, turn on terminal handling, redraw the screen, and place the cursor where it belongs. */ terminal_prep_terminal (); set_termsig (sig, old_signal_handler); /* window size might be changed while sleeping */ reset_info_window_sizes (); } break; #if defined (SIGWINCH) || defined (SIGUSR1) #ifdef SIGWINCH case SIGWINCH: #endif #ifdef SIGUSR1 case SIGUSR1: #endif { /* Turn off terminal IO, tell our parent that the window has changed, then reinitialize the terminal and rebuild our windows. */ #ifdef SIGWINCH if (sig == SIGWINCH) old_signal_handler = &old_WINCH; #endif #ifdef SIGUSR1 if (sig == SIGUSR1) old_signal_handler = &old_USR1; #endif terminal_goto_xy (0, 0); fflush (stdout); terminal_unprep_terminal (); /* needless? */ restore_termsig (sig, old_signal_handler); UNBLOCK_SIGNAL (sig); kill (getpid (), sig); /* After our old signal handler returns... */ set_termsig (sig, old_signal_handler); /* needless? */ terminal_prep_terminal (); reset_info_window_sizes (); } break; #endif /* SIGWINCH || SIGUSR1 */ } #if !defined (HAVE_SIGACTION) /* at this time it is safer to perform unblock after decrement */ term_conf_busy--; #if defined (HAVE_SIGPROCMASK) || defined (HAVE_SIGSETMASK) { sigset_t nvar, ovar; sigemptyset (&nvar); mask_termsig (&nvar); sigprocmask (SIG_UNBLOCK, &nvar, &ovar); } #endif /* HAVE_SIGPROCMASK || HAVE_SIGSETMASK */ #endif /* !HAVE_SIGACTION */ } /* vim: set sw=2 cino={1s>2sn-s^-se-s: */