/* **********************************************************************
 * *                                                                    *
 * *            Error Reporting Function For C Routines                 *
 * *                                                                    *
 * **********************************************************************
 * File: libs/user/error_report.c
 *
 *      COPYRIGHT (c) 1992, 1996, 1998, 1999, 2000, 2003, 2004  BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include <signal.h>
#include <nl_types.h>
#ifdef HPUX
#include <siginfo.h>
#endif
#include <sys/wait.h>

#include "sigtypes.h"
#include "user_lib.h"

/* The following variable is set to TRUE whenever a process is killed
 * and we wish to allow automatic restarting.
 */
static UINT2 lAllowAutoRestart_c = FALSE ;
static int   ignored_signal_c = 0 ;
static UINT1 lsay_fatal_errors_c = FALSE ;

#define NUM_SUPPORTED_SIGNALS (15)
struct signal_response {
  int  iSignal;
  int  iResponse;
  char sMessage[32];
};

static struct signal_response Response_c[NUM_SUPPORTED_SIGNALS] =
  { {-1, -1, {'\0'}}, } ;

static MESSAGE (*signal_handler_c)
  (MESSAGE istatus, UINT4 icount, const UINT4 iargs[] ) = NULL;

static char sprocess_name_c[32];
     
#define MAX_SIG_ARGUMENTS (4)	/* Max # args expanded in a message */
#define MAX_AUX_ARGUMENTS (4)	/* Max # args expanded appended message text */

/* Variables related to message modification.
 */
struct msgmod {
  UINT2 iFacility ;		/* Facility number */
  UINT2 iMessage ;		/* Catalog message number */
  char stext[80] ;		/* Supplimentary text */
} ;

static struct msgmod msgMods_c[MSGMOD_COUNT] = { {0,}, } ;
static SINT4 iMsgModsIndex_c = 0 ;

/* Local Functions
 */
static void InitResponse
( struct signal_response *pResponse_a,
  int iSignal_a,
  int iAction_a,
  const char *sMessage_a ) ;
     
/* **********************************
 * *                                *
 * * Message Text Handling Routines *
 * *                                *
 * **********************************
 */

/* ==================================================
 * Convert error number from unix style to SIGMET style.
 */
MESSAGE errno_to_message( int errno_a )
{
  MESSAGE istatus = errno_to_message_va( errno_a, 0 ) ;
  return( istatus ) ;
}

