]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
libtextstyle: Use gnulib module 'term-style-control'.
authorBruno Haible <bruno@clisp.org>
Sun, 24 Mar 2019 22:40:43 +0000 (23:40 +0100)
committerBruno Haible <bruno@clisp.org>
Sun, 24 Mar 2019 22:40:43 +0000 (23:40 +0100)
* gnulib-local/lib/term-ostream.oo.h: Include term-style-control.h.
(ttyctl_t): Remove type.
* gnulib-local/lib/term-ostream.oo.c (DEBUG_SIGNALS): Remove macro.
Don't include <signal.h>, <stdio.h>, <sys/stat.h>, fatal-signal.h,
sig-handler.h, same-inode.h.
(SIZEOF): Remove macro.
(nonintr_tcgetattr, nonintr_tcsetattr): Remove functions.
(log_message, sprintf_integer, simple_errno_string, simple_signal_string,
log_signal_handler_called): Remove functions.
(struct term_ostream): Remove fields tty_control, same_as_stderr,
non_default_active. Add control_data field instead.
(get_control_data): New function.
(BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT): Remove macro.
(term_fd): Remove variable.
(pgrp_status_t): Remove type.
(pgrp_status): Remove variable.
(update_pgrp_status): Remove function.
(out_filename): Remove variable.
(out_error): Use out_stream instead of out_filename.
(restore, tcsetattr_failed): Remove functions.
(orig_lflag_set, orig_lflag): Remove variables.
(clobber_local_mode, restore_local_mode): Remove functions.
(job_control_signals): Remove variable.
(num_job_control_signals): Remove macro.
(relevant_signal_set, relevant_signal_set_initialized): Remove variables.
(init_relevant_signal_set, block_relevant_signals, unblock_relevant_signals,
is_ignored, show_signal_marker, fatal_or_stopping_signal_handler,
fatal_signal_handler, stopping_signal_handler, continuing_signal_handler,
ensure_continuing_signal_handler, ensure_other_signal_handlers): Remove
functions.
(out_attr_change): Set out_stream and out_fd.
(activate_non_default_attr, deactivate_non_default_attr): Remove functions.
(restore, async_restore, async_set_attributes_from_default): New functions.
(controller): New variable.
(activate_default_attr, output_buffer): Update.
(term_ostream::free): Invoke deactivate_term_style_controller.
(term_ostream_create): Invoke activate_term_style_controller.
* gnulib-local/m4/term-ostream.m4 (gl_TERM_OSTREAM): Don't test for tcgetattr().
* gnulib-local/modules/term-ostream (Depends-on): Add term-style-control. Remove
fatal-signal, sigaction, sigprocmask, same-inode.

gnulib-local/lib/term-ostream.oo.c
gnulib-local/lib/term-ostream.oo.h
gnulib-local/m4/term-ostream.m4
gnulib-local/modules/term-ostream
libtextstyle/.gitignore

index 364f43b6398c3b3e4e9771cef645f2b03448e353..86410bdb84c114cdbba30e14edf916eacba6aaf4 100644 (file)
 /* Specification.  */
 #include "term-ostream.h"
 
-/* Set to 1 to get debugging output regarding signals.  */
-#define DEBUG_SIGNALS 0
-
 #include <assert.h>
 #include <errno.h>
-#include <signal.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#if DEBUG_SIGNALS
-# include <stdio.h>
-#endif
-#if HAVE_TCGETATTR || HAVE_TCDRAIN
+#if HAVE_TCDRAIN
 # include <termios.h>
-# if !defined NOFLSH            /* QNX */
-#  define NOFLSH 0
-# endif
-#endif
-#if HAVE_TCGETATTR
-# include <sys/stat.h>
 #endif
 
 #include "error.h"
-#include "fatal-signal.h"
-#include "sig-handler.h"
 #include "full-write.h"
 #include "terminfo.h"
-#include "same-inode.h"
 #include "xalloc.h"
 #include "xsize.h"
 #include "gettext.h"
@@ -65,8 +49,6 @@ static char tparambuf[100];
   tparam (str, tparambuf, sizeof (tparambuf), arg1)
 #endif
 
-#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
-
 
 /* =========================== Color primitives =========================== */
 
@@ -269,7 +251,7 @@ typedef enum
 
 /* A non-default color index doesn't exist in this color model.  */
 static inline term_color_t
-rgb_to_color_monochrome ()
+rgb_to_color_monochrome (void)
 {
   return COLOR_DEFAULT;
 }
@@ -965,38 +947,10 @@ equal_attributes (attributes_t attr1, attributes_t attr2)
 
 /* ============================ EINTR handling ============================ */
 
-/* EINTR handling for tcgetattr(), tcsetattr(), tcdrain().
-   These functions can return -1/EINTR even when we don't have any
+/* EINTR handling for tcdrain().
+   This function can return -1/EINTR even when we don't have any
    signal handlers set up, namely when we get interrupted via SIGSTOP.  */
 
-#if HAVE_TCGETATTR
-
-static inline int
-nonintr_tcgetattr (int fd, struct termios *tcp)
-{
-  int retval;
-
-  do
-    retval = tcgetattr (fd, tcp);
-  while (retval < 0 && errno == EINTR);
-
-  return retval;
-}
-
-static inline int
-nonintr_tcsetattr (int fd, int flush_mode, const struct termios *tcp)
-{
-  int retval;
-
-  do
-    retval = tcsetattr (fd, flush_mode, tcp);
-  while (retval < 0 && errno == EINTR);
-
-  return retval;
-}
-
-#endif
-
 #if HAVE_TCDRAIN
 
 static inline int
@@ -1014,151 +968,6 @@ nonintr_tcdrain (int fd)
 #endif
 
 
