/*
 * Copyright (c) 2002, 2003, 2004, 2012 by Martin C. Shepherd
 * 
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, and/or sell copies of the Software, and to permit persons
 * to whom the Software is furnished to do so, provided that the above
 * copyright notice(s) and this permission notice appear in all copies of
 * the Software and that both the above copyright notice(s) and this
 * permission notice appear in supporting documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * Except as contained in this notice, the name of a copyright holder
 * shall not be used in advertising or otherwise to promote the sale, use
 * or other dealings in this Software without prior written authorization
 * of the copyright holder.
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
#include <setjmp.h>

#ifdef HAVE_SELECT
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#endif

#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>    
#include <sys/types.h>
#include <signal.h>

#include "libtecla.h"

/*
 * The SignalActions object provides a way to temporarily install
 * a signal handler to a given set of signals, and later restore all
 * of the signal handlers that this displaced.
 */
typedef struct {
  int nsignal;               /* The number of signals on the host OS */
  sigset_t mask;             /* The set of signals who's signal handlers */
                             /*  are stored in the following actions[] */
                             /*  array. */
  struct sigaction *actions; /* An array of nsignal actions */
} SignalActions;

static SignalActions *new_SignalActions(void);
static SignalActions *del_SignalActions(SignalActions *si);
static int displace_signal_handlers(SignalActions *si, sigset_t *mask,
				    void (*handler)(int));
static int reinstate_signal_handlers(SignalActions *si);

/* Return resources, restore the terminal to a usable state and exit */

static void cleanup_and_exit(GetLine *gl, SignalActions *si, int status);

/* The function which displays the introductory text of the demo */

static void show_demo_introduction(GetLine *gl);

/* A signal-aware version of select() */

static int demo_sigselect(int n, fd_set *readfds, fd_set *writefds,
			  fd_set *exceptfds, struct timeval *timeout,
			  sigset_t *mask, SignalActions *si);

/*
 * The following variables are accessed from signal handlers.  Note
 * that these variables don't need to be either volatile or
 * sig_atomic_t because:
 *
 * 1. Outside of signal handlers we only access them when signal
 *    delivery is blocked, so we know that no signal handlers can
 *    be accessing them at that time.
 *
 * 2. When the signal handlers that set these variables are installed,
 *    the sa_mask member of the sigaction structure is used to ensure
 *    that only one instance of these signal handlers can be running
 *    at a time, so we also know that there can't be simultaneous
 *    accesses to them by multiple signal handlers.
 */
static GetLine *demo_gl;                /* The line editor object */
static sigjmp_buf demo_setjmp_buffer;   /* The sigsetjmp() buffer */
static int demo_setjmp_signo = -1;      /* The signal that was caught */

/* Signal handlers */

static void demo_signal_handler(int signo);
static void demo_setjmp_handler(int signo);

/*
 * Set the amount of time that gl_get_line() should wait for I/O before
 * returning to let the external event loop continue.
 */
#define DEMO_IO_TIMEOUT 100000000 /* ns => 100ms */

/* The timeout handler */

static GL_TIMEOUT_FN(demo_timeout_fn);

/*.......................................................................
 * This program demonstrates the use of gl_get_line() from an external
 * event loop. It takes no arguments.
 */
