/* $Id: e2_main.c 893 2008-05-20 09:42:51Z tpgww $

Copyright (C) 2003-2008 tooar <tooar@gmx.net>
Portions copyright (C) 1999 Michael Clark.

This file is part of emelFM2.
emelFM2 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 3, or (at your option)
any later version.

emelFM2 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 emelFM2; see the file GPL. If not, contact the Free Software
Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

/**
@file src/e2_main.c
@brief main function

This file contains the main function and some local startup and shutdown helpers.
*/

/**
\mainpage emelFM2 API

\attention This API-documentation is under construction!\n It is planned to provide additional information, to make it easier to read and understand the code and hopefully to inspire you to enhance it.

\section intro introduction

Welcome to emelFM2-doxygen. If you don't know doxygen well, click on
everything you're interested in. Even within the source documentation you
will find lots of links. It's quite fun.\n\n
On this page you'll find all the important aspects of coding for emelFM2.
There are three parts:\n
\arg \ref conventions
\arg \ref internals
\arg \ref visible\n

If you're interested in developing plugins, go to \ref plugin_writing

\section conventions conventions

emelFM2 code should follow some layout and naming conventions:
\arg use tabs, not spaces, to indent code. Preferred tab size is 4.
\arg stick to a consistent coding style
 - vertically-aligned matching braces
 - a space between text and left-brackets, after commas, and around operators
 - "//" C++ style single-line comments
 - in general, break lines at or about column 80

\code
void routine (void)
{
	if (function (a, b, c))
	{
		//do something
		x = a + 1;
		printf (_("Break lines after 80 characters, in general, like the following.\n))
		rt->set = e2_option_tree_register ("option_name", strdup ("group_name"), _("option_label"),
			NULL, NULL, NULL, E2_OPTION_TREE_UP_DOWN | E2_OPTION_TREE_ADD_DEL);
		printf (_("or _perhaps_ NOT after 80, if the line is just a string that needs to be internationalised like this.\n"));
	}
}
\endcode
\arg files that are part of emelFM2 have a name starting with e2_, or e2p_ for a plugin
\arg emelFM2's own header files can included with just "" even if they are not in the same directory
\arg the name of emelFM2's own functions starts with e2_, e2p_, _e2_, or _e2p_. The "_" variants are for static (private) functions, those prefixes should not be used for functions called from other parts of the program. The "p" variants apply in plugins. In general, the second part of the function name matches the file that contains the function. Sometimes it's a condensed form of that name. Functions imported from other code are probably best left with
their original name.
\arg emelFM2's own constants should begin with E2 or E2_

Code should be liberally commented, to assist others in reviewing and enhancing things.
In particular, functions should be documented in a form which doxygen understands.\n\n
Strings which are displayed to the user should be internationalised, like _("Tell me")

\section internals internals

This section explains how emelFM2 works.
\arg \ref options
\arg \ref cache
\arg \ref actions
\arg \ref commands
\arg \ref bindings
\arg \ref filesystem
\arg \ref plugins

\section visible user-interface

This section is about parts of emelFM2 you can see and interact with. It provides
development-related information which goes beyond, and should be read in conjuntion
with, user-guidance in the files
 - USAGE (general help)
 - CONFIGURATION (configuration help mainly for use via a configuration dialog)
 - ACTIONS (a listing of action-names, each with parameters and brief description)\n

The following items are covered:
\arg \ref panes
\arg \ref output
\arg \ref toolbar
\arg \ref commline
\arg \ref menu
\arg \ref othermenu
\arg \ref status
\arg \ref dialogs
*/

#include "emelfm2.h"
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include "e2_dialog.h"
#include "e2_filelist.h"
#include "e2_filetype.h"
#include "e2_plugins.h"
#include "e2_complete.h"
#include "e2_task.h"

#define UPGRADE_PNAME "e2p_upgrade.so"

extern gint refresh_refcount;
extern GList *open_history;
#ifdef USE_GLIB2_10
extern GHashTable *actions_hash;
#endif

static void _e2_main_system_shutdown (gint num); //__attribute__ ((noreturn));