-/* ========================== Logging primitives ========================== */
-
-/* We need logging, especially for the signal handling, because
-     - Debugging through gdb is hardly possible, because gdb produces output
-       by itself and interferes with the process states.
-     - strace is buggy when it comes to SIGTSTP handling:  By default, it
-       sends the process a SIGSTOP signal instead of SIGTSTP.  It supports
-       an option '-D -I4' to mitigate this, though.  Also, race conditions
-       appear with different probability with and without strace.
-   fprintf(stderr) is not possible within async-safe code, because fprintf()
-   may invoke malloc().  */
-
-#if DEBUG_SIGNALS
-
-/* Log a simple message.  */
-static _GL_ASYNC_SAFE void
-log_message (const char *message)
-{
-  full_write (STDERR_FILENO, message, strlen (message));
-}
-
-#else
-
-# define log_message(message)
-
-#endif
-
-#if HAVE_TCGETATTR || DEBUG_SIGNALS
-
-/* Async-safe implementation of sprintf (str, "%d", n).  */
-static _GL_ASYNC_SAFE void
-sprintf_integer (char *str, int x)
-{
-  unsigned int y;
-  char buf[20];
-  char *p;
-  size_t n;
-
-  if (x < 0)
-    {
-      *str++ = '-';
-      y = (unsigned int) (-1 - x) + 1;
-    }
-  else
-    y = x;
-
-  p = buf + sizeof (buf);
-  do
-    {
-      *--p = '0' + (y % 10);
-      y = y / 10;
-    }
-  while (y > 0);
-  n = buf + sizeof (buf) - p;
-  memcpy (str, p, n);
-  str[n] = '\0';
-}
-
-#endif
-
-#if HAVE_TCGETATTR
-
-/* Async-safe conversion of errno value to string.  */
-static _GL_ASYNC_SAFE void
-simple_errno_string (char *str, int errnum)
-{
-  switch (errnum)
-    {
-    case EBADF:  strcpy (str, "EBADF"); break;
-    case EINTR:  strcpy (str, "EINTR"); break;
-    case EINVAL: strcpy (str, "EINVAL"); break;
-    case EIO:    strcpy (str, "EIO"); break;
-    case ENOTTY: strcpy (str, "ENOTTY"); break;
-    default: sprintf_integer (str, errnum); break;
-    }
-}
-
-#endif
-
-#if DEBUG_SIGNALS
-
-/* Async-safe conversion of signal number to name.  */
-static _GL_ASYNC_SAFE void
-simple_signal_string (char *str, int sig)
-{
-  switch (sig)
-    {
-    /* Fatal signals (see fatal-signal.c).  */
-    #ifdef SIGINT
-    case SIGINT:   strcpy (str, "SIGINT"); break;
-    #endif
-    #ifdef SIGTERM
-    case SIGTERM:  strcpy (str, "SIGTERM"); break;
-    #endif
-    #ifdef SIGHUP
-    case SIGHUP:   strcpy (str, "SIGHUP"); break;
-    #endif
-    #ifdef SIGPIPE
-    case SIGPIPE:  strcpy (str, "SIGPIPE"); break;
-    #endif
-    #ifdef SIGXCPU
-    case SIGXCPU:  strcpy (str, "SIGXCPU"); break;
-    #endif
-    #ifdef SIGXFSZ
-    case SIGXFSZ:  strcpy (str, "SIGXFSZ"); break;
-    #endif
-    #ifdef SIGBREAK
-    case SIGBREAK: strcpy (str, "SIGBREAK"); break;
-    #endif
-    /* Stopping signals.  */
-    #ifdef SIGTSTP
-    case SIGTSTP:  strcpy (str, "SIGTSTP"); break;
-    #endif
-    #ifdef SIGTTIN
-    case SIGTTIN:  strcpy (str, "SIGTTIN"); break;
-    #endif
-    #ifdef SIGTTOU
-    case SIGTTOU:  strcpy (str, "SIGTTOU"); break;
-    #endif
-    /* Continuing signals.  */
-    #ifdef SIGCONT
-    case SIGCONT:  strcpy (str, "SIGCONT"); break;
-    #endif
-    default: sprintf_integer (str, sig); break;
-    }
-}
-
-/* Emit a message that a given signal handler is being run.  */
-static _GL_ASYNC_SAFE void
-log_signal_handler_called (int sig)
-{
-  char message[100];
-  strcpy (message, "Signal handler for signal ");
-  simple_signal_string (message + strlen (message), sig);
-  strcat (message, " called.\n");
-  log_message (message);
-}
-
-#else
-
-# define log_signal_handler_called(sig)
-
-#endif
-
-
 /* ============================ term_ostream_t ============================ */
 
 struct term_ostream : struct ostream
@@ -1198,20 +1007,12 @@ fields:
   const char * volatile restore_posture;
   const char * volatile restore_underline;
   /* Signal handling and tty control.  */
-  ttyctl_t volatile tty_control;
-  #if HAVE_TCGETATTR
-  bool volatile same_as_stderr;
-  #endif
+  struct term_style_control_data control_data;
   /* Variable state, representing past output.  */
   attributes_t default_attr;         /* Default simplified attributes of the
                                         terminal.  */
   attributes_t volatile active_attr; /* Simplified attributes that we have set
                                         on the terminal.  */
-  bool non_default_active;           /* True if activate_non_default_attr()
-                                        is in effect.
-                                        active_attr != default_attr implies
-                                        non_default_active == true,
-                                        but not the opposite!  */
   /* Variable state, representing future output.  */
   char *buffer;                      /* Buffer for the current line.  */
   attributes_t *attrbuffer;          /* Buffer for the simplified attributes;
@@ -1222,6 +1023,12 @@ fields:
   attributes_t simp_attr;            /* Simplified current attributes.  */
 };
 
+static struct term_style_control_data *
+get_control_data (term_ostream_t stream)
+{
+  return &stream->control_data;
+}
+
 /* Simplify attributes, according to the terminal's capabilities.  */
 static attributes_t
 simplify_attributes (term_ostream_t stream, attributes_t attr)
@@ -1251,164 +1058,6 @@ simplify_attributes (term_ostream_t stream, attributes_t attr)
   return attr;
 }
 
