signals.c revision 114478
1/* signals.c -- install and maintain Info signal handlers.
2   $Id: signals.c,v 1.4 2003/01/29 19:23:22 karl Exp $
3   $FreeBSD: head/contrib/texinfo/info/signals.c 114478 2003-05-02 00:50:59Z ru $
4
5   Copyright (C) 1993, 1994, 1995, 1998, 2002, 2003 Free Software
6   Foundation, Inc.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2, or (at your option)
11   any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
22   Written by Brian Fox (bfox@ai.mit.edu). */
23
24#include "info.h"
25#include "signals.h"
26
27/* **************************************************************** */
28/*                                                                  */
29/*              Pretending That We Have POSIX Signals               */
30/*                                                                  */
31/* **************************************************************** */
32
33#if !defined (HAVE_SIGPROCMASK) && defined (HAVE_SIGSETMASK)
34/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
35static void
36sigprocmask (operation, newset, oldset)
37     int operation, *newset, *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 (set)
69  sigset_t *set;
70{
71# if defined (SIGTSTP)
72  sigaddset (set, SIGTSTP);
73  sigaddset (set, SIGTTOU);
74  sigaddset (set, SIGTTIN);
75# endif
76# if defined (SIGWINCH)
77  sigaddset (set, SIGWINCH);
78# endif
79#if defined (SIGINT)
80  sigaddset (set, SIGINT);
81#endif
82# if defined (SIGUSR1)
83  sigaddset (set, SIGUSR1);
84# endif
85}
86#endif /* HAVE_SIGACTION || HAVE_SIGPROCMASK || HAVE_SIGSETMASK */
87
88static RETSIGTYPE info_signal_proc ();
89#if defined (HAVE_SIGACTION)
90typedef struct sigaction signal_info;
91signal_info info_signal_handler;
92
93static void
94set_termsig (sig, old)
95  int sig;
96  signal_info *old;
97{
98  sigaction (sig, &info_signal_handler, old);
99}
100
101static void
102restore_termsig (sig, saved)
103  int sig;
104  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, old_CONT;
118
119void
120initialize_info_signal_handler ()
121{
122#if defined (HAVE_SIGACTION)
123  info_signal_handler.sa_handler = info_signal_proc;
124  info_signal_handler.sa_flags = 0;
125  mask_termsig (&info_signal_handler.sa_mask);
126#endif /* HAVE_SIGACTION */
127
128#if defined (SIGTSTP)
129  set_termsig (SIGTSTP, &old_TSTP);
130  set_termsig (SIGTTOU, &old_TTOU);
131  set_termsig (SIGTTIN, &old_TTIN);
132#endif /* SIGTSTP */
133
134#if defined (SIGWINCH)
135  set_termsig (SIGWINCH, &old_WINCH);
136#if defined (SIGCONT)
137  set_termsig (SIGCONT, &old_CONT);
138#endif
139#endif
140
141#if defined (SIGINT)
142  set_termsig (SIGINT, &old_INT);
143#endif
144
145#if defined (SIGUSR1)
146  /* Used by DJGPP to simulate SIGTSTP on Ctrl-Z.  */
147  set_termsig (SIGUSR1, &old_USR1);
148#endif
149}
150
151static void
152redisplay_after_signal ()
153{
154  terminal_clear_screen ();
155  display_clear_display (the_display);
156  window_mark_chain (windows, W_UpdateWindow);
157  display_update_display (windows);
158  display_cursor_at_point (active_window);
159  fflush (stdout);
160}
161
162static void
163reset_info_window_sizes ()
164{
165  terminal_goto_xy (0, 0);
166  fflush (stdout);
167  terminal_unprep_terminal ();
168  terminal_get_screen_size ();
169  terminal_prep_terminal ();
170  display_initialize_display (screenwidth, screenheight);
171  window_new_screen_size (screenwidth, screenheight, NULL);
172  redisplay_after_signal ();
173}
174
175static RETSIGTYPE
176info_signal_proc (sig)
177     int sig;
178{
179  signal_info *old_signal_handler;
180
181#if !defined (HAVE_SIGACTION)
182  /* best effort: first increment this counter and later block signals */
183  if (term_conf_busy)
184    return;
185  term_conf_busy++;
186#if defined (HAVE_SIGPROCMASK) || defined (HAVE_SIGSETMASK)
187    {
188      sigset_t nvar, ovar;
189      sigemptyset (&nvar);
190      mask_termsig (&nvar);
191      sigprocmask (SIG_BLOCK, &nvar, &ovar);
192    }
193#endif /* HAVE_SIGPROCMASK || HAVE_SIGSETMASK */
194#endif /* !HAVE_SIGACTION */
195  switch (sig)
196    {
197#if defined (SIGTSTP)
198    case SIGTSTP:
199    case SIGTTOU:
200    case SIGTTIN:
201#endif
202#if defined (SIGINT)
203    case SIGINT:
204#endif
205      {
206#if defined (SIGTSTP)
207        if (sig == SIGTSTP)
208          old_signal_handler = &old_TSTP;
209        if (sig == SIGTTOU)
210          old_signal_handler = &old_TTOU;
211        if (sig == SIGTTIN)
212          old_signal_handler = &old_TTIN;
213#endif /* SIGTSTP */
214#if defined (SIGINT)
215        if (sig == SIGINT)
216          old_signal_handler = &old_INT;
217#endif /* SIGINT */
218
219        /* For stop signals, restore the terminal IO, leave the cursor
220           at the bottom of the window, and stop us. */
221        terminal_goto_xy (0, screenheight - 1);
222        terminal_clear_to_eol ();
223        fflush (stdout);
224        terminal_unprep_terminal ();
225	restore_termsig (sig, old_signal_handler);
226	UNBLOCK_SIGNAL (sig);
227	kill (getpid (), sig);
228
229        /* The program is returning now.  Restore our signal handler,
230           turn on terminal handling, redraw the screen, and place the
231           cursor where it belongs. */
232        terminal_prep_terminal ();
233	set_termsig (sig, old_signal_handler);
234	/* window size might be changed while sleeping */
235	reset_info_window_sizes ();
236      }
237      break;
238
239#if defined (SIGWINCH) || defined (SIGUSR1)
240#ifdef SIGWINCH
241#ifdef SIGCONT
242    case SIGCONT:
243      /* pretend a SIGWINCH in case the terminal window size has changed
244	 while we've been asleep */
245      /* FALLTHROUGH */
246#endif
247    case SIGWINCH:
248#endif
249#ifdef SIGUSR1
250    case SIGUSR1:
251#endif
252      {
253	/* Turn off terminal IO, tell our parent that the window has changed,
254	   then reinitialize the terminal and rebuild our windows. */
255#ifdef SIGWINCH
256	if (sig == SIGWINCH)
257	  old_signal_handler = &old_WINCH;
258#ifdef SIGCONT
259	else if (sig == SIGCONT)
260	  old_signal_handler = &old_CONT;
261#endif
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