/* sys/init.c (emx+gcc) -- Copyright (c) 1992-1993 by Eberhard Mattes */

#include <sys/emx.h>
#define INCL_DOS
#include <os2emx.h>
#include <signal.h>
#include <string.h>

#define SYS_PRIVATE_HEAP_SIZE 0x100000

#define EXTERN
#define INIT(x) = x

#include "syscalls.h"

static int arg_size;
static int env_size;
static int argc;
static int envc;
static char **vec;
static char *pool;

extern void volatile __init_ret (void *stack);
static void parse_args (const char *src);
static void parse_env (const char *src);

#define PUTC(c) BEGIN ++arg_size; if (pool != NULL) *pool++ = (c); END
#define PUTV    BEGIN ++argc; if (vec != NULL) *vec++ = pool; END

#define WHITE(c) ((c) == ' ' || (c) == '\t')

static void parse_args (const char *src)
{
  int bs, quote;
  char *flag_ptr;

  argc = 0; arg_size = 0;
  /* argv[0] */
  PUTC (0x80);
  PUTV;
  for (;;)
    {
      PUTC (*src);
      if (*src == 0)
        break;
      ++src;
    }
  ++src;
  for (;;)
    {
      while (WHITE (*src))
        ++src;
      if (*src == 0)
        break;
      flag_ptr = pool;
      PUTC (0x80);
      PUTV;
      bs = 0; quote = 0;
      for (;;)
        {
          if (*src == '"')
            {
              while (bs >= 2)
                {
                  PUTC ('\\');
                  bs -= 2;
                }
              if (bs & 1)
                PUTC ('"');
              else
                {
                  quote = !quote;
                  if (flag_ptr != NULL)
                    *flag_ptr |= 1;
                }
              bs = 0;
            }
          else if (*src == '\\')
            ++bs;
          else
            {
              while (bs != 0)
                {
                  PUTC ('\\');
                  --bs;
                }
              if (*src == 0 || (WHITE (*src) && !quote))
                break;
              PUTC (*src);
            }
          ++src;
        }
      PUTC (0);
    }
}


static void parse_env (const char *src)
{
  envc = 0; env_size = 0;
  while (*src != 0)
    {
      ++envc;
      if (vec != NULL)
        *vec++ = pool;
      for (;;)
        {
          ++env_size;
          if (pool != NULL)
            *pool++ = *src;
          if (*src == 0)
            break;
          ++src;
        }
      ++src;
    }
}


struct layout
{
  ULONG text_base;
  ULONG text_end;
  ULONG data_base;
  ULONG data_end;
  ULONG bss_base;
  ULONG bss_end;
  ULONG heap_base;
  ULONG heap_end;
  ULONG heap_brk;
  ULONG heap_off;
  ULONG os2_dll;
  ULONG stack_base;
  ULONG stack_end;
  ULONG flags;
  ULONG reserved[2];
  UCHAR options[64];
};