-/* There are several situations which can cause garbled output on the terminal's
-   screen:
-   (1) When the program calls exit() after calling flush_to_current_style,
-       the program would terminate and leave the terminal in a non-default
-       state.
-   (2) When the program is interrupted through a fatal signal, the terminal
-       would be left in a non-default state.
-   (3) When the program is stopped through a stopping signal, the terminal
-       would be left (for temporary use by other programs) in a non-default
-       state.
-   (4) When a foreground process receives a SIGINT, the kernel(!) prints '^C'.
-       On Linux, the place where this happens is
-         linux-5.0/drivers/tty/n_tty.c:713..730
-       within a call sequence
-         n_tty_receive_signal_char (n_tty.c:1245..1246)
-         -> commit_echoes (n_tty.c:792)
-         -> __process_echoes (n_tty.c:713..730).
-   (5) When a signal is sent, the output buffer is cleared.
-       On Linux, this output buffer consists of the "echo buffer" in the tty
-       and the "output buffer" in the driver.  The place where this happens is
-         linux-5.0/drivers/tty/n_tty.c:1133..1140
-       within a call
-         isig (n_tty.c:1133..1140).
-
-   How do we mitigate these problems?
-   (1) We install an exit handler that restores the terminal to the default
-       state.
-   (2) If tty_control is TTYCTL_PARTIAL or TTYCTL_FULL:
-       For some of the fatal signals (see gnulib's 'fatal-signal' module for
-       the precise list), we install a handler that attempts to restore the
-       terminal to the default state.  Since the terminal may be in the middle
-       of outputting an escape sequence at this point, the first escape
-       sequence emitted from this handler may have no effect and produce
-       garbled characters instead.  Therefore the handler outputs the cleanup
-       sequence twice.
-       For the other fatal signals, we don't do anything.
-   (3) If tty_control is TTYCTL_PARTIAL or TTYCTL_FULL:
-       For some of the stopping signals (SIGTSTP, SIGTTIN, SIGTTOU), we install
-       a handler that attempts to restore the terminal to the default state.
-       For SIGCONT, we install a handler that does the opposite: it puts the
-       terminal into the desired state again.
-       For SIGSTOP, we cannot do anything.
-   (4) If tty_control is TTYCTL_FULL:
-       The kernel's action depends on L_ECHO(tty) and L_ISIG(tty), that is, on
-       the local modes of the tty (see
-       <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
-       section 11.2.5).  We don't want to change L_ISIG; hence we change L_ECHO.
-       So, we disable the ECHO local flag of the tty; the equivalent command is
-       'stty -echo'.
-   (5) If tty_control is TTYCTL_FULL:
-       The kernel's action depends on !L_NOFLSH(tty), that is, again on the
-       local modes of the tty (see
-       <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
-       section 11.2.5).  So, we enable the NOFLSH local flag of the tty; the
-       equivalent command is 'stty noflsh'.
-       For terminals with a baud rate < 9600 this is suboptimal.  For this case
-       - where the traditional flushing behaviour makes sense - we would use a
-       technique that involves tcdrain(), TIOCOUTQ, and usleep() when it is OK
-       to disable NOFLSH.
-
-   Regarding (4) and (5), there is a complication: Changing the local modes is
-   done through tcsetattr().  However, when the process is put into the
-   background, tcsetattr() does not operate the same way as when the process is
-   running in the foreground.
-   To test this kind of behaviour, use the 'color-filter' example like this:
-     $ yes | ./filter '.*'
-     <Ctrl-Z>
-     $ bg 1
-   We have three possible implementation options:
-     * If we don't ignore the signal SIGTTOU:
-       If the TOSTOP bit in the terminal's local mode is clear (command
-       equivalent: 'stty -tostop') and the process is put into the background,
-       normal output would continue (per POSIX
-       <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
-       section 11.2.5) but tcsetattr() calls would cause it to stop due to
-       a SIGTTOU signal (per POSIX
-       <https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html>).
-       Thus, the program would behave differently with term-ostream than
-       without.
-     * If we ignore the signal SIGTTOU when the TOSTOP bit in the terminal's
-       local mode is clear (i.e. when (tc.c_lflag & TOSTOP) == 0):
-       The tcsetattr() calls do not stop the process, but they don't have the
-       desired effect.
-       On Linux, when I put the process into the background and then kill it with
-       signal SIGINT, I can see that the last operation on the terminal settings
-       (as shown by 'strace') is
-         ioctl(1, TCSETSW, {B38400 opost isig icanon echo ...}) = 0
-       and yet, once the process is terminated, the terminal settings contain
-       '-echo', not 'echo'.
-     * Don't call tcsetattr() if the process is not in the foreground.
-       This approach produces reliable results.
-
-   Blocking some signals while a non-default style is active is *not* useful:
-     - It does not help against (1), since exit() is not a signal.
-     - Signal handlers are the better approach against (2) and (3).
-     - It does not help against (4) and (5), because the kernel's actions happen
-       outside the process.  */
-#define BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT 0
-
-/* File descriptor of the currently open term_ostream_t.  */
-static int volatile term_fd = -1;
-
-#if HAVE_TCGETATTR
-
-/* Status of the process group of term_fd.  */
-typedef enum
-{
-  PGRP_UNKNOWN = 0,     /* term_fd < 0.  Unknown status.  */
-  PGRP_NO_TTY,          /* term_fd >= 0 but is not connected to a tty.  */
-  PGRP_IN_FOREGROUND,   /* term_fd >= 0 is a tty.  This process is running in
-                           the foreground.  */
-  PGRP_IN_BACKGROUND    /* term_fd >= 0 is a tty.  This process is running in
-                           the background.  */
-} pgrp_status_t;
-static pgrp_status_t volatile pgrp_status = PGRP_UNKNOWN;
-
-/* Update pgrp_status, depending on term_fd.  */
-static _GL_ASYNC_SAFE void
-update_pgrp_status (void)
-{
-  int fd = term_fd;
-  if (fd < 0)
-    {
-      pgrp_status = PGRP_UNKNOWN;
-      log_message ("pgrp_status = PGRP_UNKNOWN\n");
-    }
-  else
-    {
-      pid_t p = tcgetpgrp (fd);
-      if (p < 0)
-        {
-          pgrp_status = PGRP_NO_TTY;
-          log_message ("pgrp_status = PGRP_NO_TTY\n");
-        }
-      else
-        {
-          /* getpgrp () changes when the process gets put into the background
-             by a shell that implements job control.  */
-          if (p == getpgrp ())
-            {
-              pgrp_status = PGRP_IN_FOREGROUND;
-              log_message ("pgrp_status = PGRP_IN_FOREGROUND\n");
-            }
-          else
-            {
-              pgrp_status = PGRP_IN_BACKGROUND;
-              log_message ("pgrp_status = PGRP_IN_BACKGROUND\n");
-            }
-        }
-    }
-}
-
-#else
-
-# define update_pgrp_status()
-
-#endif
-
 /* Stream that contains information about how the various out_* functions shall
    do output.  */
 static term_ostream_t volatile out_stream;
