/*----------------------------------------*
 | File: ERROR.c - MLO 900131 V1.00       |
 | These routines perform error handling  |
 | for mathematical errors, as described  |
 | in the Lattice C library reference     |
 | manual; AND set MathError to True,     |
 | displaying a requester about the error |
 *----------------------------------------*/

#include "rpn.h"
#include "herror.h"
#include <error.h>
#include <math.h>
#include <signal.h>

extern int errno;
extern int _FPERR;
extern int (*_SIGFPE)();
extern Boolean MathError;
extern struct Window *Wrpn;

static void display(char *pc1, char *pc2);

int matherr(
  struct exception *x
)
{/*------------------------------------*
  | Error from high-level mathematical |
  | functions. Determines the routine  |
  | name and the error type, then      |
  | displays a requester.              |
  *------------------------------------*/

  char slate[SLATE_DIM];

  sprintf(slate, "routine \"%s\"", x->name);
  switch (x->type) {
    case DOMAIN:
      errno = EDOM;
      display(slate, "(domain error)");
      break;
    case SING:
      errno = EDOM;
      display(slate, "(singularity)");
      break;
    case OVERFLOW:
      errno = ERANGE;
      display(slate, "(floating overflow)");
      break;
    case UNDERFLOW:
      errno = ERANGE;
      display(slate, "(floating underflow)");
      break;
    case TLOSS:
      errno = ERANGE;
      display(slate, "(total loss of significance)");
      break;
    case PLOSS:
      errno = ERANGE;
      display(slate, "(partial loss of significance)");
      break;
    default:
      errno = ERANGE;
      display(slate, NULL);
      break;
  }
  return 0;
}

void __stdargs CXFERR(
  int code
)
{/*-------------------------------------------------------*
  | Error from low-level floating point operations.       |
  | Determines the error type, then displays a requester. |
  *-------------------------------------------------------*/

  static char llfp[] = "low-level floating operation";
  
  _FPERR = code;
  if ((void *) _SIGFPE != (void *) SIG_DFL   &&
      (void *) _SIGFPE != (void *) SIG_IGN)             (*_SIGFPE)(SIGFPE);
  switch (code) {
    case FPEUND:
      display(llfp, "(floating underflow)");
      break;
    case FPEOVF:
      display(llfp, "(floating overflow)");
      break;
    case FPEZDV:
      display(llfp, "(division by zero)");
      break;
    case FPENAN:
      display(llfp, "(not a number)");
      break;
    case FPECOM:
      display(llfp, "(not comparable)");
      break;
    default:
      display(llfp, NULL);
      break;
  }
}

static void display(
  char *pc1,                    /* First line to be displayed */
  char *pc2                     /* Second line, or NULL */
)
{/*---------------------------------------------*
  | Local function. Displays the requester with |
  | an header followed by the given text. Also  |
  | sets to True the global variable MathError. |
  *---------------------------------------------*/

  struct IntuiText IT3 = {
    BLUE_PEN, WHITE_PEN, JAM2, IT3X, IT3Y, NULL,
    NULL, NULL
  };

  struct IntuiText IT2 = {
    BLUE_PEN, WHITE_PEN, JAM2, IT2X, IT2Y, NULL,
    NULL, NULL
  };

  struct IntuiText IT1 = {
    BLUE_PEN, WHITE_PEN, JAM2, IT1X, IT1Y, NULL,
    "Mathematical error in", NULL
  };

  struct IntuiText ITOK = {
    BLUE_PEN, WHITE_PEN, JAM2, ITOKX, ITOKY, NULL,
    "OK", NULL
  };

  IT1.NextText = &IT2;
  IT2.IText = pc1;
  if (pc2 != NULL) {
    IT3.IText = pc2;
    IT2.NextText = &IT3;
  } else {
    IT2.NextText = NULL;
  }
  AutoRequest(Wrpn, &IT1, NULL, &ITOK, 0, 0, ITWID, ITHEI);
  MathError = True;
}