void __init (struct layout *layout)
{
  ULONG rc;
  PTIB ptib;
  PPIB ppib;
  void *stack;
  int n;
  ULONG times;
  thread_data *tp;

  if (layout->flags & 1)        /* Do nothing if we're a DLL */
    return;

  /* Get current time for clock(). */

  _sys_get_clock (&_sys_clock0_ms);

  /* Copy command line arguments and environment. */

  rc = DosGetInfoBlocks (&ptib, &ppib);
  if (rc != 0)
    DosExit (EXIT_PROCESS, 255);
  vec = NULL; pool = NULL;
  parse_env (ppib->pib_pchenv);
  parse_args (ppib->pib_pchcmd);
  n = arg_size + env_size + (argc+1+envc+1) * sizeof (char *);
  if (n & 3)
    n = (n | 3) + 1;            /* alignment for EXCEPTIONREGISTRATIONRECORD */
  stack = alloca (n + sizeof (EXCEPTIONREGISTRATIONRECORD));
  _sys_xreg = stack + n;
  vec = stack;
  pool = (char *)(vec + argc+1+envc+1);
  parse_env (ppib->pib_pchenv);
  *vec++ = NULL;               /* empty environment */
  parse_args (ppib->pib_pchcmd);
  *vec++ = NULL;

  /* Initialize the heap. */

  _sys_heap_size = 0x2000000;
  rc = DosAllocMem (&_sys_heap_base, _sys_heap_size, PAG_READ|PAG_WRITE);
  if (rc != 0)
    DosExit (EXIT_PROCESS, 255);
  _sys_heap_brk = _sys_heap_base;
#if defined (__MT__)
  DosCreateMutexSem (NULL, &_sbrk_mutex, 0, FALSE);
#endif

  /* Initialize threads. */
#if defined (__MT__)
  rc = DosAllocMem (&_sys_private_heap, SYS_PRIVATE_HEAP_SIZE,
                    PAG_READ | PAG_WRITE);
  if (rc != 0)
    DosExit (EXIT_PROCESS, 255);
  rc = DosSubSetMem (_sys_private_heap,
                     DOSSUB_INIT | DOSSUB_SPARSE_OBJ | DOSSUB_SERIALIZE,
                     SYS_PRIVATE_HEAP_SIZE);
  if (rc != 0)
    DosExit (EXIT_PROCESS, 255);
  for (n = 0; n < MAX_THREADS; ++n)
    _sys_thread_table[n] = NULL;
  __newthread (ptib->tib_ptib2->tib2_ultid);
#else
  _sys_init_thread (&_sys_thread_one);
#endif
  tp = SYS_THREAD;

  /* Initialize exception handling. */

  _sys_xreg->prev_structure = (void *)0xffffffff;
  _sys_xreg->ExceptionHandler = _sys_exception;
  rc = DosSetExceptionHandler (_sys_xreg);
  if (rc != 0)
    DosExit (EXIT_PROCESS, 255);
  /* DosSetSignalExceptionFocus fails for PM programs, error code 303 */
  DosSetSignalExceptionFocus (SIG_SETFOCUS, &times);

  /* Return to the program. */

  __init_ret (stack);
}

enum sig_type
{
  ST_TERM,                      /* Terminate process (SIGINT for instance) */
  ST_NEXT,                      /* Pass on to next exception handler */
  ST_IGNORE                     /* Ignore exception and continue thread */
};

struct sig_descr
{
  const char *name;             /* Name of the signal */
  enum sig_type dfl_action;     /* Default action */
  enum sig_type ign_action;     /* Action taken for SIG_IGN */
};

char const _sys_sig_valid[NSIG] =
{
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1
};

static struct sig_descr const sig_info[NSIG] =
{
  {"SIG0",     ST_TERM,   ST_TERM},
  {"SIGHUP",   ST_TERM,   ST_IGNORE},
  {"SIGINT",   ST_TERM,   ST_IGNORE},
  {"SIGQUIT",  ST_TERM,   ST_IGNORE},
  {"SIGILL",   ST_NEXT,   ST_NEXT},
  {"SIGTRAP",  ST_NEXT,   ST_NEXT},
  {"SIGABRT",  ST_TERM,   ST_TERM},
  {"SIGEMT",   ST_TERM,   ST_TERM},
  {"SIGFPE",   ST_NEXT,   ST_NEXT},
  {"SIGKILL",  ST_TERM,   ST_TERM},
  {"SIGBUS",   ST_TERM,   ST_TERM},
  {"SIGSEGV",  ST_NEXT,   ST_NEXT},
  {"SIGSYS",   ST_TERM,   ST_TERM},
  {"SIGPIPE",  ST_TERM,   ST_IGNORE},
  {"SIGALRM",  ST_TERM,   ST_IGNORE},
  {"SIGTERM",  ST_NEXT,   ST_NEXT},
  {"SIGUSR1",  ST_IGNORE, ST_IGNORE},
  {"SIGUSR2",  ST_IGNORE, ST_IGNORE},
  {"SIGCLD",   ST_IGNORE, ST_IGNORE},
  {"SIG19",    ST_TERM,   ST_TERM},
  {"SIG20",    ST_TERM,   ST_TERM},
  {"SIGBREAK", ST_TERM,   ST_IGNORE}
};


static void say (const char *msg)
{
  __write (2, msg, strlen (msg));
};


