/**************************************************************************
 *{@C
 *      Copyright:      1988-2024 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         Utilities
 *      Filename:       UT_ErrFatal.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    Signal and fatal error handling, non-local gotos.
 *                      Note that catching signals and fatal errors makes
 *                      a program multithreaded in a way. Signals and errors
 *                      can occur at any time, so a function handling these
 *                      events is likely to see global data structures it may
 *                      want to change, in an inconsistent state. In cases
 *                      like that some locking mechanism must be provided.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      UT_ErrFatal
 *
 **************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

#include "UT_Compat.h"

#if defined (OS_IS_WIN32)
    #include <io.h>
#elif defined (OS_IS_SYS5_UNIX)
    #include <unistd.h>
#endif

#include "UT_Error.h"
#include "UT_ErrPrivate.h"
#include "UT_ErrTexts.h"

/***************************************************************************
 *
 *      Global and static data
 *
 ***************************************************************************/

UT_ErrBase *UT_ErrGlobalList = NULL;                   /* Global UT_ErrBase list */
Int32       UT_ErrNum;                         /* Global error code */
const char *UT_ErrStr;                         /* Error message text */
char        UT_ErrAddStr[UT_ErrAddStrLen + 1]; /* Extra explanatory text */
const char *UT_ErrMathExStr = NULL;            /* Last math exception type */
UT_Bool     UT_ErrInitialized = UT_False;       /* UT_True after first UT_ErrInit */

static void (*fatal_funct) (void) = NULL; /* Fatal-error handler */

/***************************************************************************
 *
 *      Static functions
 *
 ***************************************************************************/

/* Abnormal program termination: If possible, save some information
about the current status of the program for later examination with
a debugger. Then exit. */

#if defined (OS_IS_SYS5_UNIX) || defined (OS_IS_WIN32)

    static void coredump (void)
    {
        (void)fprintf (stderr, "PROGRAM TERMINATING, WILL DUMP CORE.\n\n");
        (void)fflush (stdout);
        (void)fflush (stderr);

        /* Make sure, that abort doesn't return. */

        (void)signal (SIGILL, SIG_DFL);
        #if !defined (OS_IS_WIN32)
            (void)signal (SIGIOT, SIG_DFL);
        #endif

        /* Dump core. */

        (void)abort ();
    }

#else

    #error "Code for abnormal program termination missing here"

#endif


/***************************************************************************
 *[@e
 *      Name:           UT_ErrFatal
 *
 *      Usage:          Handling of cathastrophic errors.
 *
 *      Synopsis:       void UT_ErrFatal (const char *funct, const char *msg)
 *
 *      Description:    Funct should be the name of the function calling
 *                      UT_ErrFatal, and msg should expain what went wrong.
 *
 *                      Handling of cathastrophic errors (Should only be
 *                      called if the program detects internal data
 *                      inconsistencies). A message is printed to file stderr.
 *                      Then the program exits, producing a core dump.
 *
 *      Return value:   This function does not return!
 *
 *      See also:
 *
 ***************************************************************************/

void UT_ErrFatal (const char *funct, const char *msg)
{
    static UT_Bool recursive = UT_False;

    (void)fprintf (stderr,
                   "\n\nFATAL ERROR:\nFUNCTION %s DETECTED "
                   "AN INCONSISTENCY:\n%s\n",
                   funct, msg);
    if (!recursive) {
        recursive = UT_True;
        if (fatal_funct) {
            (*fatal_funct) ();
        }
    }
    coredump ();
}