int main(int argc, char *argv[])
{
  int major,minor,micro;      /* The version number of the library */
  GetLine *gl=NULL;           /* The resource object of gl_get_line() */
  SignalActions *si=NULL;     /* Temporary storage of displaced signal */
                              /*  handlers. */
  sigset_t all_signal_mask;   /* The set of signals known by gl_get_line() */
/*
 * This program requires select().
 */
#if !defined(HAVE_SELECT)
  fprintf(stderr, "The select() system call isn't available - aborting.\n");
  exit(1);
#else
/*
 * Create the line editor, specifying a maximum line length of 500 bytes,
 * and 10000 bytes to allocate to storage of historical input lines.
 */
  gl = demo_gl = new_GetLine(500, 5000);
  if(!gl)
    cleanup_and_exit(gl, si, 1);

/*
 * Allocate an object in which to temporarily record displaced
 * signal handlers.
 */
  si = new_SignalActions();
  if(!si)
    cleanup_and_exit(gl, si, 1);
/*
 * If the user has the LC_CTYPE or LC_ALL environment variables set,
 * enable display of characters corresponding to the specified locale.
 */
  (void) setlocale(LC_CTYPE, "");
/*
 * Lookup and display the version number of the library.
 */
  libtecla_version(&major, &minor, &micro);
  printf(
    "\n Welcome to the server-mode demo program of libtecla version %d.%d.%d\n",
      major, minor, micro);
/*
 * Display some introductory text, left-justifying it within the current
 * width of the terminal and enclosing it in a box of asterixes.
 */
  show_demo_introduction(gl);
/*
 * Load history.
 */
#ifndef WITHOUT_FILE_SYSTEM
  (void) gl_load_history(gl, "~/.demo_history", "#");
#endif
/*
 * In this demo, rather than having gl_get_line() return immediately
 * when it would otherwise have to wait for I/O, we register a timeout
 * callback which causes gl_get_line() to give up waiting after a short
 * interval.
 */
  gl_inactivity_timeout(gl, demo_timeout_fn, NULL, 0, DEMO_IO_TIMEOUT);
/*
 * Install our signal handlers for process termination, suspension and
 * terminal resize signals. Ignore process continuation signals.
 */
  gl_tty_signals(demo_signal_handler, demo_signal_handler, SIG_DFL,
		 demo_signal_handler);
/*
 * Get a list of all of the signals that gl_get_line() currently catches.
 */
  gl_list_signals(gl, &all_signal_mask);
/*
 * Switch gl_get_line() to non-blocking server mode.
 */
  if(gl_io_mode(gl, GL_SERVER_MODE))
    cleanup_and_exit(gl, si, 1);
/*
 * Instruct gl_get_line() to unblock any signals that it catches
 * while waiting for input. Note that in non-blocking server mode,
 * this is only necessary when using gl_inactivity_timeout() to make
 * gl_get_line() block for a non-zero amount of time.
 */
  gl_catch_blocked(gl);
/*
 * Enter the event loop.
 */
  while(1) {
    int nready;        /* The number of file-descriptors that are */
                       /*  ready for I/O */
    fd_set rfds;       /* The set of file descriptors to watch for */
                       /*  readability */
    fd_set wfds;       /* The set of file descriptors to watch for */
                       /*  writability */
/*
 * Construct the sets of file descriptors to be watched by select(),
 * starting from empty sets.
 */
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);
/*
 * To ensure that no signals are received whos handlers might change
 * the requirements for the contents of the above signal sets, block
 * all of the signals that we are handling.
 */
    sigprocmask(SIG_BLOCK, &all_signal_mask, NULL);
/*
 * Depending on which direction of I/O gl_get_line()s is currently
 * waiting for, add the terminal file descriptor to either the set
 * of file descriptors to watch for readability, or those to watch
 * for writability. Note that at the start of a new line, such as
 * after an error, or the return of a completed line, we need to
 * wait for writability, so that a prompt can be written.
 */
    switch(gl_pending_io(gl)) {
    case GLP_READ:
      FD_SET(STDIN_FILENO, &rfds);
      break;
    default:
      FD_SET(STDIN_FILENO, &wfds);
      break;
    };
/*
 * Wait for I/O to become possible on the selected file descriptors.
 * The following is a signal-aware wrapper around the select() system
 * call. This wrapper guarantees that if any of the signals marked in
 * all_signal_mask arrive after the statement above where we blocked
 * these signals, it will detect this and abort with nready=-1 and
 * errno=EINTR. If instead, we just unblocked the above signals just
 * before calling a normal call to select(), there would be a small
 * window of time between those two statements in which a signal could
 * arrive without aborting select(). This would be a problem, since
 * the functions called by our signal handler may change the type
 * of I/O that gl_get_line() wants us to wait for in select().
 */
    nready = demo_sigselect(STDIN_FILENO + 1, &rfds, &wfds, NULL, NULL,
			    &all_signal_mask, si);
/*
 * We can now unblock our signals again.
 */
    sigprocmask(SIG_UNBLOCK, &all_signal_mask, NULL);
/*
 * Did an I/O error occur?
 */
    if(nready < 0 && errno != EINTR)
      cleanup_and_exit(gl, si, 1);
/*
 * If the terminal file descriptor is now ready for I/O, call
 * gl_get_line() to continue editing the current input line.
 */
    if(FD_ISSET(STDIN_FILENO, &rfds) || FD_ISSET(STDIN_FILENO, &wfds)) {
/*
 * Start or continue editing an input line.
 */
      char *line = gl_get_line(gl, "$ ", NULL, 0);
/*
 * Did the user finish entering a new line?
 */
      if(line) {
/*
 * Before writing messages to the terminal, start a new line and
 * switch back to normal terminal I/O.
 */
	gl_normal_io(gl);
/*
 * Display what was entered.
 */
	if(printf("You entered: %s", line) < 0 || fflush(stdout))
	  break;
/*
 * Implement a few simple commands.
 */
	if(strcmp(line, "exit\n")==0)
	  cleanup_and_exit(gl, si, 0);
	else if(strcmp(line, "history\n")==0)
	  gl_show_history(gl, stdout, "%N  %T   %H\n", 0, -1);
	else if(strcmp(line, "size\n")==0) {
	  GlTerminalSize size = gl_terminal_size(gl, 80, 24);
	  printf("Terminal size = %d columns x %d lines.\n", size.ncolumn,
		 size.nline);
	} else if(strcmp(line, "clear\n")==0) {
	  if(gl_erase_terminal(gl))
	    return 1;
	};
/*
 * To resume command-line editing, return the terminal to raw,
 * non-blocking I/O mode.
 */
	gl_raw_io(gl);
/*
 * If gl_get_line() returned NULL because of an error or end-of-file,
 * abort the program.
 */
      } else if(gl_return_status(gl) == GLR_ERROR ||
		gl_return_status(gl) == GLR_EOF) {
	cleanup_and_exit(gl, si, 1);
      };
    };
  };
