/*
 * Posixly/SysV-correct signal handling for NetWare.
 * Written by Cyrus Patel <cyp@fb14.uni-mainz.de>
*/
#if 0 /* No longer needed. SIGTERM is done from prelude.c now */

#if (!defined(lint) && defined(__showids__))
const char *nwsig_cpp(void) {
return "@(#)$Id: nwlsig.cpp,v 1.1.2.1 1999/11/14 20:42:58 cyp Exp $"; }
#endif

#define MAX_SECS_TO_EXIT_ON_SIGRECPT 20

static void *__setsignal( int sig, void *func ); /* forward reference */
static void __clib_SIG_DFL( int sig ); /* forward reference */
static void _install_deinstall_handler(int which); /* forward ref */

#ifdef __cplusplus
extern "C" {
#endif
#include <signal.h>   /* SIG*, signal(), raise() */
#include <string.h>   /* memset() */
#include <unistd.h>   /* _exit() */
#include <stdlib.h>   /* exit() */
#include <process.h>  /* BeginThread(), [Get|Set]ThreadGroupID() */

extern void (*PreludeSetOnStopHook(void (*)(void)))(void); /* prelude.c */
extern void CYieldUntilIdle(void); /* kernel or emulated */
extern void CYieldWithDelay(void); /* kernel or emulated */
extern void ConsolePrintf(char *format, ...); /* grrrr */
extern void *ImportPublicSymbol(int NLMHandle, const char *lname); 
extern void Abend(const char *abendmsg);

void (*signal( int sig, auto void (*func)( int ) ))(int)
{ return (void (*)(int))__setsignal( sig, (void *)func ); }
#ifdef __cplusplus
}
#endif

#ifndef NSIG
#ifdef SIGMAX
#define NSIG SIGMAX
#else
#define NSIG 8 /* 0,ABRT,FPE,ILL,INT,SEGV,TERM,POLL */
#endif
#endif

#if (NSIG != 8)
#error whoops. API has changed, wonder of wonders.
#endif

const char *sys_signame[NSIG] =
{
  "Unknown signal",  /* we can set this, but CLIB won't raise()  */
  "Abort termination",
  "Floating-point exception",
  "Illegal instruction",
  "User Interrupt",
  "Segment violation",
  "Software termination signal",
  "Poll signal"
};

static struct   { int signo; void (*defaction)(int); void (*clibact)(int); } 
siglist[NSIG]={ {   0,       SIG_ERR, SIG_IGN        }, /* not settable */
/* SIGABRT=1 */ { SIGABRT,   SIG_DFL, __clib_SIG_DFL }, 
/* SIGFPE =2 */ { SIGFPE,    SIG_DFL, __clib_SIG_DFL }, 
/* SIGILL =3 */ { SIGILL,    SIG_DFL, __clib_SIG_DFL }, 
/* SIGINT =4 */ { SIGINT,    SIG_IGN, SIG_IGN        }, 
/* SIGSEGV=5 */ { SIGSEGV,   SIG_DFL, __clib_SIG_DFL }, 
/* SIGTERM=6 */ { SIGTERM,   SIG_DFL, SIG_IGN        }, 
/* SIGPOLL=7 */ { SIGPOLL,   SIG_IGN, __clib_SIG_DFL } 
              };

static struct
{
  void (*(*csignal)( int, void (*)(int)))(int);
  //void (*csignal( int sig, void (*)(int)))(int); 
  void (*sighandlers[NSIG])(int);
  int sigownerTGID[NSIG];
  int exitseen;
  char nlmName[32];
  int defThreadGroupID;
  int nlmHandle;
  int nlmID;
} sigstatics = {signal,{0},{0},0,{0},0,0,0};


static int __spinoffsig(int sig)
{
  int tgid = sigstatics.sigownerTGID[sig];
  void (*func)(int) = sigstatics.sighandlers[sig];
  int rc = -1;

  if (tgid != 0 && tgid != -1)
  {
    if (func && func != SIG_DFL && func != SIG_ERR && func != SIG_IGN)
    {
      if ((tgid = SetThreadGroupID( tgid )) != -1)
      {
        sigstatics.sighandlers[sig] = siglist[sig].defaction;
        sigstatics.sigownerTGID[sig] = 0;
//ConsolePrintf("sig spinoff: %s %p\r\n", sys_signame[sig], func );
        rc = 0;
        if (sig == SIGPOLL) /* the exception */
        {
          (*func)(sig);
        }
        else if (-1 == BeginThread( ((void (*)(void *))func), NULL, 16384, ((void *)sig) ))
        {
          rc = -1;
          sigstatics.sighandlers[sig] = func;
          sigstatics.sigownerTGID[sig] = tgid;
        }
        SetThreadGroupID( tgid );
        if (rc == 0)
          CYieldWithDelay(); /* let it start (and possibly finish) */
//ConsolePrintf("sig spinoff end %d\r\n", rc );
      }
    }
  }
  return rc;
}