@@ -1418,15 +1067,11 @@ static term_ostream_t volatile out_stream;
    Same as (out_stream != NULL ? out_stream->fd : -1).  */
 static int volatile out_fd = -1;
 
-/* Filename of out_fd.
-   Same as (out_stream != NULL ? out_stream->filename : NULL).  */
-static const char * volatile out_filename;
-
 /* Signal error after full_write failed.  */
 static void
-out_error ()
+out_error (void)
 {
-  error (EXIT_FAILURE, errno, _("error writing to %s"), out_filename);
+  error (EXIT_FAILURE, errno, _("error writing to %s"), out_stream->filename);
 }
 
 /* Output a single char to out_fd.  */
@@ -1686,448 +1331,6 @@ out_underline_change (term_ostream_t stream, term_underline_t new_underline,
          1, async_safe ? out_char_unchecked : out_char);
 }
 
-/* The exit handler.  */
-static void
-restore (void)
-{
-  /* Only do something while some output was started but not completed.  */
-  if (out_stream != NULL)
-    {
-      if (out_stream->restore_colors != NULL)
-        tputs (out_stream->restore_colors, 1, out_char_unchecked);
-      if (out_stream->restore_weight != NULL)
-        tputs (out_stream->restore_weight, 1, out_char_unchecked);
-      if (out_stream->restore_posture != NULL)
-        tputs (out_stream->restore_posture, 1, out_char_unchecked);
-      if (out_stream->restore_underline != NULL)
-        tputs (out_stream->restore_underline, 1, out_char_unchecked);
-    }
-}
-
-#if HAVE_TCGETATTR
-
-/* Return a failure message after tcsetattr() failed.  */
-static _GL_ASYNC_SAFE void
-tcsetattr_failed (char message[100], const char *caller)
-{
-  int errnum = errno;
-  strcpy (message, caller);
-  strcat (message, ": tcsetattr(fd=");
-  sprintf_integer (message + strlen (message), out_fd);
-  strcat (message, ") failed, errno=");
-  simple_errno_string (message + strlen (message), errnum);
-  strcat (message, "\n");
-}
-
-/* True when orig_lflag represents the original tc.c_lflag.  */
-static bool volatile orig_lflag_set;
-static tcflag_t volatile orig_lflag;
-
-/* Modifies the tty's local mode, preparing for non-default terminal state.
-   Used only when the out_stream's tty_control is TTYCTL_FULL.  */
-static _GL_ASYNC_SAFE void
-clobber_local_mode (void)
-{
-  /* Here, out_fd == term_fd.  */
-  struct termios tc;
-  if (pgrp_status == PGRP_IN_FOREGROUND
-      && nonintr_tcgetattr (out_fd, &tc) >= 0)
-    {
-      if (!orig_lflag_set)
-        orig_lflag = tc.c_lflag;
-      /* Set orig_lflag_set to true before actually modifying the tty's local
-         mode, because restore_local_mode does nothing if orig_lflag_set is
-         false.  */
-      orig_lflag_set = true;
-      tc.c_lflag &= ~ECHO;
-      tc.c_lflag |= NOFLSH;
-      if (nonintr_tcsetattr (out_fd, TCSANOW, &tc) < 0)
-        {
-          /* Since tcsetattr failed, restore_local_mode does not need to restore
-             anything.  Set orig_lflag_set to false to indicate this.  */
-          orig_lflag_set = false;
-          {
-            char message[100];
-            tcsetattr_failed (message, "term_ostream:"":clobber_local_mode");
-            full_write (STDERR_FILENO, message, strlen (message));
-          }
-        }
-    }
-}
-
-/* Modifies the tty's local mode, once the terminal is back to the default state.
-   Returns true if ECHO was turned off.
-   Used only when the out_stream's tty_control is TTYCTL_FULL.  */
-static _GL_ASYNC_SAFE bool
-restore_local_mode (void)
-{
-  /* Here, out_fd == term_fd.  */
-  bool echo_was_off = false;
-  /* Nothing to do if !orig_lflag_set.  */
-  if (orig_lflag_set)
-    {
-      struct termios tc;
-      if (nonintr_tcgetattr (out_fd, &tc) >= 0)
-        {
-          echo_was_off = (tc.c_lflag & ECHO) == 0;
-          tc.c_lflag = orig_lflag;
-          if (nonintr_tcsetattr (out_fd, TCSADRAIN, &tc) < 0)
-            {
-              char message[100];
-              tcsetattr_failed (message, "term_ostream:"":restore_local_mode");
-              full_write (STDERR_FILENO, message, strlen (message));
-            }
-        }
-      orig_lflag_set = false;
-    }
-  return echo_was_off;
-}
-
-#endif
-
-#if defined SIGCONT
-
-/* The list of signals whose default behaviour is to stop or continue the
-   program.  */
-static int const job_control_signals[] =
-  {
-    #ifdef SIGTSTP
-    SIGTSTP,
-    #endif
-    #ifdef SIGTTIN
-    SIGTTIN,
-    #endif
-    #ifdef SIGTTOU
-    SIGTTOU,
-    #endif
-    #ifdef SIGCONT
-    SIGCONT,
-    #endif
-    0
-  };
-
-# define num_job_control_signals (SIZEOF (job_control_signals) - 1)
-
-#endif
-
-/* The following signals are relevant because they do tputs invocations:
-     - fatal signals,
-     - stopping signals,
-     - continuing signals (SIGCONT).  */
-
-static sigset_t relevant_signal_set;
-static bool relevant_signal_set_initialized = false;
-
-static void
-init_relevant_signal_set ()
-{
-  if (!relevant_signal_set_initialized)
-    {
-      int fatal_signals[64];
-      size_t num_fatal_signals;
-      size_t i;
-
-      num_fatal_signals = get_fatal_signals (fatal_signals);
-
-      sigemptyset (&relevant_signal_set);
-      for (i = 0; i < num_fatal_signals; i++)
-        sigaddset (&relevant_signal_set, fatal_signals[i]);
-      #if defined SIGCONT
-      for (i = 0; i < num_job_control_signals; i++)
-        sigaddset (&relevant_signal_set, job_control_signals[i]);
-      #endif
-
-      relevant_signal_set_initialized = true;
-    }
-}
-
-/* Temporarily delay the relevant signals.  */
-static _GL_ASYNC_SAFE inline void
-block_relevant_signals ()
-{
-  /* The caller must ensure that init_relevant_signal_set () was already
-     called.  */
-  if (!relevant_signal_set_initialized)
-    abort ();
-
-  sigprocmask (SIG_BLOCK, &relevant_signal_set, NULL);
-}
-
-/* Stop delaying the relevant signals.  */
-static _GL_ASYNC_SAFE inline void
-unblock_relevant_signals ()
-{
-  sigprocmask (SIG_UNBLOCK, &relevant_signal_set, NULL);
-}
-
-/* Determines whether a signal is ignored.  */
-static _GL_ASYNC_SAFE bool
-is_ignored (int sig)
-{
-  struct sigaction action;
-
-  return (sigaction (sig, NULL, &action) >= 0
-          && get_handler (&action) == SIG_IGN);
-}
-
-#if HAVE_TCGETATTR
-
-/* Write the same signal marker that the kernel would have printed if ECHO had
-   been turned on.  See (4) above.
-   This is a makeshift and is not perfect:
-     - When stderr refers to a different target than out_stream->fd, it is too
-       hairy to write the signal marker.
-     - In some cases, when the signal was generated right before and delivered
-       right after a clobber_local_mode invocation, the result is that the
-       marker appears twice, e.g. ^C^C.  This occurs only with a small
-       probability.
-     - In some cases, when the signal was generated right before and delivered
-       right after a restore_local_mode invocation, the result is that the
-       marker does not appear at all.  This occurs only with a small
-       probability.
-   To test this kind of behaviour, use the 'color-filter' example like this:
-     $ yes | ./filter '.*'
- */
-static _GL_ASYNC_SAFE void
-show_signal_marker (int sig)
-{
-  /* Write to stderr, not to out_stream->fd, because out_stream->fd is often
-     logged or used with 'less -R'.  */
-  if (out_stream != NULL && out_stream->same_as_stderr)
-    switch (sig)
-      {
-      /* The kernel's action when the user presses the INTR key.  */
-      case SIGINT:
-        full_write (STDERR_FILENO, "^C", 2); break;
-      /* The kernel's action when the user presses the SUSP key.  */
-      case SIGTSTP:
-        full_write (STDERR_FILENO, "^Z", 2); break;
-      /* The kernel's action when the user presses the QUIT key.  */
-      case SIGQUIT:
-        full_write (STDERR_FILENO, "^\\", 2); break;
-      default: break;
-      }
-}
-
-#endif
-
-/* The main code of the signal handler for fatal signals and stopping signals.
-   It is reentrant.  */
-static _GL_ASYNC_SAFE void
-fatal_or_stopping_signal_handler (int sig)
-{
-  bool echo_was_off = false;
-  /* Only do something while some output was interrupted.  */
-  if (out_stream != NULL && out_stream->tty_control != TTYCTL_NONE)
-    {
-      unsigned int i;
-
-      /* Block the relevant signals.  This is needed, because the tputs
-         invocations below are not reentrant.  */
-      block_relevant_signals ();
-
-      /* Restore the terminal to the default state.  */
-      for (i = 0; i < 2; i++)
-        {
-          if (out_stream->restore_colors != NULL)
-            tputs (out_stream->restore_colors, 1, out_char_unchecked);
-          if (out_stream->restore_weight != NULL)
-            tputs (out_stream->restore_weight, 1, out_char_unchecked);
-          if (out_stream->restore_posture != NULL)
-            tputs (out_stream->restore_posture, 1, out_char_unchecked);
-          if (out_stream->restore_underline != NULL)
-            tputs (out_stream->restore_underline, 1, out_char_unchecked);
-        }
-      #if HAVE_TCGETATTR
-      if (out_stream->tty_control == TTYCTL_FULL)
-        {
-          /* Restore the local mode, once the tputs calls above have reached
-             their destination.  */
-          echo_was_off = restore_local_mode ();
-        }
-      #endif
-
-      /* Unblock the relevant signals.  */
-      unblock_relevant_signals ();
-    }
-
-  #if HAVE_TCGETATTR
-  if (echo_was_off)
-    show_signal_marker (sig);
-  #endif
-}
-
-/* The signal handler for fatal signals.
-   It is reentrant.  */
-static _GL_ASYNC_SAFE void
-fatal_signal_handler (int sig)
-{
-  log_signal_handler_called (sig);
-  fatal_or_stopping_signal_handler (sig);
-}
-
-#if defined SIGCONT
-
-/* The signal handler for stopping signals.
-   It is reentrant.  */
-static _GL_ASYNC_SAFE void
-stopping_signal_handler (int sig)
-{
-  log_signal_handler_called (sig);
-  fatal_or_stopping_signal_handler (sig);
-
-  /* Now execute the signal's default action.
-     We reinstall the handler later, during the SIGCONT handler.  */
-  {
-    struct sigaction action;
-    action.sa_handler = SIG_DFL;
-    action.sa_flags = SA_NODEFER;
-    sigemptyset (&action.sa_mask);
-    sigaction (sig, &action, NULL);
-  }
-  raise (sig);
-}
-
-/* The signal handler for SIGCONT.
-   It is reentrant.  */
-static _GL_ASYNC_SAFE void
-continuing_signal_handler (int sig)
-{
-  log_signal_handler_called (sig);
-  update_pgrp_status ();
-  /* Only do something while some output was interrupted.  */
-  if (out_stream != NULL && out_stream->tty_control != TTYCTL_NONE)
-    {
-      /* Reinstall the signals handlers removed in stopping_signal_handler.  */
-      {
-        unsigned int i;
-
-        for (i = 0; i < num_job_control_signals; i++)
-          {
-            int sig = job_control_signals[i];
-
-            if (sig != SIGCONT && !is_ignored (sig))
-              {
-                struct sigaction action;
-                action.sa_handler = &stopping_signal_handler;
-                /* If we get a stopping or continuing signal while executing
-                   stopping_signal_handler or continuing_signal_handler, enter
-                   it recursively, since it is reentrant.
-                   Hence no SA_RESETHAND.  */
-                action.sa_flags = SA_NODEFER;
-                sigemptyset (&action.sa_mask);
-                sigaction (sig, &action, NULL);
-              }
-          }
-      }
-
-      /* Block the relevant signals.  This is needed, because the tputs
-         invocations done inside the out_* calls below are not reentrant.  */
-      block_relevant_signals ();
-
-      #if HAVE_TCGETATTR
-      if (out_stream->tty_control == TTYCTL_FULL)
-        {
-          /* Modify the local mode.  */
-          clobber_local_mode ();
-        }
-      #endif
-      /* Set the terminal attributes.  */
-      attributes_t new_attr = out_stream->active_attr;
-      if (new_attr.color != COLOR_DEFAULT)
-        out_color_change (out_stream, new_attr.color, true);
-      if (new_attr.bgcolor != COLOR_DEFAULT)
-        out_bgcolor_change (out_stream, new_attr.bgcolor, true);
-      if (new_attr.weight != WEIGHT_DEFAULT)
-        out_weight_change (out_stream, new_attr.weight, true);
-      if (new_attr.posture != POSTURE_DEFAULT)
-        out_posture_change (out_stream, new_attr.posture, true);
-      if (new_attr.underline != UNDERLINE_DEFAULT)
-        out_underline_change (out_stream, new_attr.underline, true);
-
-      /* Unblock the relevant signals.  */
-      unblock_relevant_signals ();
-    }
-}
-
-/* Ensure the signal handlers are installed.
-   Once they are installed, we leave them installed.  It's not worth
-   installing and uninstalling them each time we switch the terminal to a
-   non-default state and back; instead we set out_stream to tell the signal
-   handler whether it has something to do or not.  */
-
-static void
-ensure_continuing_signal_handler (void)
-{
-  static bool signal_handler_installed = false;
-
-  if (!signal_handler_installed)
-    {
-      int sig = SIGCONT;
-      struct sigaction action;
-      action.sa_handler = &continuing_signal_handler;
-      /* If we get a stopping or continuing signal while executing
-         continuing_signal_handler, enter it recursively, since it is
-         reentrant.  Hence no SA_RESETHAND.  */
-      action.sa_flags = SA_NODEFER;
-      sigemptyset (&action.sa_mask);
-      sigaction (sig, &action, NULL);
-
-      signal_handler_installed = true;
-    }
-}
-
-#endif
-
-static void
-ensure_other_signal_handlers (void)
-{
-  static bool signal_handlers_installed = false;
-
-  if (!signal_handlers_installed)
-    {
-      unsigned int i;
-
-      /* Install the handlers for the fatal signals.  */
-      at_fatal_signal (fatal_signal_handler);
-
-      #if defined SIGCONT
-
-      /* Install the handlers for the stopping and continuing signals.  */
-      for (i = 0; i < num_job_control_signals; i++)
-        {
-          int sig = job_control_signals[i];
-
-          if (sig == SIGCONT)
-            /* Already handled in ensure_continuing_signal_handler.  */
-            ;
-          else if (!is_ignored (sig))
-            {
-              struct sigaction action;
-              action.sa_handler = &stopping_signal_handler;
-              /* If we get a stopping or continuing signal while executing
-                 stopping_signal_handler, enter it recursively, since it is
-                 reentrant.  Hence no SA_RESETHAND.  */
-              action.sa_flags = SA_NODEFER;
-              sigemptyset (&action.sa_mask);
-              sigaction (sig, &action, NULL);
-            }
-          #if DEBUG_SIGNALS
-          else
-            {
-              fprintf (stderr, "Signal %d is ignored. Not installing a handler!\n",
-                       sig);
-              fflush (stderr);
-            }
-          #endif
-        }
-
-      #endif
-
-      signal_handlers_installed = true;
-    }
-}
-
 /* Output escape sequences to switch from STREAM->ACTIVE_ATTR to NEW_ATTR,
    and update STREAM->ACTIVE_ATTR.  */
 static void
