/* sig6.c (emx+gcc) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <float.h>
#include <process.h>
#if defined (__MT__)
#include <stddef.h>
#define INCL_DOSSEMAPHORES
#include <os2emx.h>
#endif

#if defined (__MT__)

static int threads = 1;
static int active = 1;
static HEV hevSwitch = 0;

struct thread_data
{
  jmp_buf jcont;
};

#define JCONT  (((struct thread_data *)*_threadstore ())->jcont)

#else

static jmp_buf jcont;
#define JCONT jcont

#endif


static void sig_ret (int sig)
{
  signal (sig, SIG_ACK);
  printf ("Signal %d\n", sig);
  fflush (stdout);
}


static void sig_cont (int sig)
{
  signal (sig, SIG_ACK);
  printf ("Signal %d\n", sig);
  fflush (stdout);
#if defined (__MT__)
  active = *_threadid;
#endif
  longjmp (JCONT, 1);
}


static void set_handler (void (*handler)(int sig))
{
  signal (SIGHUP, handler);
  signal (SIGINT, handler);
  signal (SIGILL, handler);
  signal (SIGABRT, handler);
  signal (SIGFPE, handler);
  signal (SIGSEGV, handler);
  signal (SIGTERM, handler);
  signal (SIGBREAK, handler);
}


static void ill_instr (void)
{
  __asm__ ("lock;movl %eax, %eax");
}


static void fp_err (void)
{
  double volatile x;

  _control87 (0, MCW_EM);
  x = 0.0;
  x = 1.0 / x;
}


static int get_signo (const char *s)
{
  if (s[0] == 0 || s[1] != 0)
    return -1;
  switch (*s)
    {
    case 'a':
      return SIGABRT;
      break;
    case 'b':
      return SIGBREAK;
      break;
    case 'f':
      return SIGFPE;
      break;
    case 'h':
      return SIGHUP;
      break;
    case 'i':
      return SIGINT;
      break;
    case 'k':
      return SIGKILL;
      break;
    case 'v':
      return SIGSEGV;
      break;
    default:
      return -1;
    }
}


static void loop (void *arg)
{
  char buf[256], *p;
  int signo;
  sigset_t set;

#if defined (__MT__)
  *_threadstore () = alloca (sizeof (struct thread_data));
#endif
  for (;;)
    {
#if defined (__MT__)
      if (*_threadid != active)
        {
          ULONG post_count;

          do
            {
              DosSleep (200L);
              DosWaitEventSem (hevSwitch, SEM_INDEFINITE_WAIT);
            } while (*_threadid != active);
          DosResetEventSem (hevSwitch, &post_count);
        }
#endif
      if (setjmp (JCONT) != 0)
        printf ("Continue...\n");
#if defined (__MT__)
      printf ("This is thread %d\n", *_threadid);
#endif
      printf ("? "); fflush (stdout);
      if (fgets (buf, sizeof (buf), stdin) == NULL)
        exit (0);
      p = strchr (buf, '\n');
      if (p != NULL) *p = 0;
      switch (buf[0])
        {
        case '?':
          puts ("?       help");
          puts ("q       quit");
          puts ("ef      exception: floating point");
          puts ("ei      exception: illegal instruction");
          puts ("ep      exception: protection violation");
          puts ("hc      set signal handler: continue");
          puts ("hd      set signal handler: default");
          puts ("hi      set signal handler: ignore");
          puts ("hr      set signal handler: return");
          puts ("a       abort()");
          puts ("k<sig>  kill()");
          puts ("r<sig>  raise()");
          puts ("b<sig>  block");
          puts ("u<sig>  unblock");
#if defined (__MT__)
          puts ("tn   create new thread");
          puts ("t#   switch to thread #");
#endif
          puts ("\n<sig>:");
          puts ("a  SIGABRT      b  SIGBREAK     f  SIGFPE");
          puts ("h  SIGHUP       i  SIGINT       k  SIGKILL");
          puts ("v  SIGSEGV");
          break;
        case 'q':
          exit (0);
        case 'a':
          abort ();
          break;
        case 'h':
          switch (buf[1])
            {
            case 'c':
              set_handler (sig_cont);
              break;
            case 'd':
              set_handler (SIG_DFL);
              break;
            case 'i':
              set_handler (SIG_IGN);
              break;
            case 'r':
              set_handler (sig_ret);
              break;
            default:
              printf ("What?\n");
              break;
            }
          break;
        case 'e':
          switch (buf[1])
            {
            case 'p':
              p = (char *)0x0;
              ++*p;
              break;
            case 'f':
              fp_err ();
              break;
            case 'i':
              ill_instr ();
              break;
            default:
              printf ("What?\n");
              break;
            }
          break;
        case 'k':
          signo = get_signo (buf+1);
          if (signo == -1)
            printf ("What?\n");
          else
            kill (getpid (), signo);
          break;
        case 'r':
          signo = get_signo (buf+1);
          if (signo == -1)
            printf ("What?\n");
          else
            raise (signo);
          break;
        case 'b':
        case 'u':
          signo = get_signo (buf+1);
          if (signo == -1)
            printf ("What?\n");
          else
            {
              sigemptyset (&set);
              sigaddset (&set, signo);
              sigprocmask (buf[0] == 'b' ? SIG_BLOCK : SIG_UNBLOCK,
                           &set, NULL);
            }
          break;
#if defined (__MT__)
        case 't':
          if (buf[1] == 'n')
            {
              int tid = _beginthread (loop, NULL, 0x8000, NULL);
              if (tid == -1)
                puts ("_beginthread failed.");
              else
                {
                  ++threads;
                  printf ("New thread %d created.\n", tid);
                }
            }
          else
            {
              int n = atoi (buf + 1);
              if (n >= 1 && n <= threads)
                {
                  active = n;
                  DosPostEventSem (hevSwitch);
                }
              else
                printf ("What?\n");
            }
          break;
#endif
        default:
          printf ("What?\n");
          break;
        }
    }
}


int main (void)
{
#if defined (__MT__)
  DosCreateEventSem (NULL, &hevSwitch, 0, FALSE);
#endif
  loop (NULL);
  return 0;
}