MESSAGE errno_to_message_va( int errno_a, UINT4 icount_a, ... )
{
  MESSAGE istatus = SS_NORMAL ;
  UINT4 icount = MIN( MAX_AUX_ARGUMENTS, (icount_a & 0xFFFF) ) ;

  /* First try to translate this errno into a message catalog entity.
   */
  if( errno_a ) {
    switch( errno_a ) {
    default:
      sig_signal( iMessageMod_va( USER_ERRNO_NONSIGMET, 2, "%d", errno_a ) );
      istatus = USER_ERRNO_NONSIGMET       ; break ;
    case EPERM:           istatus = USER_ERRNO_EPERM           ; break ;
    case ENOENT:          istatus = USER_ERRNO_ENOENT          ; break ;
    case ESRCH:           istatus = USER_ERRNO_ESRCH           ; break ;
    case EINTR:           istatus = USER_ERRNO_EINTR           ; break ;
    case EIO:             istatus = USER_ERRNO_EIO             ; break ;
    case ENXIO:           istatus = USER_ERRNO_ENXIO           ; break ;
    case E2BIG:           istatus = USER_ERRNO_E2BIG           ; break ;
    case ENOEXEC:         istatus = USER_ERRNO_ENOEXEC         ; break ;
    case EBADF:           istatus = USER_ERRNO_EBADF           ; break ;
    case ECHILD:          istatus = USER_ERRNO_ECHILD          ; break ;
    case EAGAIN:          istatus = USER_ERRNO_EAGAIN          ; break ;
    case ENOMEM:          istatus = USER_ERRNO_ENOMEM          ; break ;
    case EACCES:          istatus = USER_ERRNO_EACCES          ; break ;
    case EFAULT:          istatus = USER_ERRNO_EFAULT          ; break ;
    case ENOTBLK:         istatus = USER_ERRNO_ENOTBLK         ; break ;
    case EBUSY:           istatus = USER_ERRNO_EBUSY           ; break ;
    case EEXIST:          istatus = USER_ERRNO_EEXIST          ; break ;
    case EXDEV:           istatus = USER_ERRNO_EXDEV           ; break ;
    case ENODEV:          istatus = USER_ERRNO_ENODEV          ; break ;
    case ENOTDIR:         istatus = USER_ERRNO_ENOTDIR         ; break ;
    case EISDIR:          istatus = USER_ERRNO_EISDIR          ; break ;
    case EINVAL:          istatus = USER_ERRNO_EINVAL          ; break ;
    case ENFILE:          istatus = USER_ERRNO_ENFILE          ; break ;
    case EMFILE:          istatus = USER_ERRNO_EMFILE          ; break ;
    case ENOTTY:          istatus = USER_ERRNO_ENOTTY          ; break ;
    case ETXTBSY:         istatus = USER_ERRNO_ETXTBSY         ; break ;
    case EFBIG:           istatus = USER_ERRNO_EFBIG           ; break ;
    case ENOSPC:          istatus = USER_ERRNO_ENOSPC          ; break ;
    case ESPIPE:          istatus = USER_ERRNO_ESPIPE          ; break ;
    case EROFS:           istatus = USER_ERRNO_EROFS           ; break ;
    case EMLINK:          istatus = USER_ERRNO_EMLINK          ; break ;
    case EPIPE:           istatus = USER_ERRNO_EPIPE           ; break ;
    case EDOM:            istatus = USER_ERRNO_EDOM            ; break ;
    case ERANGE:          istatus = USER_ERRNO_ERANGE          ; break ;
#if EWOULDBLOCK!=EAGAIN
    case EWOULDBLOCK:     istatus = USER_ERRNO_EWOULDBLOCK     ; break ;
#endif
    case EINPROGRESS:     istatus = USER_ERRNO_EINPROGRESS     ; break ;
    case EALREADY:        istatus = USER_ERRNO_EALREADY        ; break ;
    case ENOTSOCK:        istatus = USER_ERRNO_ENOTSOCK        ; break ;
    case EDESTADDRREQ:    istatus = USER_ERRNO_EDESTADDRREQ    ; break ;
    case EMSGSIZE:        istatus = USER_ERRNO_EMSGSIZE        ; break ;
    case EPROTOTYPE:      istatus = USER_ERRNO_EPROTOTYPE      ; break ;
    case ENOPROTOOPT:     istatus = USER_ERRNO_ENOPROTOOPT     ; break ;
    case EPROTONOSUPPORT: istatus = USER_ERRNO_EPROTONOSUPPORT ; break ;
    case ESOCKTNOSUPPORT: istatus = USER_ERRNO_ESOCKTNOSUPPORT ; break ;
    case EOPNOTSUPP:      istatus = USER_ERRNO_EOPNOTSUPP      ; break ;
    case EPFNOSUPPORT:    istatus = USER_ERRNO_EPFNOSUPPORT    ; break ;
    case EAFNOSUPPORT:    istatus = USER_ERRNO_EAFNOSUPPORT    ; break ;
    case EADDRINUSE:      istatus = USER_ERRNO_EADDRINUSE      ; break ;
    case EADDRNOTAVAIL:   istatus = USER_ERRNO_EADDRNOTAVAIL   ; break ;
    case ENETDOWN:        istatus = USER_ERRNO_ENETDOWN        ; break ;
    case ENETUNREACH:     istatus = USER_ERRNO_ENETUNREACH     ; break ;
    case ENETRESET:       istatus = USER_ERRNO_ENETRESET       ; break ;
    case ECONNABORTED:    istatus = USER_ERRNO_ECONNABORTED    ; break ;
    case ECONNRESET:      istatus = USER_ERRNO_ECONNRESET      ; break ;
    case ENOBUFS:         istatus = USER_ERRNO_ENOBUFS         ; break ;
    case EISCONN:         istatus = USER_ERRNO_EISCONN         ; break ;
    case ENOTCONN:        istatus = USER_ERRNO_ENOTCONN        ; break ;
    case ESHUTDOWN:       istatus = USER_ERRNO_ESHUTDOWN       ; break ;
    case ETOOMANYREFS:    istatus = USER_ERRNO_ETOOMANYREFS    ; break ;
    case ETIMEDOUT:       istatus = USER_ERRNO_ETIMEDOUT       ; break ;
    case ECONNREFUSED:    istatus = USER_ERRNO_ECONNREFUSED    ; break ;
    case ELOOP:           istatus = USER_ERRNO_ELOOP           ; break ;
    case ENAMETOOLONG:    istatus = USER_ERRNO_ENAMETOOLONG    ; break ;
    case EHOSTDOWN:       istatus = USER_ERRNO_EHOSTDOWN       ; break ;
    case EHOSTUNREACH:    istatus = USER_ERRNO_EHOSTUNREACH    ; break ;
    case ENOTEMPTY:       istatus = USER_ERRNO_ENOTEMPTY       ; break ;
    }
  }                                    

  /* Possibly modify the message with suplimentary text.
   */
  if( (istatus != SS_NORMAL) && (icount > 0) ) {
    va_list ap ; UINT4 iArgs[MAX_AUX_ARGUMENTS], ii ;

    va_start( ap, icount_a ) ;
    for( ii=0 ; ii < icount ; ii++ ) iArgs[ii] = va_arg( ap, UINT4 );

    switch( icount ) {
    case 1: istatus = iMessageMod_va( istatus, 1, iArgs[0] ) ;
      break ;
    case 2: istatus = iMessageMod_va( istatus, 2, iArgs[0], iArgs[1] ) ;
      break ;
    case 3: istatus = iMessageMod_va( istatus, 3, iArgs[0], iArgs[1], iArgs[2] ) ;
      break ;
    case 4: istatus = iMessageMod_va( istatus, 4, iArgs[0], iArgs[1], iArgs[2], iArgs[3] ) ;
      break ;
    }
  }

  return( istatus ) ;
}                                        