#endif
  return 0;
}

/*.......................................................................
 * This function is called to return resources to the system and restore
 * the terminal to its original state before exiting the process.
 *
 * Input:
 *  gl        GetLine *   The line editor.
 *  si  SignalActions *   The repository for displaced signal handlers.
 *  status        int     The exit code of the process.
 */
static void cleanup_and_exit(GetLine *gl, SignalActions *si, int status)
{
/*
 * Restore the terminal to its original state before exiting the program.
 */
  gl_normal_io(gl);
/*
 * Save historical command lines.
 */
#ifndef WITHOUT_FILE_SYSTEM
  (void) gl_save_history(gl, "~/.demo_history", "#", -1);
#endif
/*
 * Clean up.
 */
  gl = del_GetLine(gl);
  si = del_SignalActions(si);
/*
 * Exit the process.
 */
  exit(status);
}

/*.......................................................................
 * This is a signal-aware wrapper around the select() system call. It
 * is designed to facilitate reliable signal handling of a given set
 * of signals, without the race conditions that would usually surround
 * the use of select(). See the "RELIABLE SIGNAL HANDLING" section of
 * the gl_get_line(3) man page for further details.
 *
 * Provided that the calling function has blocked the specified set of
 * signals before calling this function, this function guarantees that
 * select() will be aborted by any signal that arrives between the
 * time that the caller blocked the specified signals and this
 * function returns. On return these signals will again be blocked to
 * prevent any signals that arrive after select() returns, from being
 * missed by the caller.
 *
 * Note that this function is written not to be specific to this
 * program, and is thus suitable for use in other programs, whether or
 * not they use gl_get_line().
 *
 * Also note that this function depends on the NSIG preprocessor
 * constant being >= the maximum number of signals available on the
 * host operating system. Under BSD and SysV, this macro is set
 * appropriately in signal.h. On other systems, a reasonably large
 * guess should be substituted. Although nothing terrible will happen
 * if a value that is too small is chosen, signal numbers that exceed
 * the specified value of NSIG will be ignored by this function. A
 * more robust method than depending on nsig would be to use the
 * POSIX sigismember() function to count valid signals, and use this
 * to allocate the array of sigaction structures used to preserve
 * 
 *
 * Input:
 *  n                   int    The number of file descriptors to pay
 *                             attention to at the start of each of the
 *                             following sets of file descriptors.
 *  readfds          fd_set *  The set of file descriptors to check for
 *                             readability, or NULL if not pertinent.
 *  wwritefds        fd_set *  The set of file descriptors to check for
 *                             writability, or NULL if not pertinent.
 *  exceptfds        fd_set *  The set of file descriptors to check for
 *                             the arrival of urgent data, or NULL if
 *                             not pertinent.
 *  timeout  struct timeval *  The maximum time that select() should
 *                             wait, or NULL to wait forever.
 *  mask           sigset_t *  The set of signals to catch.
 *  si       SignalHandlers *  An object in which to preserve temporary
 *                             copies signal handlers.
 * Output:
 *  return              int    > 0  The number of entries in all of the
 *                                  sets of descriptors that are ready
 *                                  for I/O.
 *                               0  Select() timed out.
 *                              -1  Error (see errno).
 */
