/*x.c*/

/*From CLib*/
#include <kernel.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>

/*From OSLib*/
#include "macros.h"
#include "messagetrans.h"
#include "os.h"

/*From Support*/
#include "x.h"

/*The type of a Shared C Library international error block*/
typedef struct {bits defaultnum; char errmess [252]; bits errnum;
      char errtoken [252];} International_Error;

/*A macro to declare them*/
#define LOCAL_INTERNATIONAL_ERROR(id, token, num, mess) \
   static struct {bits defaultnum; char errmess [sizeof mess + 1]; \
         bits errnum; char errtoken [sizeof token + 1];} id ## _ = \
         {num, mess, num, token}; \
   static International_Error *id = (International_Error *) &id ## _;

/*2 international error blocks (for use with Copy_Error())*/
LOCAL_INTERNATIONAL_ERROR (Error_Stack, "C64", error_STK_OFLO, "Not "
      "enough memory, allocate failed")
LOCAL_INTERNATIONAL_ERROR (Error_Unknown, "C35", 1, "No error (errno = "
      "0)")

/*2 error blocks with tokens in (for use with MessageTrans).*/
x_LOCAL_ERROR (Error_Escape, error_ESCAPE, "Escape")
x_LOCAL_ERROR (Error_No_Memory, os_GLOBAL_NO_MEM, "NoMem")

static int Signals [] = {SIGINT, SIGSTAK, SIGOSERROR};
/*------------------------------------------------------------------------*/
static os_error *Copy_Error (International_Error *ie)

   /*This code is what the SharedCLibrary does, except that this happens
      every time a message is to be looked up, and the module only opens
      the file at most once. We don't really care about this.*/

{  messagetrans_control_block cb;
   os_error *e;

   if (xmessagetrans_open_file (&cb, "SharedCLibrary:Messages", 0) ==
         NULL)
   {  e = xmessagetrans_error_lookup ((os_error *) &ie->errnum, &cb,
            NULL, 0, "SharedCLibrary", NULL, NULL, NULL);
      xmessagetrans_close_file (&cb);
   }
   else
      e = (os_error *) &ie->defaultnum;

   return e;
}
/*------------------------------------------------------------------------*/
static void Try_Handler (int sig, x_exception *x)

   /*Handler for use after x_try().*/

{  os_error *error = NULL /**/;

   /*Save this condition in the buffer.*/
   switch (sig)
   {  case SIGINT:
         error = xmessagetrans_error_lookup (Error_Escape, NULL, NULL,
               0, SKIP, SKIP, SKIP, SKIP);
      break;

      case SIGSTAK:
         error = Copy_Error (Error_Stack);
      break;

      case SIGOSERROR:
         error = x__last_error ();
      break;
   }

   /*Longjump back to the x_TRY() call.*/
   longjmp (x->buf, (int) (x->error = error));
}
/*------------------------------------------------------------------------*/
void x__try (x_exception *x)

{  int sig;

   /*trampoline*/
#if 0
   /*branch using B*/
   x->trampoline [0] = 0xE24F1008;
      /*i e, 'ADR R1, x,' which is the same as 'SUB R1, PC, #8'*/
   x->trampoline [1] = 0xEA000000 | ((int) &Try_Handler -
         (int) &x->trampoline [1] - 8 >> 2 & 0xFFFFFF);
      /*i e, 'B Try_Handler'*/
#else
   /*branch using LDR PC*/
   x->trampoline [0] = 0xE24F1008;
      /*i e, 'ADR R1, x,' which is the same as 'SUB R1, PC, #8'*/
   x->trampoline [1] = 0xE591F008;
      /*i e, 'LDR PC, [R1, #8]'*/
   x->trampoline [2] = (int) &Try_Handler;
      /*i e, '& Try_Handler'*/
#endif

   /*previous*/
   for (sig = 0; sig < COUNT (Signals); sig++)
      if ((x->previous [sig] = signal (Signals [sig],
            (void (*) (int)) x)) == SIG_ERR)
         x->previous [sig] = NULL;

   /*error*/
   x->error = NULL;

   /*buf*/
   /*Done in calling macro.*/
}
/*------------------------------------------------------------------------*/
void x__catch (x_exception *x)

{  int sig;

   /*Restore the caller's handlers.*/
   for (sig = 0; sig < COUNT (Signals); sig++)
      if (x->previous [sig] != NULL)
         (void) signal (Signals [sig], x->previous [sig]);
}
/*------------------------------------------------------------------------*/
os_error *x__last_error (void)

   /*Messing about here because _kernel_last_oserror() returns NULL the
      second time it's called*/

{  os_error *last_error;

   static os_error *Last_Error;

   if ((last_error = (os_error *) _kernel_last_oserror ()) != NULL)
      Last_Error = last_error;

   if (Last_Error == NULL)
      Last_Error = Copy_Error (Error_Unknown);

   return Last_Error; /*never NULL*/
}
/*------------------------------------------------------------------------*/
void *x__alloc (int size)

{  void *ptr;

   if ((ptr = malloc (size)) == NULL && size != 0)
      messagetrans_error_lookup (Error_No_Memory, NULL, NULL, 0, SKIP,
            SKIP, SKIP, SKIP);

   return ptr;
}
/*------------------------------------------------------------------------*/
void *x__calloc (int count, int size)

{  void *ptr;

   if ((ptr = calloc (count, size)) == NULL && size != 0 && count != 0)
      messagetrans_error_lookup (Error_No_Memory, NULL, NULL, 0, SKIP,
            SKIP, SKIP, SKIP);

   return ptr;
}
/*------------------------------------------------------------------------*/
void x__free (void *ptr, int size) {NOT_USED (size) free (ptr);}
/*------------------------------------------------------------------------*/
void *x__realloc (void *ptr, int old_size, int size)

{  NOT_USED (old_size)
   NOT_USED (size)

   if ((ptr = realloc (ptr, size)) == NULL && size != 0)
      messagetrans_error_lookup (Error_No_Memory, NULL, NULL, 0, SKIP,
            SKIP, SKIP, SKIP);

   return ptr;
}