/* ==================================================
 * Translate message number to text string.
 */                                      
#define FACILITY_MASK (0xFF)	/* Only deal with this many */

char *message_to_text		/* Original thread-unsafe version */
( MESSAGE istatus_a, UINT4 icount_a, const UINT4 iarg_list_a[] )
{
  static char sMessageRet[MESSAGE_SIZE] ;
  return( message_to_text_r( istatus_a, icount_a, iarg_list_a, sMessageRet ) ) ;
}

/* =====================================================
 * Translate a message number to a text string.  Will never
 * use more than MESSAGE_SIZE characters.
 */
char *message_to_text_r		/* Re-entrant version */
( MESSAGE istatus_a, UINT4 icount_a, const UINT4 iarg_list_a[],
  char sMessageRet_a[MESSAGE_SIZE] )
{
  char smessage[MESSAGE_SIZE], sdefault[80] ;
  UINT4 ii, ifacility, imessage, icount ;

  static UINT1 lFirst = TRUE ; static nl_catd catd[ FACILITY_MASK + 1 ] ;

  /* Initialize all the catalog pointers to -1 the first time through.
   * Threaded applications should call this routine once from the root
   * process prior to starting other threads.
   */
  if( lFirst ) {
    for( ii=0 ; ii < (FACILITY_MASK + 1) ; ii++ ) catd[ii] = (nl_catd)-1 ;
    lFirst = FALSE ;
  }
  ifacility = FACILITY_MASK & (istatus_a >> 16) ;
  imessage  = istatus_a & MSGVAL_MASK ;
  icount    = icount_a  & 0xFFFF ;

  /* Open the catalog if it is not already done
   */
  if( catd[ifacility] == (nl_catd)-1 )
    {
      SINT4 iLength;
      char sPath[PATHNAME_SIZE], *sDir;
      sDir = getenv("IRIS_NLS");
      if (sDir)
	{
	  strcpy( sPath, sDir );
	  iLength = strlen(sPath);
	  /* Append "C/" if it does not end with a language */
	  if ((iLength>5)&&(0==strcmp( &sPath[iLength-5], "/nls/" ) ) )
	    { strcat( sPath, "C/" ); iLength+=2; }
	}
      else
	{
	  sPath[0] = 0;
	  iLength = 0;
	}
      
      sprintf(&sPath[iLength], "iris_signal%d.cat", ifacility );
      catd[ifacility] = catopen( sPath, 0 );
    }

  /* Translate the message based on the catalog file.
   */
  switch( icount )
    {
    default:
      sprintf(sdefault, "Message #%d, Facility #%d.", imessage, ifacility );
      strncpy( sMessageRet_a, catgets( catd[ifacility], ifacility, imessage, sdefault ),
	       MESSAGE_SIZE-1 );
      sMessageRet_a[MESSAGE_SIZE-1]=0;
      break;
    case 1:
      sprintf(sdefault, "Message #%d, Facility #%d, %d.", imessage, ifacility,
	      iarg_list_a[0] );
      strncpy( smessage, catgets( catd[ifacility], ifacility, imessage, sdefault ), MESSAGE_SIZE-1 );
      smessage[MESSAGE_SIZE-1]=0;
      snprintf( sMessageRet_a, MESSAGE_SIZE, smessage, iarg_list_a[0] );
      break;
    case 2:
      sprintf(sdefault, "Message #%d, Facility #%d, %d, %d.", imessage, ifacility,
	      iarg_list_a[0], iarg_list_a[1] );
      strncpy( smessage, catgets( catd[ifacility], ifacility, imessage, sdefault ), MESSAGE_SIZE-1 );
      smessage[MESSAGE_SIZE-1]=0;
      snprintf( sMessageRet_a, MESSAGE_SIZE, smessage, iarg_list_a[0], iarg_list_a[1] );
      break;
    case 3:
      sprintf(sdefault, "Message #%d, Facility #%d, %d, %d, %d.", imessage, ifacility,
	      iarg_list_a[0], iarg_list_a[1], iarg_list_a[2] );
      strncpy( smessage, catgets( catd[ifacility], ifacility, imessage, sdefault ), MESSAGE_SIZE-1 );
      smessage[MESSAGE_SIZE-1]=0;
      snprintf( sMessageRet_a, MESSAGE_SIZE, smessage, iarg_list_a[0], iarg_list_a[1],
		iarg_list_a[2] );
      break;
    case 4:
      sprintf(sdefault, "Message #%d, Facility #%d, %d, %d, %d, %d.", imessage, ifacility,
	      iarg_list_a[0], iarg_list_a[1], iarg_list_a[2], iarg_list_a[3] );
      strncpy( smessage, catgets( catd[ifacility], ifacility, imessage, sdefault ), MESSAGE_SIZE-1 );
      smessage[MESSAGE_SIZE-1]=0;
      snprintf( sMessageRet_a, MESSAGE_SIZE, smessage, iarg_list_a[0], iarg_list_a[1], 
		iarg_list_a[2], iarg_list_a[3] );
      break;
    }

  /* Append supplimentary text if the modification bits in the
   * original signal value are non zero.
   */
  if( istatus_a & MSGMOD_MASK ) {
    SINT4 index = ((istatus_a & MSGMOD_MASK) >> MSGMOD_BIT) - 1 ;
    struct msgmod *msgmod = &msgMods_c[index] ;    

    /* Because the supplimentary text is dynamically assigned to each
     * modified signal, there is a chance that we've lost it by the
     * time this translation process is done.  Check that at least the
     * message and facility numbers match the stored entry.
     */
    strcat( sMessageRet_a, " <" ) ;

    if( (imessage == msgmod->iMessage) && (ifacility == msgmod->iFacility) )
      strncat( sMessageRet_a, msgmod->stext, sizeof(msgmod->stext) ) ;
    else
      strcat ( sMessageRet_a, "Text-Lost" ) ;

    strcat( sMessageRet_a, ">"  ) ;
  }

  return( sMessageRet_a ) ;
}                                        