/**
@brief local management of gdk threads mutex
These allow a conservative approach to locking without risk of deadlock
@return
*/
void e2_main_close_gdklock (void)
{
#ifdef NATIVE_BGL
	gdk_threads_enter ();
#else
	//a single thread can't write to screen more than once at a time,
	//so deadlocks just reflect "unnecessary" re-locks, and can be ignored
# ifdef DEBUG_MESSAGES
	if (pthread_mutex_lock (&gdklock) == EDEADLK)
		printd (DEBUG, "attempted BGL re-lock");
# else
	pthread_mutex_lock (&gdklock);
# endif
#endif
}
void e2_main_open_gdklock (void)
{
#ifdef NATIVE_BGL
	gdk_threads_leave ();
#else
# ifdef DEBUG_MESSAGES
	if (pthread_mutex_unlock (&gdklock) == EPERM)
		printd (DEBUG, "bad attempt to open BGL");
# else
	pthread_mutex_unlock (&gdklock);
# endif
#endif
}

/**
@brief idle and then timer callback to start refreshing filelists after both filelists are initialised
This tests that both view->dir strings are not ""
@param data NULL specified when idle was established, non-NULL for timer

@return TRUE until the filelists have been established
*/
static gboolean _e2_main_refresh_initialise (gpointer data)
{
	if (app.pane1_view.dir == NULL || *app.pane1_view.dir == '\0'
	 || app.pane2_view.dir == NULL || *app.pane2_view.dir == '\0'
	 || app.pane1_view.listcontrols.cd_working
	 || app.pane2_view.listcontrols.cd_working)
	{
		if (data != NULL)	//not first first callback
			return TRUE;
		//start a timer to check completion
		//app.timers[?] = CHECKME no need to force shutdown ?
		g_timeout_add (100, (GSourceFunc) (_e2_main_refresh_initialise),
			GINT_TO_POINTER (100));
			return FALSE;
	}
	//this func may be called at some racy time when these need to be reset
	app.pane1_view.listcontrols.norefresh = FALSE;
	app.pane2_view.listcontrols.norefresh = FALSE;
#ifdef E2_REFRESH_DEBUG
	printd (DEBUG, "enable refresh, session start after filelists filled");
#endif
	e2_filelist_enable_refresh ();
#ifndef E2_REFRESH_DEBUG
	printd (DEBUG, "all change-polling initialised after filelists filled");
#endif
	return FALSE;
}
/**
 @brief spit out a warning if the current locale-information looks dodgy

 @param locale single or ";"-joined series of locale identifier strings

 @return
*/
static void _e2_main_check_locale_broken (const gchar *locale)
 {
	gboolean broken = FALSE;
	if (g_str_equal (locale, "C"))
		broken = TRUE;
	else
	{
		gchar **split = g_strsplit (locale, ";", -1);
		gint i = 0;
		while (split[i] != NULL)
		{
			if (g_str_has_prefix (split[0], "LC_CTYPE") &&
				g_str_has_suffix (split[0], "=C"))
			{
				broken = TRUE;
				break;
			}
			i++;
		}
	}
	if (broken)
 	{
 		printf (_("Your current locale is '%s'.\n"), locale);
		printf (
		  _("You have set the environment variable G_BROKEN_FILENAMES, which\n"
 			"causes GTK+ to convert filename encoding, from the one specified\n"
			"by the system locale, to UTF-8.\n"
			"However, you have not set a system locale. Please do so, by setting\n"
			"the environment variable LANG or LC_CTYPE!\n"));
 		if (!e2_cl_options.ignore_problems)
 		{
			printf (
			  _("(Note: There is a command line option -i/--ignore-problems, but use it\n"
				"at your own risk!)\n"));
 			exit (1);
 		} else
			printf (
			  _("%s will ignore locale problems when reading filenames because\n"
 				"--ignore-problems/-i has been set. This might result in segfaults and all\n"
 				"kind of problems. You really should set a system locale with the\n"
				"LANG or LC_CTYPE environment variable.\n"), PROGNAME);
 	}
}
/**
@brief check locale

@return
*/
static void _e2_main_check_locale (void)
{
	printd (DEBUG, "check_locale ()");
	const gchar *locale = gtk_set_locale ();
	printd (NOTICE, "current locale is '%s'", locale);
	const gchar *broken = g_getenv ("G_BROKEN_FILENAMES");
	if (broken != NULL)
		_e2_main_check_locale_broken (locale);

#ifdef ENABLE_NLS
	printd (DEBUG, "setting locale base dir to '%s'", LOCALE_DIR);
	bindtextdomain (BINNAME, LOCALE_DIR);
	textdomain (BINNAME);
	bind_textdomain_codeset (BINNAME, "UTF-8");
#endif
}

