text: /* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
   */
  
  #include <stdio.h>
  #include <signal.h>
  
  #include <math.h>
  #include <fenv.h>
  
  #define import_spp
  #define	import_kernel
  #define	import_knames
  #define import_xwhen
  #include <iraf.h>
  
  /* ZXWHEN.C -- IRAF exception handling interface.  This version has been 
   * customized for PC-IRAF, i.e., LINUX and FreeBSD.
   *
   * Rewritten Aug200 to use sigaction by default on all systems (done in 
   * connection with the LinuxPPC port).  This got rid of a lot of old kludgy
   * platform-dependent code used to workaround Linux signal handling problems.
   */
  
  /* Set the following nonzero to cause process termination with a core dump
   * when the first signal occurs.
   */
  int debug_sig = 0;
  
  
  /* If the OS allows, cancel any buffered output. If the OS doesn't, 
   * do nothing.
   */
  #ifdef __APPLE__
  #define	fcancel(fp)	((fp)->_r = (fp)->_w = 0)
  #else
  #define	fcancel(fp)
  #endif
  
  void ex_handler ( int, siginfo_t *, void * );
  
  static long setsig(int code, SIGFUNC handler);
  static int ignore_sigint = 0;
  
  
  /* Exception handling:  ZXWHEN (exception, handler, old_handler)
   *
   *	exception:	X_INT, X_ARITH, X_ACV, or X_IPC
   *
   *	handler:	Either X_IGNORE or the entry point address
   *			of a user supplied exception handler which
   *			will gain control in the event of an exception.
   *
   *	old_handler:	On output, contains the value of the previous
   *			handler (either X_IGNORE or an EPA).  Used to
   *			restore an old handler, or to chain handlers.
   *
   * An exception can be entirely disabled by calling ZXWHEN with the
   *   handler X_IGNORE.  Otherwise, the user supplied exception handler
   *   gains control when the exception occurs.  An exception handler is
   *   called with one argument, an integer code identifying the exception.
   *   The handler should return as its function value either X_IGNORE,
   *   causing normal processing to resume, or the EPA of the next handler
   *   to be called (normally the value of the parameter "old_handler").
   *   The user handler should call FATAL if error restart is desired.
   * 
   * If the SIGINT exeception has already been set to SIG_IGN, i.e., by the
   *   parent process which spawned us, then it will continue to be ignored.
   *   It is standard procedure in UNIX to spawn a background task with SIGINT
   *   disabled, so that interrupts sent to the parent process are ignored by
   *   the child.  If this is the case then SIGTERM may still be sent to the
   *   child to raise the X_INT exception in the high level code.
   */
  
  #define	EOMAP		(-1)	/* end of map array sentinel		*/
  #define	mask(s)		(1 << ((s) - 1))
  
  int	last_os_exception;	/* save OS code of last exception	*/
  int	last_os_hwcode;		/* hardware exception code		*/
  
  XINT handler_epa[] = {		/* table of handler EPAs		*/
  	0,			/* X_ACV    				*/
  	0,			/* X_ARITH  				*/
  	0,			/* X_INT    				*/
  	0,			/* X_IPC    				*/
  };
  
  struct osexc {
  	int	x_vex;		/* UNIX signal code			*/
  	char	*x_name;	/* UNIX signal name string		*/
  };
  
  struct osexc unix_exception[] = {
  	{ 0,		""					},
  	{ 0,		"hangup"				},
  	{ X_INT,	"interrupt"				},
  	{ 0,		"quit"					},
  	{ X_ACV,	"illegal instruction"			},
  	{ 0,		"trace trap"				},
  	{ X_ACV,	"abort"					},
  	{ X_ACV,	"EMT exception"				},
  	{ X_ARITH,	"arithmetic exception"			},
  	{ 0,		"kill"					},
  	{ X_ACV,	"bus error"				},
  	{ X_ACV,	"segmentation violation"		},
  	{ X_ACV,	"bad arg to system call"		},
  	{ X_IPC,	"write to pipe with no reader"		},
  	{ 0,		"alarm clock"				},
  	{ X_INT,	"software terminate (interrupt)" 	},
  	{ X_ARITH,	"STKFLT"				},
  	{ EOMAP,	""					}
  };
  
  
  /* Hardware exceptions [MACHDEP].  To customize for a new machine, replace
   * the symbol MYMACHINE by the machine name, #define the name in <iraf.h>
   * (i.e., hlib$libc/iraf.h), and edit the hardware exception list below.
   */
  struct	_hwx {
  	int	v_code;			/* Hardware exception code	*/
  	char	*v_msg;			/* Descriptive error message	*/
  };
  
  #ifdef __APPLE__
  #ifdef  FPE_INTDIV
  #undef  FPE_INTDIV
  #endif
  #define	FPE_INTDIV	(-2)		/* N/A */
  #ifdef  FPE_INTOVF
  #undef  FPE_INTOVF
  #endif
  #define	FPE_INTOVF	(-2)		/* N/A */
  #ifdef  FPE_FLTRES
  #undef  FPE_FLTRES
  #endif
  #define FPE_FLTRES	FE_INEXACT	/* inexact */
  #ifdef  FPE_FLTDIV
  #undef  FPE_FLTDIV
  #endif
  #define FPE_FLTDIV	FE_DIVBYZERO	/* divide-by-zero */
  #ifdef  FPE_FLTUND
  #undef  FPE_FLTUND
  #endif
  #define FPE_FLTUND	FE_UNDERFLOW	/* underflow */
  #ifdef  FPE_FLTOVF
  #undef  FPE_FLTOVF
  #endif
  #define FPE_FLTOVF	FE_OVERFLOW	/* overflow */
  #ifdef  FPE_FLTINV
  #undef  FPE_FLTINV
  #endif
  #define FPE_FLTINV	FE_INVALID	/* invalid */
  #ifdef  FPE_FLTSUB
  #undef  FPE_FLTSUB
  #endif
  #define	FPE_FLTSUB	(-2)		/* N/A */
  #endif
  
  struct	_hwx hwx_exception[] = {
  	{ FPE_INTDIV,             "integer divide by zero"		},
  	{ FPE_INTOVF,             "integer overflow"			},
  	{ FPE_FLTDIV,             "floating point divide by zero"	},
  	{ FPE_FLTOVF,             "floating point overflow"		},
  	{ FPE_FLTUND,             "floating point underflow"		},
  	{ FPE_FLTRES,             "floating point inexact result"	},
  	{ FPE_FLTINV,             "floating point invalid operation"	},
  	{ FPE_FLTSUB,             "subscript out of range"		},
  	{ EOMAP,			""				}
  };
  
  
  /* ZXWHEN -- Post an exception handler or turn off interrupts.  Return
   * value of old handler, so that it may be restored by the user code if
   * desired.  The function EPA's are the type of value returned by ZLOCPR.
   */
  int
  ZXWHEN (
    XINT	*sig_code,
    XINT	*epa,			/* EPA of new exception handler	*/
    XINT	*old_epa 		/* receives EPA of old handler	*/
  )
  {
  	static	int first_call = 1;
  	int     vex, uex;
  	SIGFUNC	vvector;
  
  	extern  int  kernel_panic (char *errmsg);
  
  
  	/* Convert code for virtual exception into an index into the table
  	 * of exception handler EPA's.
  	 */
  	switch (*sig_code) {
  	case X_ACV:
  	case X_ARITH:
  	case X_INT:
  	case X_IPC:
  	    vex = *sig_code - X_FIRST_EXCEPTION;
  	    break;
  	default:
  	    vex = (int) 0;
  	    kernel_panic ("zxwhen: bad exception code");
  	}
  	    
  	*old_epa = handler_epa[vex];
  	handler_epa[vex] = *epa;
  	vvector = (SIGFUNC) ex_handler;
  
  	/* Check for attempt to post same handler twice.  Do not return EPA
  	 * of handler as old_epa as this could lead to recursion.
  	 */
  	if (*epa == (XINT) X_IGNORE)
  	    vvector = (SIGFUNC) SIG_IGN;
  	else if (*epa == *old_epa)
  	    *old_epa = (XINT) X_IGNORE;
  
  	/* Set all hardware vectors in the indicated exception class.
  	 * If interrupt (SIGINT) was disabled when we were spawned (i.e.,
  	 * when we were first called to set SIGINT) leave it that way, else
  	 * we will get interrupted when the user interrupts the parent.
  	 */
  	for (uex=1;  unix_exception[uex].x_vex != EOMAP;  uex++) {
  	    if (unix_exception[uex].x_vex == *sig_code) {
  		if (uex == SIGINT) {
  		    if (first_call) {
  			if (setsig (uex, vvector) == (long) SIG_IGN) {
  			    setsig (uex, SIG_IGN);
  			    ignore_sigint++;
  			}
  			first_call = 0;
  		    } else if (!ignore_sigint) {
  			if (debug_sig)
  			    setsig (uex, SIG_DFL);
  			else
  			    setsig (uex, vvector);
  		    }
  		} else {
  		    if (debug_sig)
  			setsig (uex, SIG_DFL);
  		    else
  			setsig (uex, vvector);
  		}
  	    }
  	}
  
  	return (XOK);
  }
  
  
  /* SETSIG -- Post an exception handler for the given exception.
   */
  static long
  setsig (int code, SIGFUNC handler)
  {
  	struct sigaction sig;
  	long status;
  
  	sigemptyset (&sig.sa_mask);
  #ifdef __APPLE__
  	sig.sa_handler = (SIGFUNC) handler;
  #else
  	sig.sa_sigaction = (SIGFUNC) handler;
  #endif
  	sig.sa_flags = (SA_NODEFER|SA_SIGINFO);
  	status = (long) sigaction (code, &sig, NULL);
  
  	return (status);
  }
  
  
  /* EX_HANDLER -- Called to handle an exception.  Map OS exception into
   * xwhen signal, call user exception handler.  A default exception handler
   * posted by the IRAF Main is called if the user has not posted another
   * handler.  If we get the software termination signal from the CL, 
   * stop process execution immediately (used to kill detached processes).
   */
  void
  ex_handler (
    int  	    unix_signal,
    siginfo_t *info,
    void      *ucp
  )
  {
  	XINT  next_epa, epa, x_vex;
  	int   vex;
  
  	last_os_exception = unix_signal;
          last_os_hwcode = info ? info->si_code : 0;
  
  	x_vex = unix_exception[unix_signal].x_vex;
  	vex = x_vex - X_FIRST_EXCEPTION;	
  	epa = handler_epa[vex];
  
  	/* Reenable/initialize the exception handler.
  	 */
  
          /*  Clears the exception-occurred bits in the FP status register.
           */
          feclearexcept (FE_ALL_EXCEPT);
  
  	/* If signal was SIGINT, cancel any buffered standard output.  */
  	if (unix_signal == SIGINT) {
  	    fcancel (stdout);
  	}
  
  	/* Call user exception handler(s).  Each handler returns with the
  	 * "value" (epa) of the next handler, or X_IGNORE if exception handling
  	 * is completed and processing is to continue normally.  If the handler
  	 * wishes to restart the process, i.e., initiate error recovery, then
  	 * the handler procedure will not return.
  	 */
  	for (next_epa=epa;  next_epa != (XINT) X_IGNORE;
  	    ((SIGFUNC)epa)(&x_vex,&next_epa))
  	        epa = next_epa;
  }
  
  
  /* ZXGMES -- Get the machine dependent integer code and error message for the
   * most recent exception.  The integer code XOK is returned if no exception
   * has occurred, or if we are called more than once.
   */
  int
  ZXGMES (
    XINT	  *os_exception,
    PKCHAR  *errmsg,
    XINT	  *maxch
  )
  {
  	register int	v;
  	char	*os_errmsg;
  
  	*os_exception = last_os_exception;
  
  	if (last_os_exception == XOK)
  	    os_errmsg = "";
  	else {
  	    os_errmsg = unix_exception[last_os_exception].x_name;
  	    if (last_os_exception == SIGFPE) {
  		for (v=0;  hwx_exception[v].v_code != EOMAP;  v++)
  		    if (hwx_exception[v].v_code == last_os_hwcode) {
  			os_errmsg = hwx_exception[v].v_msg;
  			break;
  		    }
  	    }
  	}
  
  	strncpy ((char *)errmsg, os_errmsg, (int)*maxch);
  	((char *)errmsg)[*maxch] = EOS;
  
  	last_os_exception = XOK;
  
  	return (XOK);
  }
  