/* ==================================================
 * Modify a signal so that it will contain some additional text
 * appended to its end when signaled.  The message number is altered
 * so that the supplimentary text can be looked up and appended later
 * when the message is actually translated.
 */
MESSAGE iMessageMod_va
( MESSAGE iMessage_a, UINT4 icount_a, ... )
{
  MESSAGE iMessage = iMessage_a ;

  /* If there is no supplimentary text, then return the original
   * message with no changes.
   */
  if( icount_a & 0xFFFF ) {
    UINT4 icount = MIN( MAX_AUX_ARGUMENTS, (icount_a & 0xFFFF) ) ;
    struct msgmod *msgmod ; va_list ap ;
    SINT4 ii, index ; UINT4 iArgs[MAX_AUX_ARGUMENTS] ;

    /* Grab an index, and bump the allocator for the modification
     * bits.  This is not thread safe, but the worst that can happen
     * is overlapped/lost messages.
     */
    index = iMsgModsIndex_c ;
    if( (index+1) < MSGMOD_COUNT ) iMsgModsIndex_c = (index+1) ;
    else                           iMsgModsIndex_c = 0 ;

    msgmod = &msgMods_c[index] ;

    /* Extract the argument list
     */
    va_start( ap, icount_a ) ;
    for( ii=0 ; ii < icount ; ii++ ) iArgs[ii] = va_arg( ap, UINT4 );

    /* Modify the signal value, and store away the auxiliary text
     * definition.
     */
    iMessage |= ((index + 1) << MSGMOD_BIT) ;

    msgmod->iFacility = (iMessage_a >> 16) ;
    msgmod->iMessage  = (iMessage_a & MSGVAL_MASK) ;

    switch( icount ) {
    default:
      msgmod->stext[0] = '\0' ;
      break ;
    case 1: 
      strncpy( msgmod->stext, (char *)iArgs[0], sizeof(msgmod->stext) ) ;
      msgmod->stext[sizeof(msgmod->stext)-1]=0;
      break ;
    case 2: 
      snprintf( msgmod->stext, sizeof(msgmod->stext), (char *)iArgs[0], iArgs[1] ) ;
      break ;
    case 3: 
      snprintf( msgmod->stext, sizeof(msgmod->stext), (char *)iArgs[0], iArgs[1], 
		iArgs[2] ) ;
      break ;
    case 4: 
      snprintf( msgmod->stext, sizeof(msgmod->stext), (char *)iArgs[0], iArgs[1], 
		iArgs[2], iArgs[3] ) ;
      break ;
    }

    /* Remove any non-printable characters that may have come in via
     * the added arguments.
     */
    {
      char *rch = msgmod->stext, *wch = msgmod->stext ;
      while( *rch ) {
	if( isprint( *rch ) ) *wch++ = *rch ; rch++ ;	
      }
      *wch++ = '\0' ;
    }
  }

  return( iMessage ) ;
}