@@ -2136,6 +1339,10 @@ out_attr_change (term_ostream_t stream, attributes_t new_attr)
   attributes_t old_attr = stream->active_attr;
   bool cleared_attributes;
 
+  /* For out_char to work.  */
+  out_stream = stream;
+  out_fd = stream->fd;
+
   /* We don't know the default colors of the terminal.  The only way to switch
      back to a default color is to use stream->orig_pair.  */
   if ((new_attr.color == COLOR_DEFAULT && old_attr.color != COLOR_DEFAULT)
@@ -2236,85 +1443,69 @@ out_attr_change (term_ostream_t stream, attributes_t new_attr)
   stream->active_attr = new_attr;
 }
 
-/* Prepare for activating some non-default attributes.
-   Note: This may block some signals.  */
 static void
-activate_non_default_attr (term_ostream_t stream)
+restore (term_ostream_t stream)
 {
-  if (!stream->non_default_active)
-    {
-      if (stream->tty_control != TTYCTL_NONE)
-        ensure_other_signal_handlers ();
-
-      #if BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT
-      /* Block fatal signals, so that a SIGINT or similar doesn't interrupt
-         us without the possibility of restoring the terminal's state.
-         Likewise for SIGTSTP etc.  */
-      block_relevant_signals ();
-      #endif
+  /* For out_char_unchecked to work.  */
+  out_stream = stream;
+  out_fd = stream->fd;
 
-      /* Enable the exit handler for restoring the terminal's state,
-         and make the signal handlers effective.  */
-      if (out_stream != NULL)
-        {
-          /* We can't support two term_ostream_t active with non-default
-             attributes at the same time.  */
-          abort ();
-        }
-      /* The uses of 'volatile' (and ISO C 99 section 5.1.2.3.(5)) ensure that
-         we set out_stream to a non-NULL value only after the memory locations
-         out_fd and out_filename have been filled.  */
-      out_fd = stream->fd;
-      out_filename = stream->filename;
-      out_stream = stream;
-
-      #if HAVE_TCGETATTR
-      /* Now that the signal handlers are effective, modify the tty.  */
-      if (stream->tty_control == TTYCTL_FULL)
-        {
-          /* Modify the local mode.  */
-          clobber_local_mode ();
-        }
-      #endif
+  if (stream->restore_colors != NULL)
+    tputs (stream->restore_colors, 1, out_char_unchecked);
+  if (stream->restore_weight != NULL)
+    tputs (stream->restore_weight, 1, out_char_unchecked);
+  if (stream->restore_posture != NULL)
+    tputs (stream->restore_posture, 1, out_char_unchecked);
+  if (stream->restore_underline != NULL)
+    tputs (stream->restore_underline, 1, out_char_unchecked);
+}
 
-      stream->non_default_active = true;
-    }
+static _GL_ASYNC_SAFE void
+async_restore (term_ostream_t stream)
+{
+  /* For out_char_unchecked to work.  */
+  out_stream = stream;
+  out_fd = stream->fd;
+
+  if (stream->restore_colors != NULL)
+    tputs (stream->restore_colors, 1, out_char_unchecked);
+  if (stream->restore_weight != NULL)
+    tputs (stream->restore_weight, 1, out_char_unchecked);
+  if (stream->restore_posture != NULL)
+    tputs (stream->restore_posture, 1, out_char_unchecked);
+  if (stream->restore_underline != NULL)
+    tputs (stream->restore_underline, 1, out_char_unchecked);
 }
 
