aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc/sciteco.1.in11
-rw-r--r--src/core-commands.c2
-rw-r--r--src/interface-curses/interface.c13
-rw-r--r--src/interface-gtk/interface.c46
-rw-r--r--src/main.c10
-rw-r--r--src/ring.c10
-rw-r--r--src/sciteco.h6
-rw-r--r--src/spawn.c2
8 files changed, 61 insertions, 39 deletions
diff --git a/doc/sciteco.1.in b/doc/sciteco.1.in
index e47ca93..83dba44 100644
--- a/doc/sciteco.1.in
+++ b/doc/sciteco.1.in
@@ -425,13 +425,12 @@ Note that this signal can usually also be generated when pressing
.TP
.SCITECO_TOPIC "SIGTERM"
.B SIGTERM
+.TQ
+.SCITECO_TOPIC "SIGHUP"
+.B SIGHUP
Try to gracefully shut down \*(ST.
-In batch mode this only interrupts the currently running macro
-similar to \fBSIGINT\fP causing \*(ST to exit.
-If technically possible, user interfaces will additionally
-process \fBSIGTERM\fP in interactive mode as if the \fICLOSE\fP
-function key has been pressed, which will result in unconditional
-program termination or user-programmed behaviour.
+If technically possible, this will immediately dump recovery
+files unless recovery file dumping is disabled (\(lq0,6EJ\(rq).
.
.
.SH FILES
diff --git a/src/core-commands.c b/src/core-commands.c
index 528fa64..2dc1da7 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -2194,6 +2194,8 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* They are removed automatically when no longer required,
* but may be left around when the \*(ST crashes or terminates
* unexpectedly.
+ * During graceful shutdowns (\fBSIGTERM\fP etc.) recovery
+ * files may be dumped immediately even before the interval expires.
* After changing the interval, the new value may become
* active only after the previous interval expires.
* Recovery files are not dumped in batch mode.
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index b49540b..569b13a 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -125,7 +125,8 @@ teco_console_ctrl_handler(DWORD type)
{
switch (type) {
case CTRL_C_EVENT:
- teco_interrupted = TRUE;
+ case CTRL_BREAK_EVENT:
+ teco_interrupted = TECO_INTERRUPTED;
return TRUE;
}
@@ -1022,7 +1023,7 @@ teco_interface_getch(gboolean widechar)
case KEY_ENTER:
return '\n';
case TECO_CTL_KEY('C'):
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
/* fall through */
case TECO_CTL_KEY('D'):
/* emulates EOF on stdin */
@@ -1799,7 +1800,7 @@ teco_interface_popup_clear(void)
gboolean
teco_interface_is_interrupted(void)
{
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
#else /* !CURSES_TTY && !PDCURSES_WINCON && !NCURSES_WIN32 */
@@ -1818,7 +1819,7 @@ teco_interface_is_interrupted(void)
{
if (!teco_interface.input_pad)
/* batch mode */
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
/*
* NOTE: wgetch() is configured to be nonblocking.
@@ -1833,7 +1834,7 @@ teco_interface_is_interrupted(void)
GINT_TO_POINTER(key));
}
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
#endif
@@ -2122,7 +2123,7 @@ teco_interface_blocking_getch(void)
gint key = wgetch(teco_interface.input_pad);
teco_memory_start_limiting();
/* allow asynchronous interruptions on <CTRL/C> */
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
wtimeout(teco_interface.input_pad, 0);
#if defined(CURSES_TTY) || defined(PDCURSES_WINCON) || defined(NCURSES_WIN32)
noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index 1540245..a31790f 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -78,7 +78,7 @@ static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong
gpointer user_data);
static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event,
gpointer user_data);
-static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED;
+static gboolean teco_interface_termination_handler(gpointer user_data) G_GNUC_UNUSED;
static gchar teco_interface_get_ansi_key(GdkEventKey *event);
#define TECO_UNNAMED_FILE "(Unnamed)"
@@ -477,7 +477,7 @@ teco_interface_getch_process_event(GdkEvent *event, teco_int_t *cp)
*cp = TECO_CTL_KEY(g_ascii_toupper(*cp));
switch (*cp) {
case TECO_CTL_KEY('C'):
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
/* fall through */
case TECO_CTL_KEY('D'):
/* emulates EOF on stdin */
@@ -823,7 +823,7 @@ teco_interface_is_interrupted(void)
{
if (!gtk_main_level())
/* batch mode */
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
/*
* By polling only every TECO_POLL_INTERVAL microseconds
@@ -833,11 +833,11 @@ teco_interface_is_interrupted(void)
guint64 now_ts = g_get_monotonic_time();
if (G_LIKELY(last_poll_ts+TECO_POLL_INTERVAL > now_ts))
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
last_poll_ts = now_ts;
gtk_main_iteration_do(FALSE);
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
void
@@ -1298,13 +1298,12 @@ teco_interface_event_loop(GError **error)
#endif
/*
- * SIGTERM emulates the "Close" key just like when
- * closing the window if supported by this version of glib.
- * Note that this replaces SciTECO's default SIGTERM handler
- * so it will additionally raise(SIGINT).
+ * SIGTERM and SIGHUP terminate the program, but will
+ * dump recovery files first (if enabled).
*/
#ifdef G_OS_UNIX
- g_unix_signal_add(SIGTERM, teco_interface_sigterm_handler, NULL);
+ g_unix_signal_add(SIGTERM, teco_interface_termination_handler, NULL);
+ g_unix_signal_add(SIGHUP, teco_interface_termination_handler, NULL);
#endif
/* the interval might have been changed in the profile */
@@ -1428,7 +1427,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
* If the execution thread is currently blocking,
* the key is delivered like an ordinary key press.
*/
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
else
g_queue_push_tail(teco_interface.event_queue,
gdk_event_copy(event));
@@ -1469,7 +1468,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
const teco_view_t *last_view = teco_interface_current_view;
sptr_t last_vpos = teco_interface_ssm(SCI_GETFIRSTVISIBLELINE, 0, 0);
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
switch (event->type) {
case GDK_KEY_PRESS:
teco_interface_handle_key_press(&event->key, &error);
@@ -1486,7 +1485,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
default:
g_assert_not_reached();
}
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
teco_interface_update(teco_interface_current_view != last_view);
/* always expand folds, even after mouse clicks */
@@ -1596,16 +1595,23 @@ teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer
}
static gboolean
-teco_interface_sigterm_handler(gpointer user_data)
+teco_interface_termination_handler(gpointer user_data)
{
+ teco_interface_msg(TECO_MSG_WARNING, "Received termination signal - dumping recovery files");
+ teco_interrupted = TECO_TERMINATED;
+
/*
- * Similar to window deletion - emulate "close" key press.
+ * This may be an emergency shutdown or another kind of
+ * termination that we cannot ignore, so better dump
+ * recovery files before terminating.
*/
- GtkWidget *widget = teco_interface.window;
+ if (teco_ring_recovery_interval != 0)
+ teco_ring_dump_recovery();
- g_autoptr(GdkEvent) close_event = gdk_event_new(GDK_KEY_PRESS);
- close_event->key.window = gtk_widget_get_parent_window(widget);
- close_event->key.keyval = GDK_KEY_Close;
+ /*
+ * Otherwise we terminate and clean up as if by `-EX`.
+ */
+ gtk_main_quit();
- return teco_interface_input_cb(widget, close_event, NULL);
+ return G_SOURCE_REMOVE;
}
diff --git a/src/main.c b/src/main.c
index 8bc02f1..33ab26f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -66,12 +66,15 @@
teco_int_t teco_ed = TECO_ED_AUTOEOL;
/**
- * Whether there was an asyncronous interruption (usually after pressing CTRL+C).
+ * Whether there was an asynchronous interruption (usually after
+ * pressing CTRL+C) or termination (SIGTERM).
* However you should always use teco_interface_is_interrupted(),
* to check for interruptions because of its side effects.
* This variable is safe to set to TRUE from signal handlers and threads.
+ *
+ * This is a teco_interrupted_t.
*/
-volatile sig_atomic_t teco_interrupted = FALSE;
+volatile sig_atomic_t teco_interrupted = TECO_NORMAL;
/*
* FIXME: Move this into file-utils.c?
@@ -346,7 +349,7 @@ teco_initialize_environment(void)
static void
teco_sigint_handler(int signal)
{
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
}
#ifdef G_OS_WIN32
@@ -386,7 +389,6 @@ main(int argc, char **argv)
#endif
signal(SIGINT, teco_sigint_handler);
- signal(SIGTERM, teco_sigint_handler);
/*
* Important for Unicode handling in curses and glib.
diff --git a/src/ring.c b/src/ring.c
index 8bb825c..9feb444 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -143,7 +143,12 @@ teco_buffer_save(teco_buffer_t *ctx, const gchar *filename, GError **error)
static inline void
teco_buffer_free(teco_buffer_t *ctx)
{
- if (ctx->state > TECO_BUFFER_DIRTY_NO_DUMP) {
+ /*
+ * During graceful, but unexpected shutdowns (SIGTERM etc.),
+ * we must preserve the recovery files.
+ */
+ if (ctx->state > TECO_BUFFER_DIRTY_NO_DUMP &&
+ teco_interrupted != TECO_TERMINATED) {
g_autofree gchar *filename_recovery = teco_buffer_get_recovery(ctx);
g_unlink(filename_recovery);
}
@@ -317,7 +322,8 @@ guint teco_ring_recovery_interval = 2*60;
/**
* Create recovery files for all dirty buffers.
*
- * Should be called by the interface every teco_ring_recovery_interval seconds.
+ * Should be called by the interface every teco_ring_recovery_interval seconds
+ * or before graceful terminations (SIGTERM etc.).
* This does not generate or expect undo tokens, so it can be called
* even when idlying.
*/
diff --git a/src/sciteco.h b/src/sciteco.h
index 16dba69..88078c2 100644
--- a/src/sciteco.h
+++ b/src/sciteco.h
@@ -109,6 +109,12 @@ teco_default_codepage(void)
return teco_ed & TECO_ED_DEFAULT_ANSI ? SC_CHARSET_ANSI : SC_CP_UTF8;
}
+typedef enum {
+ TECO_NORMAL = 0,
+ TECO_INTERRUPTED,
+ TECO_TERMINATED
+} teco_interrupted_t;
+
/* in main.c */
extern volatile sig_atomic_t teco_interrupted;
diff --git a/src/spawn.c b/src/spawn.c
index 61718fd..716bafa 100644
--- a/src/spawn.c
+++ b/src/spawn.c
@@ -869,7 +869,7 @@ teco_spawn_idle_cb(gpointer user_data)
{
if (G_LIKELY(!teco_interface_is_interrupted()))
return G_SOURCE_CONTINUE;
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
/*
* The first CTRL+C will try to gracefully terminate the process.