static int demo_sigselect(int n, fd_set *readfds, fd_set *writefds,
			  fd_set *exceptfds, struct timeval *timeout,
			  sigset_t *mask, SignalActions *si)
{
/*
 * The reason that the the following variables are marked as volatile
 * is to prevent the compiler from placing their values in registers
 * that might not be saved and restored by sigsetjmp().
 */
  volatile sigset_t old_mask;  /* The displaced process signal mask */
  volatile int status;         /* The return value of select() */
/*
 * Make sure that all of the specified signals are blocked. This is
 * redundant if the caller has already blocked signals.
 */
  if(sigprocmask(SIG_BLOCK, mask, (sigset_t *) &old_mask) < 0)
    return -1;
/*
 * Record the fact that no signal has been caught yet.
 */
  demo_setjmp_signo = -1;
/*
 * Now set up the point where our temporary signal handlers will return
 * control if a signal is received.
 */
  if(sigsetjmp(demo_setjmp_buffer, 1) == 0) {
/*
 * Now install the temporary signal handlers that cause the above
 * sigsetjmp() to return non-zero when a signal is detected.
 */
    if(displace_signal_handlers(si, mask, demo_setjmp_handler)) {
      reinstate_signal_handlers(si);
      return 1;
    };
/*
 * Now that we are ready to catch the signals, unblock them.
 */
    sigprocmask(SIG_UNBLOCK, mask, NULL);
/*
 * At last, call select().
 */
    status = select(n, readfds, writefds, exceptfds, timeout);
/*
 * Block the specified signals again.
 */
    sigprocmask(SIG_BLOCK, mask, NULL);
/*
 * Record the fact that no signal was caught.
 */
    demo_setjmp_signo = -1;
  };
/*
 * We can get to this point in one of two ways. Either no signals were
 * caught, and the above block ran to completion (with demo_setjmp_signo=-1),
 * or a signal was caught that caused the above block to be aborted,
 * in which case demo_setjmp_signo will now equal the number of the signal that
 * was caught, and sigsetjmp() will have restored the process signal
 * mask to how it was before it was called (ie. all of the specified
 * signals blocked).
 *
 * First restore the signal handlers to how they were on entry to
 * this function.
 */
  reinstate_signal_handlers(si);
/*
 * Was a signal caught?
 */
  if(demo_setjmp_signo > 0) {
    sigset_t new_mask;
/*
 * Send the signal again, then unblock its delivery, so that the application's
 * signal handler gets invoked.
 */
    raise(demo_setjmp_signo);
    sigemptyset(&new_mask);
    sigaddset(&new_mask, demo_setjmp_signo);
    sigprocmask(SIG_UNBLOCK, &new_mask, NULL);
/*
 * Set the return status to show that a signal was caught.
 */
    errno = EINTR;
    status = -1;
  };
/*
 * Now restore the process signal mask to how it was on entry to this
 * function.
 */
  sigprocmask(SIG_SETMASK, (sigset_t *) &old_mask, NULL);
  return status;
}