-/* Deactivate the non-default attributes mode.
-   Note: This may unblock some signals.  */
-static void
-deactivate_non_default_attr (term_ostream_t stream)
+static _GL_ASYNC_SAFE void
+async_set_attributes_from_default (term_ostream_t stream)
 {
-  if (stream->non_default_active)
-    {
-      #if HAVE_TCGETATTR
-      /* Before we make the signal handlers ineffective, modify the tty.  */
-      if (out_stream->tty_control == TTYCTL_FULL)
-        {
-          /* Restore the local mode, once the tputs calls from out_attr_change
-             have reached their destination.  */
-          restore_local_mode ();
-        }
-      #endif
+  attributes_t new_attr = stream->active_attr;
 
-      /* Disable the exit handler, and make the signal handlers ineffective.  */
-      /* The uses of 'volatile' (and ISO C 99 section 5.1.2.3.(5)) ensure that
-         we reset out_fd and out_filename only after the memory location
-         out_stream has been cleared.  */
-      out_stream = NULL;
-      out_fd = -1;
-      out_filename = NULL;
-
-      #if BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT
-      /* Unblock the relevant signals.  */
-      unblock_relevant_signals ();
-      #endif
+  /* For out_char_unchecked to work.  */
+  out_stream = stream;
+  out_fd = stream->fd;
 
-      stream->non_default_active = false;
-    }
+  if (new_attr.color != COLOR_DEFAULT)
+    out_color_change (stream, new_attr.color, true);
+  if (new_attr.bgcolor != COLOR_DEFAULT)
+    out_bgcolor_change (stream, new_attr.bgcolor, true);
+  if (new_attr.weight != WEIGHT_DEFAULT)
+    out_weight_change (stream, new_attr.weight, true);
+  if (new_attr.posture != POSTURE_DEFAULT)
+    out_posture_change (stream, new_attr.posture, true);
+  if (new_attr.underline != UNDERLINE_DEFAULT)
+    out_underline_change (stream, new_attr.underline, true);
 }
 