/* ******************************
 * *                            *
 * * Signal Generation Routines *
 * *                            *
 * ******************************
 */

/* ==================================================
 * Signal a message with no arguments.
 */                                      
void sig_signal
( const MESSAGE istatus_a )
{                                        
  sig_signal_va( istatus_a, 0 ) ;
}                                        

/* ==================================================
 * Signal a message with at least some arguments.
 */                                      
void sig_signal_va
(MESSAGE istatus_a,
 UINT4 icount_a, ... )
{                                        
  va_list ap;
  UINT4 iarg_list[MAX_SIG_ARGUMENTS], ii, icount;

  va_start( ap, icount_a );
  icount = MIN( MAX_SIG_ARGUMENTS, (icount_a & 0xFFFF) ) ;
  for (ii=0; ii<icount; ii++)
    {
      iarg_list[ii] = va_arg( ap, UINT4 );
    }

  if (NULL == signal_handler_c)
    {
      fprintf( stderr, "%s\n", message_to_text( istatus_a, icount, iarg_list ) );
    }
  else
    {
      (*signal_handler_c)( istatus_a, icount_a, iarg_list );
    }
}

/* ****************************
 * *                          *
 * * Signal Handling Routines *
 * *                          *
 * ****************************
 */

/* ==================================================
 * UNIX signal handler to catch a subset of troublesome signals.  As a
 * special case, there is a hook to ignore the next occurence of any
 * particular signal.
 */