/*.......................................................................
 * This is the main signal handler of this demonstration program. If a
 * SIGINT is received by the process, it arranges that the next call
 * to gl_get_line() will abort entry of the current line and start
 * entering a new one. Otherwise it calls the library function which
 * handles terminal resize signals and process suspension and process
 * termination signals. Both of the functions called by this signal
 * handler are designed to be async-signal safe, provided that the
 * rules laid out in the gl_io_mode(3) man page are followed.
 */
static void demo_signal_handler(int signo)
{
  if(signo==SIGINT)
    gl_abandon_line(demo_gl);
  else
    gl_handle_signal(signo, demo_gl, 1);
}

/*.......................................................................
 * The following signal handler is installed while select() is being
 * called from within a block of code protected by sigsetjmp(). It
 * simply records the signal that was caught in setjmp_signo, then
 * causes the sigsetjmp() to return non-zero.
 */
static void demo_setjmp_handler(int signo)
{
  demo_setjmp_signo = signo;
  siglongjmp(demo_setjmp_buffer, 1);
}

/*.......................................................................
 * This optional inactivity timeout function is used in this
 * demonstration to cause gl_get_line() to wait for a small amount of
 * time for I/O, before returning and allowing the event loop to
 * continue. This isn't needed if you want gl_get_line() to return
 * immediately, rather than blocking.
 */
static GL_TIMEOUT_FN(demo_timeout_fn)
{
  return GLTO_CONTINUE;
}

/*.......................................................................
 * Display introductory text to the user, formatted according to the
 * current terminal width and enclosed in a box of asterixes.
 *
 * Input:
 *  gl      GetLine *   The resource object of gl_get_line().
 */
static void show_demo_introduction(GetLine *gl)
{
  int start;     /* The column in which gl_display_text() left the cursor */
  int i;
/*
 * Break the indtroductory text into an array of strings, so as to
 * avoid overflowing any compiler string limits.
 */
  const char *doc[] = {
    "To the user this program appears to act identically to the main ",
    "demo program. However whereas the code underlying the main demo ",
    "program uses gl_get_line() in its default configuration, where each ",
    "call blocks the caller until the user has entered a complete input ",
    "line, demo3 uses gl_get_line() in its non-blocking server mode, ",
    "where it must be called repeatedly from an external ",
    "event loop to incrementally accept entry of the input ",
    "line, as and when terminal I/O becomes possible. The well commented ",
    "source code of demo3, which can be found in demo3.c, thus provides ",
    "a working example of how to use gl_get_line() in a manner that ",
    "doesn't block the caller. Documentation of this mode can be found ",
    "in the gl_io_mode(3) man page.\n"
  };
/*
 * Form the top line of the documentation box by filling the area of
 * the line between a " *" prefix and a "* " suffix with asterixes.
 */
  printf("\n");
  gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n");
/*
 * Justify the documentation text within margins of asterixes.
 */
  for(start=0,i=0; i<sizeof(doc)/sizeof(doc[0]) && start >= 0; i++)
    start = gl_display_text(gl, 0, " * ", " * ", ' ', 80, start,doc[i]);
/*
 * Draw the bottom line of the documentation box.
 */
  gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n");
  printf("\n");
}


/*.......................................................................
 * This is a constructor function for an object who's role is to allow
 * a signal handler to be assigned to potentially all available signals,
 * while preserving a copy of the original signal handlers, for later
 * restration.
 *
 * Output:
 *  return  SignalActions *  The new object, or NULL on error.
 */
