+ * An empty signal handler, currently used for SIGPIPE
+ * -------------------------------------------------------------------------- */
+static void
+empty_handler (int sig STG_UNUSED)
+{
+ // nothing
+}
+
+/* -----------------------------------------------------------------------------
+ SIGTSTP handling
+
+ When a process is suspeended with ^Z and resumed again, the shell
+ makes no attempt to save and restore the terminal settings. So on
+ resume, any terminal setting modificaions we made (e.g. turning off
+ ICANON due to hSetBuffering NoBuffering) may well be lost. Hence,
+ we arrange to save and restore the terminal settings ourselves.
+
+ The trick we use is:
+ - catch SIGTSTP
+ - in the handler, kill(getpid(),SIGTSTP)
+ - when this returns, restore the TTY settings
+ This means we don't have to catch SIGCONT too.
+
+ -------------------------------------------------------------------------- */
+
+static void sigtstp_handler(int sig);
+static void set_sigtstp_action (rtsBool handle);
+
+static void
+sigtstp_handler (int sig)
+{
+ int fd;
+ struct termios ts[3];
+
+ // save the current TTY state for TTYs we modified
+ for (fd = 0; fd <= 2; fd++) {
+ if (__hscore_get_saved_termios(fd) != NULL) {
+ tcgetattr(fd,&ts[fd]);
+ }
+ }
+
+ // de-install the SIGTSTP handler
+ set_sigtstp_action(rtsFalse);
+
+ // really stop the process now
+ {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ kill(getpid(), sig);
+ }
+
+ // on return, restore the TTY state
+ for (fd = 0; fd <= 2; fd++) {
+ if (__hscore_get_saved_termios(fd) != NULL) {
+ tcsetattr(0,TCSANOW,&ts[fd]);
+ }
+ }
+
+ set_sigtstp_action(rtsTrue);
+}
+
+static void
+set_sigtstp_action (rtsBool handle)
+{
+ struct sigaction sa;
+ if (handle) {
+ sa.sa_handler = sigtstp_handler;
+ } else {
+ sa.sa_handler = SIG_DFL;
+ }
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGTSTP, &sa, NULL);
+}
+
+/* -----------------------------------------------------------------------------