1/* signals.c -- install and maintain signal handlers.
2   $Id: signals.c,v 1.7 2004/04/11 17:56:46 karl Exp $
3
4   Copyright (C) 1993, 1994, 1995, 1998, 2002, 2003, 2004 Free Software
5   Foundation, Inc.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21   Originally written by Brian Fox (bfox@ai.mit.edu). */
22
23#include "info.h"
24#include "signals.h"
25
26void initialize_info_signal_handler (void);
27
28/* **************************************************************** */
29/*                                                                  */
30/*              Pretending That We Have POSIX Signals               */
31/*                                                                  */
32/* **************************************************************** */
33
34#if !defined (HAVE_SIGPROCMASK) && defined (HAVE_SIGSETMASK)
35/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
36static void
37sigprocmask (int operation, int *newset, int *oldset)
38{
39  switch (operation)
40    {
41    case SIG_UNBLOCK:
42      sigsetmask (sigblock (0) & ~(*newset));
43      break;
44
45    case SIG_BLOCK:
46      *oldset = sigblock (*newset);
47      break;
48
49    case SIG_SETMASK:
50      sigsetmask (*newset);
51      break;
52
53    default:
54      abort ();
55    }
56}
57#endif /* !HAVE_SIGPROCMASK && HAVE_SIGSETMASK */
58
59/* **************************************************************** */
60/*                                                                  */
61/*                  Signal Handling for Info                        */
62/*                                                                  */
63/* **************************************************************** */
64
65#if defined (HAVE_SIGACTION) || defined (HAVE_SIGPROCMASK) ||\
66  defined (HAVE_SIGSETMASK)
67static void
68mask_termsig (sigset_t *set)
69{
70# if defined (SIGTSTP)
71  sigaddset (set, SIGTSTP);
72  sigaddset (set, SIGTTOU);
73  sigaddset (set, SIGTTIN);
74# endif
75# if defined (SIGWINCH)
76  sigaddset (set, SIGWINCH);
77# endif
78#if defined (SIGQUIT)
79  sigaddset (set, SIGQUIT);
80#endif
81#if defined (SIGINT)
82  sigaddset (set, SIGINT);
83#endif
84# if defined (SIGUSR1)
85  sigaddset (set, SIGUSR1);
86# endif
87}
88#endif /* HAVE_SIGACTION || HAVE_SIGPROCMASK || HAVE_SIGSETMASK */
89
90static RETSIGTYPE info_signal_proc (int sig);
91#if defined (HAVE_SIGACTION)
92typedef struct sigaction signal_info;
93signal_info info_signal_handler;
94
95static void
96set_termsig (int sig, signal_info *old)
97{
98  sigaction (sig, &info_signal_handler, old);
99}
100
101static void
102restore_termsig (int sig, const signal_info *saved)
103{
104  sigaction (sig, saved, NULL);
105}
106#else /* !HAVE_SIGACTION */
107typedef RETSIGTYPE (*signal_info) ();
108#define set_termsig(sig, old) (void)(*(old) = signal (sig, info_signal_proc))
109#define restore_termsig(sig, saved) (void)signal (sig, *(saved))
110#define info_signal_handler info_signal_proc
111static int term_conf_busy = 0;
112#endif /* !HAVE_SIGACTION */
113
114static signal_info old_TSTP, old_TTOU, old_TTIN;
115static signal_info old_WINCH, old_INT, old_USR1;
116static signal_info old_QUIT;
117
118void
119initialize_info_signal_handler (void)
120{
121#ifdef SA_NOCLDSTOP
122  /* (Based on info from Paul Eggert found in coreutils.)  Don't use
123     HAVE_SIGACTION to decide whether to use the sa_handler, sa_flags,
124     sa_mask members, as some systems (Solaris 7+) don't define them.  Use
125     SA_NOCLDSTOP instead; it's been part of POSIX.1 since day 1 (in 1988).  */
126  info_signal_handler.sa_handler = info_signal_proc;
127  info_signal_handler.sa_flags = 0;
128  mask_termsig (&info_signal_handler.sa_mask);
129#endif /* SA_NOCLDSTOP */
130
131#if defined (SIGTSTP)
132  set_termsig (SIGTSTP, &old_TSTP);
133  set_termsig (SIGTTOU, &old_TTOU);
134  set_termsig (SIGTTIN, &old_TTIN);
135#endif /* SIGTSTP */
136
137#if defined (SIGWINCH)
138  set_termsig (SIGWINCH, &old_WINCH);
139#endif
140
141#if defined (SIGQUIT)
142  set_termsig (SIGQUIT, &old_QUIT);
143#endif
144
145#if defined (SIGINT)
146  set_termsig (SIGINT, &old_INT);
147#endif
148
149#if defined (SIGUSR1)
150  /* Used by DJGPP to simulate SIGTSTP on Ctrl-Z.  */
151  set_termsig (SIGUSR1, &old_USR1);
152#endif
153}
154
155static void
156redisplay_after_signal (void)
157{
158  terminal_clear_screen ();
159  display_clear_display (the_display);
160  window_mark_chain (windows, W_UpdateWindow);
161  display_update_display (windows);
162  display_cursor_at_point (active_window);
163  fflush (stdout);
164}
165
166static void
167reset_info_window_sizes (void)
168{
169  terminal_goto_xy (0, 0);
170  fflush (stdout);
171  terminal_unprep_terminal ();
172  terminal_get_screen_size ();
173  terminal_prep_terminal ();
174  display_initialize_display (screenwidth, screenheight);
175  window_new_screen_size (screenwidth, screenheight);
176  redisplay_after_signal ();
177}
178
179static RETSIGTYPE
180info_signal_proc (int sig)
181{
182  signal_info *old_signal_handler = NULL;
183
184#if !defined (HAVE_SIGACTION)
185  /* best effort: first increment this counter and later block signals */
186  if (term_conf_busy)
187    return;
188  term_conf_busy++;
189#if defined (HAVE_SIGPROCMASK) || defined (HAVE_SIGSETMASK)
190    {
191      sigset_t nvar, ovar;
192      sigemptyset (&nvar);
193      mask_termsig (&nvar);
194      sigprocmask (SIG_BLOCK, &nvar, &ovar);
195    }
196#endif /* HAVE_SIGPROCMASK || HAVE_SIGSETMASK */
197#endif /* !HAVE_SIGACTION */
198  switch (sig)
199    {
200#if defined (SIGTSTP)
201    case SIGTSTP:
202    case SIGTTOU:
203    case SIGTTIN:
204#endif
205#if defined (SIGQUIT)
206    case SIGQUIT:
207#endif
208#if defined (SIGINT)
209    case SIGINT:
210#endif
211      {
212#if defined (SIGTSTP)
213        if (sig == SIGTSTP)
214          old_signal_handler = &old_TSTP;
215        if (sig == SIGTTOU)
216          old_signal_handler = &old_TTOU;
217        if (sig == SIGTTIN)
218          old_signal_handler = &old_TTIN;
219#endif /* SIGTSTP */
220#if defined (SIGQUIT)
221        if (sig == SIGQUIT)
222          old_signal_handler = &old_QUIT;
223#endif /* SIGQUIT */
224#if defined (SIGINT)
225        if (sig == SIGINT)
226          old_signal_handler = &old_INT;
227#endif /* SIGINT */
228
229        /* For stop signals, restore the terminal IO, leave the cursor
230           at the bottom of the window, and stop us. */
231        terminal_goto_xy (0, screenheight - 1);
232        terminal_clear_to_eol ();
233        fflush (stdout);
234        terminal_unprep_terminal ();
235	restore_termsig (sig, old_signal_handler);
236	UNBLOCK_SIGNAL (sig);
237	kill (getpid (), sig);
238
239        /* The program is returning now.  Restore our signal handler,
240           turn on terminal handling, redraw the screen, and place the
241           cursor where it belongs. */
242        terminal_prep_terminal ();
243	set_termsig (sig, old_signal_handler);
244	/* window size might be changed while sleeping */
245	reset_info_window_sizes ();
246      }
247      break;
248
249#if defined (SIGWINCH) || defined (SIGUSR1)
250#ifdef SIGWINCH
251    case SIGWINCH:
252#endif
253#ifdef SIGUSR1
254    case SIGUSR1:
255#endif
256      {
257	/* Turn off terminal IO, tell our parent that the window has changed,
258	   then reinitialize the terminal and rebuild our windows. */
259#ifdef SIGWINCH
260	if (sig == SIGWINCH)
261	  old_signal_handler = &old_WINCH;
262#endif
263#ifdef SIGUSR1
264	if (sig == SIGUSR1)
265	  old_signal_handler = &old_USR1;
266#endif
267	terminal_goto_xy (0, 0);
268	fflush (stdout);
269	terminal_unprep_terminal (); /* needless? */
270	restore_termsig (sig, old_signal_handler);
271	UNBLOCK_SIGNAL (sig);
272	kill (getpid (), sig);
273
274	/* After our old signal handler returns... */
275	set_termsig (sig, old_signal_handler); /* needless? */
276	terminal_prep_terminal ();
277	reset_info_window_sizes ();
278      }
279      break;
280#endif /* SIGWINCH || SIGUSR1 */
281    }
282#if !defined (HAVE_SIGACTION)
283  /* at this time it is safer to perform unblock after decrement */
284  term_conf_busy--;
285#if defined (HAVE_SIGPROCMASK) || defined (HAVE_SIGSETMASK)
286    {
287      sigset_t nvar, ovar;
288      sigemptyset (&nvar);
289      mask_termsig (&nvar);
290      sigprocmask (SIG_UNBLOCK, &nvar, &ovar);
291    }
292#endif /* HAVE_SIGPROCMASK || HAVE_SIGSETMASK */
293#endif /* !HAVE_SIGACTION */
294}
295/* vim: set sw=2 cino={1s>2sn-s^-se-s: */
296