static void *__setsignal( int sig, void *func )
{
  if ((sig > 0 && sig < NSIG) && (func && func != SIG_ERR))
  {
    void (*oldfunc)(int) = sigstatics.sighandlers[sig];
    int tgid = GetThreadGroupID();
    if (tgid == -1 || tgid == 0) 
    {
      if (func && func != SIG_IGN && func != SIG_DFL && func != SIG_ERR)
        return SIG_ERR;
      tgid = 0;
    }    
    sigstatics.sighandlers[sig] = (void (*)(int))func;
    sigstatics.sigownerTGID[sig] = tgid;
    return (void *)oldfunc;
  }
  /* errno = EINVAL; */
  return (void *)SIG_ERR;
}



int raise(int sig)
{
  int rc = -1;
  if (sig > 0 && sig < NSIG && sigstatics.exitseen == 0)
  {
    void (*func)(int) = sigstatics.sighandlers[sig];
    if (func == SIG_DFL)
    {
      rc = 0;
      if (siglist[sig].defaction == SIG_DFL) /* else sig_ign */
      {
        sigstatics.exitseen = 1;
//ConsolePrintf("%s\r\n", sys_signame[sig]);
        exit(sig);
      }
    }
    else if (func == SIG_IGN)
      rc = 0;
    else if (func && func != SIG_ERR)
      rc = __spinoffsig(sig);
  }
  return rc;
}  

static int __waitforexit(unsigned long secs)
{
  unsigned long lasttime = 0;
  unsigned long ticks = ((secs*182)/10);
  while (sigstatics.exitseen == 0 && ticks)
  {
    unsigned long timenow = GetCurrentTime();
    if (timenow != lasttime)
      ticks--;
    lasttime = timenow;
    CYieldWithDelay();
  }  
  return sigstatics.exitseen;
}


void abort(void)
{
  int grid = GetThreadGroupID();
  if (grid == -1 || grid == 0)
  {
    int rc;
    ConsolePrintf("\a%s called abort() without CLIB context!\r\n", 
                        sigstatics.nlmName );
    /*
    grid = SetThreadGroupID(sigstatics.defThreadGroupID);
    BeginThread( ((void (*)(void *))_exit), NULL, 0, ((void *)0x03) );
    SetThreadGroupID(grid);
    */
    /* normal CLIB Abend()s here */
    rc = 0;
    while (!rc) /* forever */
    {
      SuspendThread(GetThreadID()); /* this may trip an abend too */
      Abend("");
    }
  }
                                                              
  ConsolePrintf("ABNORMAL NLM TERMINATION in: %s\r\n",sigstatics.nlmName);
  
  if (!sigstatics.exitseen)
  {
    void (*func)(int) = sigstatics.sighandlers[SIGABRT];
    if (func && func != SIG_IGN && func != SIG_DFL && func != SIG_ERR)
    {
      int tgid = sigstatics.sigownerTGID[SIGABRT];
      if (tgid != -1 && tgid != 0)
      {
        tgid = SetThreadGroupID(tgid);
        (*func)(SIGABRT);
        SetThreadGroupID(tgid);
      }
    }
  }
  if (sigstatics.exitseen)
    SuspendThread(GetThreadID());
  _exit(3);
}

static void __onstophook(void)
{
  //ConsolePrintf("%d on_stop_hook() -> %p %lu\r\n", sigstatics.exitseen, sigstatics.sighandlers[SIGTERM], GetCurrentTime() );
  if (sigstatics.exitseen == 0)
  {
    if (__spinoffsig(SIGTERM) != -1)
      __waitforexit(MAX_SECS_TO_EXIT_ON_SIGRECPT);
  }
  //ConsolePrintf("%d on_stop_hook() %lu\r\n", sigstatics.exitseen, GetCurrentTime() );
  return;
}  


static void __clib_SIG_DFL(int sig)
{
  if (sigstatics.exitseen == 0)
  {
    //ConsolePrintf("%d __clib_SIG_DFL(%s) -> %p %lu\r\n", sigstatics.exitseen, sys_signame[sig], sigstatics.sighandlers[sig], GetCurrentTime() );
    if (sig == SIGTERM) /* should not happen! */
      _install_deinstall_handler(-1);
    else if (raise(sig) == 0 && sig != SIGPOLL)
      __waitforexit(MAX_SECS_TO_EXIT_ON_SIGRECPT);
    //ConsolePrintf("%d __clib_SIG_DFL(%s) -> %p %lu\r\n", sigstatics.exitseen, sys_signame[sig], sigstatics.sighandlers[sig], GetCurrentTime() );
  }    
  return;
}  