+static const struct term_style_controller controller =
+{
+  get_control_data,
+  restore,
+  async_restore,
+  async_set_attributes_from_default
+};
+
 /* Activate the default attributes.  */
 static void
 activate_default_attr (term_ostream_t stream)
@@ -2322,7 +1513,7 @@ activate_default_attr (term_ostream_t stream)
   /* Switch back to the default attributes.  */
   out_attr_change (stream, stream->default_attr);
 
-  deactivate_non_default_attr (stream);
+  deactivate_term_non_default_mode (&controller, stream);
 }
 
 /* Output the buffered line atomically.
@@ -2363,7 +1554,7 @@ output_buffer (term_ostream_t stream, attributes_t goal_attr)
   if (len > 0)
     {
       if (!equal_attributes (*ap, stream->default_attr))
-        activate_non_default_attr (stream);
+        activate_term_non_default_mode (&controller, stream);
 
       do
         {
@@ -2395,13 +1586,13 @@ output_buffer (term_ostream_t stream, attributes_t goal_attr)
   /* Before changing to goal_attr, we may need to enable the non-default
      attributes mode.  */
   if (!equal_attributes (goal_attr, stream->default_attr))
-    activate_non_default_attr (stream);
+    activate_term_non_default_mode (&controller, stream);
   /* Change to goal_attr.  */
   if (!equal_attributes (goal_attr, stream->active_attr))
     out_attr_change (stream, goal_attr);
   /* When we can deactivate the non-default attributes mode, do so.  */
   if (equal_attributes (goal_attr, stream->default_attr))
