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