static void _install_deinstall_handler(int which)
{
  static int sigstatics_initseen = -1;
  int sig;

  if (which > 0)
  {
//ConsolePrintf("begin install sig handler\r\n");    
    memset((void *)&sigstatics,0,sizeof(sigstatics));
    if (sigstatics_initseen < 0)
    {
      void *sigproc;

      sigstatics.defThreadGroupID = GetThreadGroupID();
      if (sigstatics.defThreadGroupID == -1)
        sigstatics.defThreadGroupID = 0;
      if (sigstatics.defThreadGroupID==0)
      {
        ConsolePrintf("\asignal initialization called without CLIB context.\r\n"
                      "\aThe offending thread has been suspended.\r\n");
        SuspendThread(GetThreadID()); /* Yaaaay! for wierdness */
        return;
      }
      sigstatics.nlmHandle = GetNLMHandle();
      sigstatics.nlmID = GetNLMID();
      if (GetNLMNameFromNLMID( sigstatics.nlmID, sigstatics.nlmName, 0) == -1)
        strcpy(sigstatics.nlmName, "???.NLM" );
      

//ConsolePrintf("begin ImportSymbol(signal)\r\n");    
      sigproc = ImportPublicSymbol( GetNLMHandle(), "\x06""signal" );
//ConsolePrintf("end ImportSymbol(signal) -> %p\r\n", sigproc);    
      if (sigproc == NULL)
      {
        sigstatics_initseen = 0;
        ConsolePrintf("Unable to initialize signal handler\r\n");
        _exit(3);
      }
      else
      {
        sigstatics_initseen = +1;
        sigstatics.csignal = (void (*(*)(int,void(*)(int)))(int))sigproc;
//ConsolePrintf("begin sig init table %d\r\n",sigstatics_initseen);    
        for (sig=1; sig<NSIG; sig++)
        {
          sigstatics.sighandlers[sig] = siglist[sig].defaction = 
            (*(sigstatics.csignal))( sig, siglist[sig].clibact );
        }   
//ConsolePrintf("end sig init table\r\n");    
//ConsolePrintf("begin PreludeSetOnStop\r\n");    
        PreludeSetOnStopHook(__onstophook);
//ConsolePrintf("end PreludeSetOnStop\r\n");    
      }
    }
//ConsolePrintf("end install sig handler\r\n");    
  }
  else /* if (which < 0) */
  {
//ConsolePrintf("beginning deinstall sig handler %d %d\r\n", sigstatics.exitseen, sigstatics_initseen);
    sigstatics.exitseen = 1;
    if (sigstatics_initseen > 0)
    {
      int grid = GetThreadGroupID();
      if (grid != 0 && grid != -1)
      {
        for (sig=1; sig<NSIG; sig++)
        {
          sigstatics.sighandlers[sig] = SIG_IGN;
          #if 0 /* may not have CLIB context */
          (*(sigstatics.csignal))( sig, SIG_IGN);
          #endif
        }
      }
      UnImportPublicSymbol( GetNLMHandle(), "\x06""signal" );
      PreludeSetOnStopHook(NULL);
    }
    sigstatics_initseen = -1;
//ConsolePrintf("end deinstall sig handler %d %d\r\n", sigstatics.exitseen, sigstatics_initseen );
  }
  return;
}  

static void _installhandler(void)   { _install_deinstall_handler(+1); }
static void _deinstallhandler(void) { _install_deinstall_handler(-1); }

#if defined(__WATCOMC__)
  #pragma pack(1)
  struct ib_data { char resfield; char level; void (*proc)(void); };
  #pragma pack()
  #pragma data_seg ( "XIB" );
  #pragma data_seg ( "XI" );
  struct ib_data __sig_xi__ = { 0, 40, _installhandler };
  #pragma data_seg ( "XIE" );
  #pragma data_seg ( "YIB" );
  #pragma data_seg ( "YI" );
  struct ib_data __sig_yi__ = { 0, 255, _deinstallhandler };
  #pragma data_seg ( "YIE" );
  #pragma data_seg ( "_DATA" );
#elif defined(__cplusplus)
  static class sigboot {public: sigboot(){_installhandler();}
                               ~sigboot(){_deinstallhandler();} _sigboot_;
#else
  #error Do something about this...                               
#endif

#endif