static SignalActions *new_SignalActions(void)
{
  SignalActions *si;  /* The object to be returned */
/*
 * Allocate the container.
 */
  si = malloc(sizeof(SignalActions));
  if(!si) {
    fprintf(stderr, "new_SignalActions: Insufficient memory.\n");
    return NULL;
  };
/*
 * Before attempting any operation that might fail, initialize the
 * container at least up to the point at which it can safely be passed
 * to del_SignalActions().
 */
  si->nsignal = 0;
  sigemptyset(&si->mask);
  si->actions = NULL;
/*
 * Count the number of signals that are available of the host
 * platform. Note that si->mask has no members set, and that
 * sigismember() is defined to return -1 if the signal number
 * isn't valid.
 */
  for(si->nsignal=1; sigismember(&si->mask, si->nsignal) == 0; si->nsignal++)
    ;
/*
 * Allocate the array of sigaction structures to use to keep a record
 * of displaced signal handlers.
 */
  si->actions = (struct sigaction *) malloc(sizeof(*si->actions) * si->nsignal);
  if(!si->actions) {
    fprintf(stderr, "Insufficient memory for %d sigaction structures.\n",
	    si->nsignal);
    return del_SignalActions(si);
  };
  return si;
}

/*.......................................................................
 * Delete a SignalActions object.
 *
 * Input:
 *  si     SignalActions *  The object to be deleted.
 * Output:
 *  return SignalActions *  The deleted object (always NULL).
 */
static SignalActions *del_SignalActions(SignalActions *si)
{
  if(si) {
    if(si->actions)
      free(si->actions);
    free(si);
  };
  return NULL;
}

/*.......................................................................
 * Replace the signal handlers of all of the signals in 'mask' with
 * the signal handler 'handler'.
 *
 * Input:
 *  si       SignalActions *     The object in which to record the displaced
 *                               signal handlers.
 *  mask          sigset_t *     The set of signals who's signal handlers
 *                               should be displaced.
 *  handler void (*handler)(int) The new signal handler to assign to each
 *                               of the signals marked in 'mask'.
 * Output:
 *  return             int       0 - OK.
 *                               1 - Error.
 */
static int displace_signal_handlers(SignalActions *si, sigset_t *mask,
				    void (*handler)(int))
{
  int signo;                /* A signal number */
  struct sigaction action;  /* The new signal handler */
/*
 * Mark the fact that so far we haven't displaced any signal handlers.
 */
  sigemptyset(&si->mask);
/*
 * Set up the description of the new signal handler. Note that
 * we make sa_mask=mask. This ensures that only one instance of the
 * signal handler will ever be running at one time.
 */
  action.sa_handler = handler;
  memcpy(&action.sa_mask, mask, sizeof(*mask));
  action.sa_flags = 0;
/*
 * Check each of the available signals to see if it is specified in 'mask'.
 * If so, install the new signal handler, record the displaced one in
 * the corresponding element of si->actions[], and make a record in
 * si->mask that this signal handler has been displaced.
 */
  for(signo=1; signo < si->nsignal; signo++) {
    if(sigismember(mask, signo)) {
      if(sigaction(signo, &action, &si->actions[signo]) < 0) {
	fprintf(stderr, "sigaction error (%s)\n", strerror(errno));
	return 1;
      };
      sigaddset(&si->mask, signo);
    };
  };
  return 0;
}

/*.......................................................................
 * Reinstate any signal handlers displaced by displace_signal_handlers().
 *
 * Input:
 *  sig   SignalActions *  The object containing the displaced signal
 *                         handlers.
 * Output:
 *  return          int    0 - OK.
 *                         1 - Error.
 */
static int reinstate_signal_handlers(SignalActions *si)
{
  int signo;                /* A signal number */
/*
 * Check each of the available signals to see if it is specified in
 * si->mask. If so, reinstate the displaced recorded in the
 * corresponding element of si->actions[], and make a record in
 * si->mask that this signal handler has been reinstated.
 */
  for(signo=1; signo < si->nsignal; signo++) {
    if(sigismember(&si->mask, signo)) {
      if(sigaction(signo, &si->actions[signo], NULL) < 0) {
	fprintf(stderr, "sigaction error (%s)\n", strerror(errno));
	return 1;
      };
      sigdelset(&si->mask, signo);
    };
  };
  return 0;
}