ULONG _sys_signal (int sig, int os_exc)
{
  enum sig_type action;
  thread_data *tp;

  if (sig < 1 || sig >= NSIG)
    return (XCPT_CONTINUE_SEARCH);
  tp = SYS_THREAD;
  if (tp == NULL)
    return (XCPT_CONTINUE_SEARCH);
  if (tp->sig_handlers[sig] == SIG_IGN)
    {
      if (os_exc)
        action = sig_info[sig].ign_action;
      else
        action = ST_IGNORE;
    }
  else if (tp->sig_handlers[sig] == SIG_DFL)
    action = sig_info[sig].dfl_action;
  else
    {
      tp->sig_handlers[sig] (sig);
      action = sig_info[sig].ign_action;
    }
  if (!os_exc && action == ST_NEXT)
    action = ST_TERM;
  switch (action)
    {
    case ST_TERM:
      if (sig == SIGABRT)
        say ("\r\nAbnormal program termination\r\n");
      else
        {
          say ("\r\nProcess terminated by ");
          say (sig_info[sig].name);
          say ("\r\n");
        }
      while (1)
        DosExit (EXIT_PROCESS, 3);
    case ST_NEXT:
      return (XCPT_CONTINUE_SEARCH);
    case ST_IGNORE:
      return (XCPT_CONTINUE_EXECUTION);
    }
  return (XCPT_CONTINUE_SEARCH); /* Keep the optimizer happy */
}


ULONG _sys_exception (PEXCEPTIONREPORTRECORD report,
                      PEXCEPTIONREGISTRATIONRECORD registration,
                      PCONTEXTRECORD context,
                      PVOID whatever)
{
  __asm__ ("cld");              /* Don't trust */
  if (report->fHandlerFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
    return (XCPT_CONTINUE_SEARCH);
  switch (report->ExceptionNum)
    {
    case XCPT_SIGNAL:
      if (report->cParameters >= 1)
        {
          if (report->ExceptionInfo[0] == XCPT_SIGNAL_INTR)
            return (_sys_signal (SIGINT, FALSE));
          else if (report->ExceptionInfo[0] == XCPT_SIGNAL_KILLPROC)
            return (_sys_signal (SIGTERM, FALSE));
          else if (report->ExceptionInfo[0] == XCPT_SIGNAL_BREAK)
            return (_sys_signal (SIGBREAK, FALSE));
        }
      break;
    case XCPT_ACCESS_VIOLATION:
    case XCPT_DATATYPE_MISALIGNMENT:
      return (_sys_signal (SIGSEGV, TRUE));

    case XCPT_INTEGER_DIVIDE_BY_ZERO:
    case XCPT_INTEGER_OVERFLOW:
    case XCPT_ARRAY_BOUNDS_EXCEEDED:
    case XCPT_FLOAT_DENORMAL_OPERAND:
    case XCPT_FLOAT_DIVIDE_BY_ZERO:
    case XCPT_FLOAT_INEXACT_RESULT:
    case XCPT_FLOAT_INVALID_OPERATION:
    case XCPT_FLOAT_OVERFLOW:
    case XCPT_FLOAT_STACK_CHECK:
    case XCPT_FLOAT_UNDERFLOW:
      return (_sys_signal (SIGFPE, TRUE));

    case XCPT_ILLEGAL_INSTRUCTION:
    case XCPT_INVALID_LOCK_SEQUENCE:
    case XCPT_PRIVILEGED_INSTRUCTION:
      return (_sys_signal (SIGILL, TRUE));
    }
  return (XCPT_CONTINUE_SEARCH);
}


void _sys_get_clock (unsigned long *ms)
{
  ULONG val_ms;

  DosQuerySysInfo (QSV_MS_COUNT, QSV_MS_COUNT, &val_ms, sizeof (val_ms));
  *ms = val_ms;
}


void _sys_init_thread (thread_data *tp)
{
  int n;

  for (n = 0; n < NSIG; ++n)
    {
      tp->sig_handlers[n] = SIG_DFL;
      tp->sig_ack_req[n] = 0;
      tp->sig_pending[n] = 0;
    }
}
