/*
    Clif - A C-like Interpreter Framework
    Copyright (C) 1992 - 1998 T. Hruz, L. Koren

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

/*
 * inter_handl_posix.c
 *
 * Functions for framework interrupt handling.
 */

#include "global.h"
#include "config.h"
#include <stdio.h>

#include <signal.h>
#include <termios.h>
#include <setjmp.h>

#define OFF(x, y) (x) & (~(y))
#define ON(x, y) (x) | (y)
#define NL '\n'

int handler = 0;
static int handle_fd;
static struct termios term, term_initial;
static struct sigaction act, oact;

RETSIGTYPE (*interrupt_handler) PROTO((int));
RETSIGTYPE interrupt_service PROTO((int));
void interrupt_register PROTO((void));
void term_restore PROTO((void));

RETSIGTYPE fatal_handler PROTO((int));
void fatal_handler_register PROTO((void));
extern jmp_buf jmpbuf;
extern int error_count;

/*
 * Asynchronous interrupt service.
 */
RETSIGTYPE
interrupt_service (signo)
  int signo;
{
#ifdef DEBUG_INTER 
  printfx ("interrupt\n");
#endif
  if (clif_interrupt_level || ! virtual_machine_suspended)

/* 
 * Test of the virtual machine running and the level of interrupt.
 * An interrupt is only accepted if the virtual machine is running.
 */

    {
#ifdef DEBUG_INTER 
      printfx ("virtual machine is running, interrupt accepted\n");
#endif
      handler = 1;
    }
  return;
}


/*
 * Registers interrupt handler.
 */
void 
interrupt_register ()
{
  interrupt_handler = interrupt_service;
  handle_fd = fileno (stdin);
  if (tcgetattr (handle_fd, &term) != 0)
    perror ("tcgetattr");
  term_initial = term;
  term.c_cc[0] = 0x14;		/* DC4 */
  term.c_cc[5] = 0x12;		/* DC2 */
  if (tcsetattr (handle_fd, TCSANOW, &term) != 0)
    perror ("tcsetattr");
  act.sa_handler = interrupt_handler;
  sigemptyset (&act.sa_mask);
  act.sa_flags = 0;
#ifdef SA_INTERRUPT /* SunOS */
  act.sa_flags |= SA_INTERRUPT;
#endif
  if (sigaction (SIGINT, &act, &oact) < 0)
    error_message (4004);
}

/*
 * Synchronous interrupt service.
 */
void 
interrupt_service_sync ()
{
  handler = 1;
}

/*
 * Restores setting of the terminal at the termination of Clif session.
 */
void 
term_restore ()
{
  tcsetattr (handle_fd, TCSANOW, &term_initial);
}

RETSIGTYPE
fatal_handler (signo)
  int signo;
{
  if (signo != SIGFPE && signo != SIGSEGV)
    return;
  if (error_count)
    longjmp (jmpbuf, 1);

  {
    struct sigaction act, oact;
    act.sa_handler = SIG_DFL;
    sigemptyset (&act.sa_mask);
    act.sa_flags = SA_RESETHAND;
    sigaction (signo, &act, &oact);
  }
}

void
fatal_handler_register ()
{
  struct sigaction act, oact;
  
  act.sa_handler = fatal_handler;
  sigemptyset (&act.sa_mask);
  act.sa_flags = 0;

  if (sigaction (SIGFPE, &act, &oact) < 0)
    error_message (4004);
  if (sigaction (SIGSEGV, &act, &oact) < 0)
    error_message (4004);
}