-    deactivate_non_default_attr (stream);
+    deactivate_term_non_default_mode (&controller, stream);
 }
 
 /* Implementation of ostream_t methods.  */
@@ -2498,11 +1689,9 @@ static void
 term_ostream::free (term_ostream_t stream)
 {
   term_ostream_flush (stream, FLUSH_THIS_STREAM);
-  /* Verify that the non-default attributes mode is turned off.  */
-  if (stream->non_default_active)
-    abort ();
-  term_fd = -1;
-  update_pgrp_status ();
+
+  deactivate_term_style_controller (&controller, stream);
+
   free (stream->filename);
   if (stream->set_a_foreground != NULL)
     free (stream->set_a_foreground);
@@ -2823,37 +2012,6 @@ term_ostream_create (int fd, const char *filename, ttyctl_t tty_control)
         : stream->exit_attribute_mode)
      : NULL);
 
-  /* Prepare tty control.  */
-  if (tty_control == TTYCTL_AUTO)
-    tty_control = TTYCTL_FULL;
-  stream->tty_control = tty_control;
-  if (stream->tty_control != TTYCTL_NONE)
-    init_relevant_signal_set ();
-  #if HAVE_TCGETATTR
-  if (stream->tty_control == TTYCTL_FULL)
-    {
-      struct stat statbuf1;
-      struct stat statbuf2;
-      if (fd == STDERR_FILENO
-          || (fstat (fd, &statbuf1) >= 0
-              && fstat (STDERR_FILENO, &statbuf2) >= 0
-              && SAME_INODE (statbuf1, statbuf2)))
-        stream->same_as_stderr = true;
-      else
-        stream->same_as_stderr = false;
-    }
-  else
-    /* This value is actually not used.  */
-    stream->same_as_stderr = false;
-  #endif
-
-  /* Start keeping track of the process group status.  */
-  term_fd = fd;
-  #if defined SIGCONT
-  ensure_continuing_signal_handler ();
-  #endif
-  update_pgrp_status ();
-
   /* Initialize the buffer.  */
   stream->allocated = 120;
   stream->buffer = XNMALLOC (stream->allocated, char);
@@ -2875,20 +2033,12 @@ term_ostream_create (int fd, const char *filename, ttyctl_t tty_control)
 
     stream->default_attr = simplified_default;
     stream->active_attr = simplified_default;
-    stream->non_default_active = false;
     stream->curr_attr = assumed_default;
     stream->simp_attr = simplified_default;
   }
 
-  /* Register an exit handler.  */
-  {
-    static bool registered = false;
-    if (!registered)
-      {
-        atexit (restore);
-        registered = true;
-      }
-  }
+  /* Prepare tty control.  */
+  activate_term_style_controller (&controller, stream, fd, tty_control);
 
   return stream;
 }
index 5e2fd7de86af20df6ae08122a091454d8baf80cb..51d0e0c45a51f85399b51784c46db77ba8ceadf4 100644 (file)
@@ -97,25 +97,9 @@ methods:
   void flush_to_current_style (term_ostream_t stream);
 };
 
-/* The amount of control to take over the underlying tty in order to avoid
-   garbled output on the screen, due to interleaved output of escape sequences
-   and output from the kernel (such as when the kernel echoes user's input
-   or when the kernel prints '^C' after the user pressed Ctrl-C).  */
-typedef enum
-{
-  TTYCTL_AUTO = 0,  /* Automatic best-possible choice.  */
-  TTYCTL_NONE,      /* No control.
-                       Result: Garbled output can occur, and the terminal can
-                       be left in any state when the program is interrupted.  */
-  TTYCTL_PARTIAL,   /* Signal handling.
-                       Result: Garbled output can occur, but the terminal will
-                       be left in the default state when the program is
-                       interrupted.  */
-  TTYCTL_FULL       /* Signal handling and disabling echo and flush-upon-signal.
-                       Result: No garbled output, and the the terminal will
-                       be left in the default state when the program is
-                       interrupted.  */
-} ttyctl_t;
+/* Get ttyctl_t.  */
+#define term_style_user_data term_ostream_representation
+#include "term-style-control.h"
 
 
 #ifdef __cplusplus
index c5446e98598befc8d09742195903932e6bb08779..60461970a82cee1c91cf8e1163bd1b556ae30d8a 100644 (file)
@@ -1,4 +1,4 @@
-# term-ostream.m4 serial 3
+# term-ostream.m4 serial 4
 dnl Copyright (C) 2006, 2019 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -7,5 +7,5 @@ dnl with or without modifications, as long as this notice is preserved.
 AC_DEFUN([gl_TERM_OSTREAM],
 [
   AC_REQUIRE([AC_C_INLINE])
-  AC_CHECK_FUNCS_ONCE([tcgetattr tcdrain])
+  AC_CHECK_FUNCS_ONCE([tcdrain])
 ])
index 77e893f7dc821acf0af6d194b323bfd56aea1670..2768bb2f8c7f1c7d51cddca36e2c7f42a2039038 100644 (file)
@@ -8,14 +8,11 @@ m4/term-ostream.m4
 
 Depends-on:
 ostream
+term-style-control
 error
 stdlib
-fatal-signal
-sigaction
-sigprocmask
 full-write
 fsync
-same-inode
 gettext-h
 terminfo-h
 xalloc
index f4196f42fba21c8c8b2c316a6695d83be2002b6b..12e4908bee687fce31531620d4cc8a45cea0f16d 100644 (file)
 /lib/sys_types.in.h
 /lib/term-ostream.oo.c
 /lib/term-ostream.oo.h
+/lib/term-style-control.c
+/lib/term-style-control.h
 /lib/term-styled-ostream.oo.c
 /lib/term-styled-ostream.oo.h
 /lib/terminfo.h