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