Patch registers SIGTSTP hook where it sends reset terminal color
control sequence using write and fsync.
Handler is installed only if signal is not being ignored.
Patch uses rl_free_line_state and rl_cleanup_after_signal to unhook
readline from terminal, then it calls original TSTP handler using
approach in URL below and again hooks readline using
rl_reset_after_signal.
Handling is based on code from:
http://man7.org/tlpi/code/online/dist/pgsjc/handling_SIGTSTP.c.html
This approach seems to mostly work. User is sometimes able to get
readline into state when it doesn't correctly respond to newline.
^Z and subsequent fg helps there as reset command helps broken
terminal.
---
fish/fish.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
diff --git a/fish/fish.c b/fish/fish.c
index d26f8b3..ec855f4 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -73,6 +73,7 @@ static void cleanup_readline (void);
static char *decode_ps1 (const char *);
static void add_history_line (const char *);
#endif
+static void tstp_handler (int sig);
static int override_progress_bars = -1;
static struct progress_bar *bar = NULL;
@@ -159,6 +160,65 @@ usage (int status)
exit (status);
}
+/* Handler for SIGTSTP */
+static void
+tstp_handler (int sig)
+{
+ /* Source:
http://man7.org/tlpi/code/online/dist/pgsjc/handling_SIGTSTP.c.html */
+ sigset_t tstp_mask, prev_mask;
+ int saved_errno;
+ struct sigaction sa;
+
+ /* In case we change 'errno' here */
+ saved_errno = errno;
+
+#ifdef HAVE_LIBREADLINE
+ /* Cleanup readline, unhook from terminal */
+ rl_free_line_state ();
+ rl_cleanup_after_signal ();
+#endif
+
+ /* Reset terminal color */
+#define RESETCOLOR "\033[0m"
+ if (write (1, RESETCOLOR, sizeof RESETCOLOR) != sizeof RESETCOLOR)
+ perror ("write");
+ fsync (1);
+
+ /* Set handling to default */
+ if (signal (SIGTSTP, SIG_DFL) == SIG_ERR)
+ perror ("signal");
+
+ /* Generate a further SIGTSTP */
+ raise (SIGTSTP);
+
+ /* Unblock SIGTSTP; the pending SIGTSTP immediately suspends the program */
+
+ sigemptyset (&tstp_mask);
+ sigaddset (&tstp_mask, SIGTSTP);
+ if (sigprocmask (SIG_UNBLOCK, &tstp_mask, &prev_mask) == -1)
+ perror ("sigprocmask");
+
+ /* Execution resumes here after SIGCONT */
+
+ /* Reblock SIGTSTP */
+ if (sigprocmask (SIG_SETMASK, &prev_mask, NULL) == -1)
+ perror ("sigprocmask");
+
+ /* Reestablish handler */
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = tstp_handler;
+ if (sigaction (SIGTSTP, &sa, NULL) == -1)
+ perror ("sigaction");
+
+#ifdef HAVE_LIBREADLINE
+ /* Restore readline state */
+ rl_reset_after_signal ();
+#endif
+
+ errno = saved_errno;
+}
+
int
main (int argc, char *argv[])
{
@@ -439,6 +499,22 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
+ /* Register ^Z handler. We need it to reset terminal colors
+ */
+ if (is_interactive) {
+ /* Only establish handler for SIGTSTP if it is not being ignored */
+ if (sigaction(SIGTSTP, NULL, &sa) == -1)
+ perror ("sigaction");
+
+ if (sa.sa_handler != SIG_IGN) {
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = tstp_handler;
+ if (sigaction(SIGTSTP, &sa, NULL) == -1)
+ perror ("sigaction");
+ }
+ }
+
/* Old-style -i syntax? Since -a/-d/-N and -i was disallowed
* previously, if we have -i without any drives but with something
* on the command line, it must be old-style syntax.
--
2.5.0