diff options
| -rw-r--r-- | doc/sciteco.1.in | 11 | ||||
| -rw-r--r-- | src/core-commands.c | 2 | ||||
| -rw-r--r-- | src/interface-curses/interface.c | 13 | ||||
| -rw-r--r-- | src/interface-gtk/interface.c | 46 | ||||
| -rw-r--r-- | src/main.c | 10 | ||||
| -rw-r--r-- | src/ring.c | 10 | ||||
| -rw-r--r-- | src/sciteco.h | 6 | ||||
| -rw-r--r-- | src/spawn.c | 2 |
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; } @@ -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. @@ -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. |
