summaryrefslogtreecommitdiff
path: root/info/signals.c
diff options
context:
space:
mode:
Diffstat (limited to 'info/signals.c')
-rw-r--r--info/signals.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/info/signals.c b/info/signals.c
new file mode 100644
index 0000000..0657f97
--- /dev/null
+++ b/info/signals.c
@@ -0,0 +1,294 @@
+/* signals.c -- install and maintain signal handlers.
+ $Id: signals.c,v 1.10 2007/07/01 21:20:31 karl Exp $
+
+ Copyright (C) 1993, 1994, 1995, 1998, 2002, 2003, 2004, 2007
+ 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
+
+ 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: */