/**
@brief blind log handler used when gtk messages-logging is suppressed

@return
*/
//static void _e2_main_blind_log_handler () { }
/**
@brief handle messages from glib or gtk

@return
*/
static void _e2_main_message_handler (const gchar *log_domain,
	GLogLevelFlags log_level, const gchar *message, gpointer user_data)
{
	if (e2_cl_options.suppress_gtk_log)
		g_log_default_handler (log_domain, log_level, message, user_data);
	else
	{
#ifdef DEBUG_MESSAGES
		printd (WARN, "%s message: %s", log_domain, message);
#else
		//mimic default message printer without aborting on fatal error
		gchar *fmt = (log_level < G_LOG_LEVEL_WARNING) ?
			"\n** %s: %s **\n\n" : "%s: %s\n";
		printf (fmt, log_domain, message);
#endif
		g_log (log_domain, log_level, "%s", message);
	}
}

  /****************/
 /***** main *****/
/****************/

/**
@brief main function

@param argc number of command-line arguments
@param argv array of command-line arguments

@return nothing, directly
*/
gint main (gint argc, gchar *argv[])
{
	//threads needed
	pthread_mutexattr_t attr;
	//setup mutex to protect threaded access to cd functionality
	pthread_mutexattr_init (&attr);
	pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
	pthread_mutex_init (&list_mutex, &attr);
	pthread_mutex_init (&history_mutex, &attr);
#ifndef NATIVE_BGL
//PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP isn't always available to init mutex
//	pthread_mutexattr_t attr;
	pthread_mutexattr_init (&attr);
	pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
	pthread_mutex_init (&gdklock, &attr);
//	pthread_mutexattr_destroy (&attr);
#endif
	pthread_mutexattr_destroy (&attr);

	g_thread_init (NULL);
#ifndef NATIVE_BGL
	gdk_threads_set_lock_functions (e2_main_close_gdklock, e2_main_open_gdklock);
#endif
	gdk_threads_init ();	//setup gdk mutex
	//gtk initialization
	printd (DEBUG, "gtk init");
	gtk_init (&argc, &argv);
	g_set_application_name (_("emelFM2"));

	e2_cl_option_process (argc, argv);
	_e2_main_check_locale ();
#ifndef E2_FILES_UTF8ONLY
	e2_fs_check_coding ();	//setup for path/filename conversion if needed
#endif
	//send all messages to the handler
	GLogLevelFlags levels = G_LOG_LEVEL_MASK | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL;
	g_log_set_handler ("Glib", levels, _e2_main_message_handler, NULL);
	g_log_set_handler ("Gtk", levels, _e2_main_message_handler, NULL);

	//some local init stuff
	e2_action_setup_labels ();  //need this before option_init, to avoid crashes when there is no config file
	e2_option_setup_labels ();
	//after labels & before options setup, setup data for referencing toolbars
	e2_toolbar_data_create ();

	e2_option_init ();  //setup default options (before cache or config read)

	gboolean confdir_ok = e2_option_set_config_dir ();  //set name for config dir, make it if not there already
	e2_cache_init (confdir_ok);  //read and process cache file (if any) (before options init)
	gboolean confile_ok = (confdir_ok) ?
		e2_option_file_read (): //read & process saved options (if any)
		FALSE;

	if (confile_ok)
	{
		//ASAP after config settled, CHECKME custom dialog ok ?
		if (strcmp (app.cfgfile_version, VERSION RELEASE) < 0)
		{
			Plugin *p = e2_plugins_open1 (PLUGINS_DIR G_DIR_SEPARATOR_S UPGRADE_PNAME);	//localised path
			if (p != NULL)
			{
				if (!p->plugin_init (p))	//do any upgrades
					printd (ERROR, "Can't initialize upgrade plugin: "UPGRADE_PNAME);
				gdk_threads_enter ();
				e2_plugins_unload1 (p, FALSE);
				gdk_threads_leave ();
			}
			else
				printd (ERROR, "Can't find upgrade plugin "UPGRADE_PNAME", so can't upgrade the config file");
		}
		gchar **values = e2_list_to_strv (e2_cl_options.option_overrides);
		e2_option_read_array (values);
		g_strfreev (values);
	}

	//after config & updates, install default tree options where needed
	e2_option_tree_install_defaults ();

	//before plugins loaded, before bars/menus created
	e2_actions_init (); //setup action data stores & register most actions
	e2_complete_init (); //"like" actions

#ifndef USE_GTK2_12TIPS
	app.tooltips = gtk_tooltips_new ();
#endif

	//check if all plugin data is in config already
//	E2_OptionSet *set = e2_option_get ("plugins");  //internal name
//	gboolean defer_plugs = set->ex.tree.synced;
//	if (!defer_plugs)
	//if this is a fresh start,
	//do this before bars/menus created
	e2_plugins_load_all (); // load plugins specified in the config data

	//build GUI
	printd (DEBUG, "create main window");
	e2_window_create (&app.window);
	//show the window before the filelist creation (in case there's a big delay)
	gdk_threads_enter ();
	gtk_widget_show (app.main_window);
	gdk_threads_leave ();

#ifdef E2_FAM
	//before change dir, signal unknown state
	app.pane1.FAMreq = -1;
	app.pane2.FAMreq = -1;
	//hookup to a file monitor if possible
	e2_fs_FAM_connect ();
	//CHECKME ensure FAM reports don't pile up due to lack of polling
#endif
	//initate dirty-checks
	e2_filelist_start_refresh_polling ();
	//config-change checks are disabled during list creation
	//so the mechanism needs to be initialised beforehand
//	app.config_timer_id = 0;
	e2_option_enable_config_checks ();

#ifndef E2_STATUS_DEMAND
	e2_window_enable_status_update (-1);
#endif
	//set refresh counter to its real initial value
	refresh_refcount = 0;
#ifdef E2_REFRESH_DEBUG
	printd (DEBUG, "disable refresh, session start before filelists built");
#endif
	//prevent any actual refreshing until after the initial filelists are created
	e2_filelist_disable_refresh ();

	e2_button_setup_labels ();	//maybe needed by dialog when opening dir

	//session-starts with BGL closed
//	gdk_threads_leave ();
	//threaded cd's may start at any time, including after main loop is running
	//and cd completion depends on speed of dir loading
	//this func will probably not set app.pane2.view.dir immediately
	e2_utils_goto_accessible_path (&app.pane2);
	//show pane 1 last, making it the default for session start
	e2_utils_goto_accessible_path (&app.pane1);
//	gdk_threads_enter ();

	gint pos = gtk_paned_get_position (GTK_PANED (app.window.output_paned));
	if (pos == 0)
	{
		gchar *command = g_strconcat(_A(1),".",_A(42),NULL);  //_("command.focus")
		e2_action_run_simple (command, NULL);
		g_free (command);
	}

	e2_utils_update_gtk_settings ();

	e2_filetype_add_all ();	//process filetypes into data structure

	e2_cache_list_register ("open-history", &open_history); //history for 'open with' command
	e2_cache_int_register ("config-width", &app.cfgdlg_width, -1);	//default to 'normal' window size
	e2_cache_int_register ("config-height", &app.cfgdlg_height, -1);
	e2_option_set_trash_dir ();

	GList *tmp;
	for (tmp = e2_cl_options.startup_commands; tmp != NULL; tmp = g_list_next (tmp))
#ifdef E2_COMMANDQ
		e2_command_run (tmp->data, E2_COMMAND_RANGE_DEFAULT, FALSE);
#else
		e2_command_run (tmp->data, E2_COMMAND_RANGE_DEFAULT);
#endif
	//if instructed, detach from controlling terminal and run in background
	if (e2_cl_options.detached
#ifdef DEBUG_MESSAGES
		&& (e2_cl_options.debug_level < 5)
#endif
	)
		daemon (1, 1);

	//trap signals that might be (but probably aren't) emitted when a
	//session-manager requests a shutdown/"save-yourself"
	//CHECKME only work if not running as daemon ?
	struct sigaction sigdata;
	sigdata.sa_handler = _e2_main_system_shutdown;
//	sigemptyset (&sigdata.sa_mask);	//don't block any signal while the handler is busy
	sigfillset (&sigdata.sa_mask);	//block all allowed signals while the handller is busy
	sigdata.sa_flags = 0;
//	sigaction (SIGKILL, &sigdata, NULL);	//CHECKME save this for stop without saving ?
	sigaction (SIGABRT, &sigdata, NULL);
	sigaction (SIGQUIT, &sigdata, NULL);
	sigaction (SIGTSTP, &sigdata, NULL);
	sigaction (SIGHUP, &sigdata, NULL);
	sigaction (SIGINT, &sigdata, NULL);	//this from a <Ctrl>c in a terminal
	sigaction (SIGTERM, &sigdata, NULL);

/*	//SIGPIPE ignorance maybe needed to ensure thread-safety ?
	//(but we don't want to pass that on to child processes)
//	sigemptyset (&sigdata.sa_mask);	//don't block any signal while the handler is busy
	sigaction (SIGPIPE, NULL, &sigdata);
	sigdata.sa_handler = SIG_IGN;
	sigaction (SIGPIPE, &sigdata, NULL);
*/
#ifdef E2_VFSTMP
	//session startup always uses local fs first, to prevent potential long
	//delay when app window is being created
	//now we can arrange to reinstate vfs state if appropriate
//now done in plugin init	g_idle_add ((GSourceFunc) e2_vfs_initialise, NULL);
#endif

	//start change polling after both initial filelists are completed
	g_idle_add ((GSourceFunc) _e2_main_refresh_initialise , NULL);

	//get all actions, for help document
//	e2_action_list_all ();

#ifdef E2_HAL
	e2_hal_init ();
#endif

	printd (DEBUG, "enter main loop");
	gdk_threads_enter ();
	gtk_main ();
	gdk_threads_leave ();
	exit (0);
}

  /************************/
 /*** departure lounge ***/
