/* Signal handling setup and code.

   Copyright (C) 1993 Sebastiano Vigna

    This file is part of ne, the nice editor.

    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 1, 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.

In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */


#include "ne.h"
#include <signal.h>



/* These variables remember if we are already in a signal handling
code. In this case, the arrival of another signal must kill us. */

static int fatal_code_in_progress, fatal_error_code;



/* This code is called by all the fatal signals. It records that something
bad is happening, and then tries to autosave all the files currently in
memory using auto_save(). If another signal arrives during the execution,
we exit without any other delay. */

static void fatal_code(int sig) {

	fatal_error_code = sig;

	signal (sig, SIG_DFL);

	if (fatal_code_in_progress) kill(getpid (), fatal_error_code);

	fatal_code_in_progress = TRUE;

	/* Let us clean up the terminal configuration. */

	unset_interactive_mode();

	apply_to_list(&buffers, auto_save);

	kill (getpid (), fatal_error_code);
}


/* This mask will hold all the existing signals. */

static sigset_t signal_full_mask;


/* This function diverts to fatal_code() the behaviour of all fatal signals.
Moreover, signal_full_mask is filled with all the existing signals. 

PORTABILITY PROBLEM: certain systems could have extra, non-POSIX signals whose
trapping could be necessary. Feel free to add other signals to this list, but
please leave SIGINT and SIGQUIT for the interrupt character. */

void set_fatal_code(void) {

	sigfillset (&signal_full_mask);

	signal(SIGILL, fatal_code);
	signal(SIGABRT, fatal_code);
	signal(SIGFPE, fatal_code);
	signal(SIGSEGV, fatal_code);
	signal(SIGTERM, fatal_code);
	signal(SIGHUP, fatal_code);
	signal(SIGPIPE, fatal_code);
	signal(SIGUSR1, fatal_code);
	signal(SIGUSR2, fatal_code);
	signal(SIGTTIN, fatal_code);

}


/* This variable keeps track of the degree of nesting of blocking
and releasing signals, so that block_signals() and release_signals()
can be safely called at any time. */


static int signal_block_nest_count;

/* The following functions block and release, respectively, all signals
(except of course the ones which cannot be trapped). They are used in order
to make atomic sections which modify vital parts of the internal state, such
as lists. */


void block_signals(void) {

	if (!signal_block_nest_count++)
		sigprocmask(SIG_BLOCK, &signal_full_mask, NULL);
}


void release_signals(void) {

	if (!--signal_block_nest_count)
		sigprocmask(SIG_UNBLOCK, &signal_full_mask, NULL);
}



/* This function handles SIGINT and SIGQUIT. It just sets the stop
global variable to TRUE, so that the interested functions can check it,
and restores itself as signal handler. */

unsigned int stop;

void set_stop (int sig) {
	signal(sig, SIG_IGN);
	stop = TRUE;
	signal(sig, set_stop);
}