#ifdef IRIX
static void unix_signal_handler( int signal_a, int code, struct sigcontext *sc )
#endif
#ifdef HPUX 
static void unix_signal_handler( int signal_a, siginfo_t *info, void *context )
#endif
#ifdef AIX
static void unix_signal_handler( int signal_a )
#endif
#ifdef LINUX
static void unix_signal_handler( int signal_a )
#endif
{
  char string[120] ; UINT2 lexit, lMessage ;
  UINT4 icount, iIndex;
    
  /* Do nothing if we were told to ignore the next occurence of this
   * signal.
   */
  if( signal_a == ignored_signal_c )
    {
      ignored_signal_c = 0 ;
      return;
    }
 
  /* If this is the SIGCHLD signal, then perform a nonblocking WAIT to
   * clean up the zombie process.
   */
  if( signal_a == SIGCHLD )
    {
      int iWaitStatus;
      /* Might be multiple SIGCHLDs at the same time */
      while( 0 < wait3( &iWaitStatus, WNOHANG, 0 ) ) {}
      return ;
    }

  /* Handle the signal according to the actions that were originally
   * chosen for it.
   */
  lexit = FALSE;
  lAllowAutoRestart_c = FALSE;
  lMessage = TRUE;

  sprintf( string, "Unexpected Signal: %d", signal_a ) ;

  for (iIndex=0; iIndex<NUM_SUPPORTED_SIGNALS; iIndex++)
    {
      if (signal_a == Response_c[iIndex].iSignal)
	{
	  lexit               = (Response_c[iIndex].iResponse & SIGNAL_KILL_ON );
	  lAllowAutoRestart_c = (Response_c[iIndex].iResponse & SIGNAL_RESTART_ON );
	  lMessage            = (Response_c[iIndex].iResponse & SIGNAL_MESSAGE_ON );
	  strncpy( string, Response_c[iIndex].sMessage, sizeof(string) ) ;
	  break;
	}
    }
  icount=1;
  if ( lexit && lsay_fatal_errors_c )
  {
    icount |= SIG_SAY_MESSAGE;
  }
#ifdef IRIX
  {
    char newstring[120] ;
    snprintf( newstring, 120, "%s Code: %d Addr: 0x%llX",
	     string, code, sc->sc_pc ) ;
    strncpy( string, newstring, sizeof(string) ) ;
  }
#endif
#ifdef HPUX
  {
    char newstring[120] ;
    snprintf( newstring, 120, "%s Code: %d Addr: 0x%X",
	      string, info->si_code, (info->si_addr) ) ;
    strncpy( string, newstring, sizeof(string) ) ;
  }
#endif

  if (lMessage) sig_signal_va( USER_UNIX_SIG, icount, string ) ;
  if (lexit) exit(EXIT_FAILURE) ;
}

/* ==================================================
 * Get/Set auto restart flag.  This is used by external modules that
 * need to access the static internal flag.
 */                                      
UINT2 lAutoRestartRequested
  (void)
{
  return( lAllowAutoRestart_c ) ;
}

/* ==================================================
 */
void ignore_next_signal( int signal_a )
{
  ignored_signal_c = signal_a ;
}

/* ==================================================
 */
void set_say_on_error( UINT1 lsay_it_a )
{
  lsay_fatal_errors_c = lsay_it_a ;
}

/* ==================================================
 * Establish a signal handler to be called when sig_signal is called.
 * This routine also arranges for a set of signals to be caught and
 * signaled.
 */                                      
#define DO_RESTART   (SIGNAL_MESSAGE_ON | SIGNAL_RESTART_ON  | SIGNAL_KILL_ON )
#define DONT_RESTART (SIGNAL_MESSAGE_ON | SIGNAL_RESTART_OFF | SIGNAL_KILL_ON )
#define DONT_KILL    (SIGNAL_MESSAGE_ON | SIGNAL_RESTART_OFF | SIGNAL_KILL_OFF)

void sig_establish
( MESSAGE (*handler_a)(MESSAGE istatus, UINT4 icount, const UINT4 iargs[] ),
  const char *sprocess_a )
{
  sig_establish_opt( handler_a, sprocess_a, TRUE ) ;
}