/************************/

/**
@brief session-end cleanups
Expects BGL on/closed, for any dialog or error message here or downstream
@param compulsory TRUE to prevent the user from cancelling
@param saveconfig TRUE to write config and cache files
@param doexit TRUE to do a normal program exit

@return FALSE if the user cancels or @a doexit is FALSE, else no return
*/
gboolean e2_main_closedown (gboolean compulsory, gboolean saveconfig, gboolean doexit)
{
	if (!compulsory)
	{
		if (e2_option_bool_get ("session-end-warning"))
		{
			//only count running child commands if they are in line for killing
			guint running = e2_command_count_running_tasks
				(!e2_option_bool_get ("command-persist"), TRUE);
			if (running > 0)
			{
				gchar *prompt = g_strdup_printf (_("%u process(es) are running"), running);
				DialogButtons choice = e2_dialog_warning (prompt);
				g_free (prompt);
				if (choice != OK)
					return FALSE;
			}
		}
	}
	guint i;
	for (i = 0; i < MAX_TIMERS; i++)
	{
		if (app.timers[i] > 0)
			g_source_remove (app.timers[i]);
	}
	//CHECKME other non-static timers e.g. keybinding timers, edit blink timers etc
	e2_task_abort (TRUE);
#ifdef E2_FAM
	e2_fs_FAM_disconnect ();
#endif
#ifdef E2_HAL
	e2_hal_disconnect ();
#endif
#ifdef E2_IMAGECACHE
	e2_cache_image_clearall ();
#endif
	e2_plugins_unload_all (TRUE);	//backup caches, cleanup etc
/*#ifdef E2_VFS
	if (vfs.loaded)
	{	//unloading does cacheing and proper cleanups
		Plugin *p = e2_plugins_check_installed ("vfs"VERSION);
		e2_plugins_unload1 (p, TRUE);
	}
#endif */
	if (saveconfig)
	{
		e2_option_file_write (NULL);
		e2_cache_file_write ();
		printd (DEBUG, "touch config dir");
		e2_fs_touch_config_dir (); //signal to the world that changes saved
	}
#ifdef USE_GLIB2_10
//	GList *member;
	//need to deallocate the glib slices, at least
	//NOTE this must be after all cleanups involving actions
	g_hash_table_destroy (actions_hash);
	//NOTE this must be after all cleanups involving options
	g_hash_table_destroy (options_hash);
/*	e2_cache_clean ();
	e2_complete_clear ();
	e2_alias_clean ();
	e2_command_line_clean ();
	for (member = app.taskhistory; member != NULL; member = member->next)
	{
		//also free the data in the rt ??
		DEALLOCATE (E2_TaskRuntime, (E2_TaskRuntime *) member->data);
	}
	e2_command_clear_pending (NULL, NULL);
	//output tabs
	for (member = app.tabslist; member != NULL; member = member->next)
		DEALLOCATE (E2_OutputTabRuntime, (E2_OutputTabRuntime *) member->data);
	//toolbar data
	E2_ToolbarData **thisbar;
	for (thisbar = app.bars; *thisbar != NULL; thisbar++)
		DEALLOCATE (E2_ToolbarData, (E2_ToolbarData *)*thisbar);
	e2_keybinding_clean (); //keybinding runtimes
	//FIXME deallocate other slices:
	//EncArray
	//E2_ToggleBox's ?
	//E2_ToggleData's ?
	//anything from e2_fileview_get_selected()
	//PlaceInfo's handled by unloading the vfs plugin
	//FileInfo's tied to treeview lines are cleaned already ?? HOW?
	//all E2_OptionTreeColumns
//unref all optiontree stores ?
	//view & edit dialog history items (if sliced!)
	//edit dialog undo items
	//ETC
*/
#endif
	//these might be in some backup place, for VFS
	//dirline histories ?
	//directory history list items
	e2_fileview_clean_history (&app.pane1_view.dir_history);
	e2_fileview_clean_history (&app.pane2_view.dir_history);
	g_list_free (app.pane1.opendirs);	//this for non-cached list without heaped data
	g_list_free (app.pane2.opendirs);	//ditto
/*	printd (DEBUG, "clean options");
	destroy_config ();  //why bother, if the kernel does it all anyway?
								//cache(s) @ cache or named (lists ??)
								//FIXME?? = all those non-constant config strings
	printd (DEBUG, "clean bookmarks");
	e2_bookmark_clean ();
	printd (DEBUG, "clean actions");
	e2_actions_clean ();
*/
	i = gtk_main_level ();	//mainloop level isn't decremented by quit func
	printd (DEBUG, "gtk_main_quit () from level %d", i);
	while (i > 0)
	{
		gtk_main_quit ();
		i--;
	}

	if (doexit)
	{
		gdk_threads_leave ();
//		printd (DEBUG, "exit (0)");
		exit (0);
	}
	return FALSE;
}
/**
@brief user-initiated shutdown action

@param from the button, menu item etc which was activated
@param art action runtime data

@return FALSE if the user cancelled, or else it does not return at all
*/
gboolean e2_main_user_shutdown (gpointer from, E2_ActionRuntime *art)
{
	e2_main_closedown (FALSE, TRUE, TRUE);
	return FALSE;
}
/**
@brief perform system-initiated shutdown in repsonse to a shutdown signal
This is performed the "proper" way, so as to not block signalling to related
processes
@param num the signal number

@return
*/
static void _e2_main_system_shutdown (gint num)
{
	printd (DEBUG, "system shutdown initiated by signal %d", num);
	gdk_threads_enter ();	//downstream errors expect BGL closed
	e2_main_closedown (TRUE, (num != SIGINT && num != SIGKILL), FALSE);
	gdk_threads_leave ();
	//in this handler, current signal is blocked, unblock it for this use
	struct sigaction sigdata;
	sigdata.sa_handler = SIG_DFL;
	sigemptyset (&sigdata.sa_mask);
	sigdata.sa_flags = 0;
	sigaction (num, &sigdata, NULL);
	sigaddset (&sigdata.sa_mask, num);
	sigprocmask (SIG_UNBLOCK, &sigdata.sa_mask, NULL);
	kill (getpid(), num);
}