void sig_establish_opt
( MESSAGE (*handler_a)(MESSAGE istatus, UINT4 icount, const UINT4 iargs[] ),
  const char *sprocess_a, UINT1 lTrapSignals_a )
{
  /* Initialize shared variables that will be referenced later.
   */
  lAllowAutoRestart_c = FALSE ;
  signal_handler_c = handler_a;
  strncpy( sprocess_name_c, sprocess_a, 32 );
  sprocess_name_c[31]=0;

  /* Initialize message catalog translation by making one dummy call
   * right now.  This must be done by the root process in a threaded
   * application (see comments in message_to_text_r()).
   */
  {
    char sMessage[160] ; message_to_text_r( SS_NORMAL, 0, NULL, sMessage ) ;
  }
  /* Optionally setup the UNIX signal handler
   */
  if( lTrapSignals_a ) {
    struct sigaction action ; SINT4 index=0 ;
    sigemptyset( &action.sa_mask ) ;
#ifdef HPUX
    action.sa_handler = NULL ;
    action.sa_flags = SA_SIGINFO | SA_RESTART;
    action.sa_sigaction = unix_signal_handler;
#else
    action.sa_handler = unix_signal_handler ;
    action.sa_flags = SA_RESTART;
#endif
    if( sigaction( SIGHUP , &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++],  SIGHUP, DONT_RESTART,
		  "Hangup" );

    if( sigaction( SIGINT , &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++],  SIGINT, DONT_RESTART,
		  "Interrupt from TTY" );

    if( sigaction( SIGQUIT, &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++], SIGQUIT, DONT_RESTART,
		  "Quit ( ^\\ from TTY )" );

    if( sigaction( SIGILL , &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++],  SIGILL,   DO_RESTART,
		  "Illegal Instruction" );

    if( sigaction( SIGABRT, &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++], SIGABRT,   DO_RESTART,
		  "Process Abort" );

    if( sigaction( SIGFPE , &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++],  SIGFPE,   DO_RESTART,
		  "Floating Point Exception" );

    if( sigaction( SIGBUS , &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++],  SIGBUS,   DO_RESTART,
		  "Bus Error" );

    if( sigaction( SIGSEGV, &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++], SIGSEGV,   DO_RESTART,
		  "Segmentation Violation" );

#ifndef LINUX
    if( sigaction( SIGSYS , &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++],  SIGSYS,    DONT_KILL,
		  "Bad Argument to System Call" );
#endif

    if( sigaction( SIGPIPE, &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++], SIGPIPE,   DO_RESTART,
		  "Write on Pipe with no reader" );

    if( sigaction( SIGTERM, &action, NULL ) != 0 ) goto error ;
    InitResponse( &Response_c[index++], SIGTERM, DONT_RESTART,
		  "Software Termination" );

    if( sigaction( SIGCHLD, &action, NULL ) != 0 ) goto error ;
  }
  return ;

 error:
  sig_signal( errno_to_message(errno) ) ;
}

/* ==================================================
 * Return the process name that was set with sig_establish.
 */
char *get_established_name
( void )
{                                        
  return sprocess_name_c;
}                                        

/* ==================================================
 * Initialize an entry in the response table.
 */
static void InitResponse
( struct signal_response *pResponse_a,
  int iSignal_a,
  int iAction_a,
  const char *sMessage_a )
{
  memset( pResponse_a, 0, sizeof(*pResponse_a) );
  pResponse_a->iSignal   = iSignal_a;
  pResponse_a->iResponse = iAction_a;
  strncpy( pResponse_a->sMessage, sMessage_a, 31 );
}

/* ==================================================
 * Override the default signal response set by sig_establish.
 */
void OverrideSignalResponse
( int iSignal_a,
  int iResponse_a )
{
  SINT4 iIndex;
  for (iIndex=0; iIndex<NUM_SUPPORTED_SIGNALS; iIndex++)
    {
      if (iSignal_a == Response_c[iIndex].iSignal)
	{
	  Response_c[iIndex].iResponse = iResponse_a;
	  return;
	}
    }
  
  fprintf( stderr, "Invalid signal %d passed to OverrideSignalResponse()\n",
	   iSignal_a );
}
