aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core-commands.c25
-rw-r--r--src/interface-curses/interface.c28
-rw-r--r--src/interface-gtk/interface.c14
-rw-r--r--src/ring.c61
-rw-r--r--src/ring.h8
-rw-r--r--src/view.c7
6 files changed, 87 insertions, 56 deletions
diff --git a/src/core-commands.c b/src/core-commands.c
index 47809e2..e756eab 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -2149,13 +2149,20 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* Unlike most other settings, this is on purpose not restored on rubout,
* so it \(lqsurvives\(rq command line replacements.
* .IP 5:
- * Height of the command line view in lines.
+ * Height of the command line view in lines (1 by default).
* Must not be smaller than 1.
* .IP 6:
- * Backup interval in seconds or 0 if backups are disabled.
+ * .SCITECO_TOPIC recovery
+ * Interval in seconds for the creation of recovery files
+ * or 0 if those dumps are disabled (the default is 300 seconds).
+ * When enabled all dirty buffers are dumped to files with hash
+ * signs around the original basename (\fB#\fIfilename\fB#\fR).
+ * They are removed automatically when no longer required,
+ * but may be left around when the \*(ST crashes or terminates
+ * unexpectedly.
* After changing the interval, the new value may become
* active only after the previous interval expires.
- * Backups are not created in batch mode.
+ * Recovery files are not dumped in batch mode.
* .
* .IP -1:
* Type of the last mouse event (\fBread-only\fP).
@@ -2210,7 +2217,7 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
EJ_INIT_COLOR,
EJ_CARETX,
EJ_CMDLINE_HEIGHT,
- EJ_BACKUP_INTERVAL
+ EJ_RECOVERY_INTERVAL
};
static teco_int_t caret_x = 0;
@@ -2261,14 +2268,14 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
teco_undo_guint(teco_cmdline.height) = value;
break;
- case EJ_BACKUP_INTERVAL:
+ case EJ_RECOVERY_INTERVAL:
if (value < 0 || value > G_MAXUINT) {
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Invalid backup interval %" TECO_INT_FORMAT "s "
+ "Invalid recovery interval %" TECO_INT_FORMAT "s "
"for <EJ>", value);
return;
}
- teco_undo_guint(teco_ring_backup_interval) = value;
+ teco_undo_guint(teco_ring_recovery_interval) = value;
/* FIXME: Perhaps signal the interface to reprogram timers */
break;
@@ -2331,8 +2338,8 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
teco_expressions_push(teco_cmdline.height);
break;
- case EJ_BACKUP_INTERVAL:
- teco_expressions_push(teco_ring_backup_interval);
+ case EJ_RECOVERY_INTERVAL:
+ teco_expressions_push(teco_ring_recovery_interval);
break;
default:
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index e03ba72..073ff86 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -205,8 +205,8 @@ static struct {
teco_string_t info_current;
gboolean info_dirty;
- /** timer to track the backup interval */
- GTimer *backup_timer;
+ /** timer to track the recovery interval */
+ GTimer *recovery_timer;
WINDOW *msg_window;
@@ -2019,14 +2019,14 @@ teco_interface_blocking_getch(void)
nodelay(teco_interface.input_pad, FALSE);
/*
- * Make sure we return when it's time to create backups.
+ * Make sure we return when it's time to create recovery dumps.
*/
- if (teco_ring_backup_interval != 0) {
- if (G_UNLIKELY(!teco_interface.backup_timer))
- teco_interface.backup_timer = g_timer_new();
- gdouble elapsed = g_timer_elapsed(teco_interface.backup_timer, NULL);
+ if (teco_ring_recovery_interval != 0) {
+ if (G_UNLIKELY(!teco_interface.recovery_timer))
+ teco_interface.recovery_timer = g_timer_new();
+ gdouble elapsed = g_timer_elapsed(teco_interface.recovery_timer, NULL);
wtimeout(teco_interface.input_pad,
- MAX((gdouble)teco_ring_backup_interval - elapsed, 0)*1000);
+ MAX((gdouble)teco_ring_recovery_interval - elapsed, 0)*1000);
}
/*
@@ -2044,10 +2044,10 @@ teco_interface_blocking_getch(void)
cbreak();
#endif
- if (key == ERR && teco_ring_backup_interval != 0 &&
- g_timer_elapsed(teco_interface.backup_timer, NULL) >= teco_ring_backup_interval) {
- teco_ring_backup();
- g_timer_start(teco_interface.backup_timer);
+ if (key == ERR && teco_ring_recovery_interval != 0 &&
+ g_timer_elapsed(teco_interface.recovery_timer, NULL) >= teco_ring_recovery_interval) {
+ teco_ring_dump_recovery();
+ g_timer_start(teco_interface.recovery_timer);
}
return key;
@@ -2312,6 +2312,6 @@ teco_interface_cleanup(void)
if (teco_interface.pair_table)
g_hash_table_destroy(teco_interface.pair_table);
- if (teco_interface.backup_timer)
- g_timer_destroy(teco_interface.backup_timer);
+ if (teco_interface.recovery_timer)
+ g_timer_destroy(teco_interface.recovery_timer);
}
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index 0eec55e..d7fc4c4 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -60,7 +60,7 @@
//#define DEBUG
static gboolean teco_interface_busy_timeout_cb(gpointer user_data);
-static gboolean teco_interface_backup_cb(gpointer user_data);
+static gboolean teco_interface_dump_recovery_cb(gpointer user_data);
static void teco_interface_event_box_realized_cb(GtkWidget *widget, gpointer user_data);
static void teco_interface_cmdline_size_allocate_cb(GtkWidget *widget,
GdkRectangle *allocation,
@@ -1222,8 +1222,8 @@ teco_interface_event_loop(GError **error)
#endif
/* the interval might have been changed in the profile */
- g_timeout_add_seconds(teco_ring_backup_interval,
- teco_interface_backup_cb, NULL);
+ g_timeout_add_seconds(teco_ring_recovery_interval,
+ teco_interface_dump_recovery_cb, NULL);
/* don't limit while waiting for input as this might be a busy operation */
teco_memory_stop_limiting();
@@ -1284,16 +1284,16 @@ teco_interface_busy_timeout_cb(gpointer user_data)
}
static gboolean
-teco_interface_backup_cb(gpointer user_data)
+teco_interface_dump_recovery_cb(gpointer user_data)
{
- teco_ring_backup();
+ teco_ring_dump_recovery();
/*
* The backup interval could have changed (6EJ).
* New intervals will not be effective immediately, though.
*/
- g_timeout_add_seconds(teco_ring_backup_interval,
- teco_interface_backup_cb, NULL);
+ g_timeout_add_seconds(teco_ring_recovery_interval,
+ teco_interface_dump_recovery_cb, NULL);
return G_SOURCE_REMOVE;
}
diff --git a/src/ring.c b/src/ring.c
index e42ff14..bc04e50 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -77,9 +77,11 @@ teco_buffer_undo_edit(teco_buffer_t *ctx)
/** @private @memberof teco_buffer_t */
static inline gchar *
-teco_buffer_get_backup(teco_buffer_t *ctx)
+teco_buffer_get_recovery(teco_buffer_t *ctx)
{
- return g_strconcat(ctx->filename, "~", NULL);
+ g_autofree gchar *dirname = g_path_get_dirname(ctx->filename);
+ g_autofree gchar *basename = g_path_get_basename(ctx->filename);
+ return g_strconcat(dirname, G_DIR_SEPARATOR_S, "#", basename, "#", NULL);
}
/** @private @memberof teco_buffer_t */
@@ -116,10 +118,10 @@ teco_buffer_save(teco_buffer_t *ctx, const gchar *filename, GError **error)
*/
if (ctx == teco_ring_current && !teco_qreg_current)
undo__teco_interface_info_update_buffer(ctx);
- if (ctx->state == TECO_BUFFER_DIRTY_BACKEDUP) {
- g_autofree gchar *filename_backup = teco_buffer_get_backup(ctx);
- g_unlink(filename_backup);
- /* on rubout, we do not restore the backup file */
+ if (ctx->state == TECO_BUFFER_DIRTY_DUMPED) {
+ g_autofree gchar *filename_recovery = teco_buffer_get_recovery(ctx);
+ g_unlink(filename_recovery);
+ /* on rubout, we do not restore the recovery file */
ctx->state = TECO_BUFFER_DIRTY;
}
teco_undo_guint(ctx->state) = TECO_BUFFER_CLEAN;
@@ -141,9 +143,9 @@ 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_BACKEDUP) {
- g_autofree gchar *filename_backup = teco_buffer_get_backup(ctx);
- g_unlink(filename_backup);
+ if (ctx->state == TECO_BUFFER_DIRTY_DUMPED) {
+ g_autofree gchar *filename_recovery = teco_buffer_get_recovery(ctx);
+ g_unlink(filename_recovery);
}
teco_view_free(ctx->view);
@@ -242,9 +244,9 @@ teco_ring_find_by_id(teco_int_t id)
static void
teco_ring_undirtify(void)
{
- if (teco_ring_current->state == TECO_BUFFER_DIRTY_BACKEDUP) {
- g_autofree gchar *filename_backup = teco_buffer_get_backup(teco_ring_current);
- g_unlink(filename_backup);
+ if (teco_ring_current->state == TECO_BUFFER_DIRTY_DUMPED) {
+ g_autofree gchar *filename_recovery = teco_buffer_get_recovery(teco_ring_current);
+ g_unlink(filename_recovery);
}
teco_ring_current->state = TECO_BUFFER_CLEAN;
@@ -268,7 +270,7 @@ teco_ring_dirtify(void)
break;
case TECO_BUFFER_DIRTY:
break;
- case TECO_BUFFER_DIRTY_BACKEDUP:
+ case TECO_BUFFER_DIRTY_DUMPED:
/* set to TECO_BUFFER_DIRTY on rubout */
teco_undo_guint(teco_ring_current->state);
break;
@@ -306,22 +308,22 @@ teco_ring_save_all_dirty_buffers(GError **error)
}
/**
- * Backup interval in seconds or 0 if disabled.
+ * Recovery creation interval in seconds or 0 if disabled.
* It's not currently enforced in batch mode.
*/
-guint teco_ring_backup_interval = 5*60;
+guint teco_ring_recovery_interval = 5*60;
/**
- * Back up all dirty buffers.
+ * Create recovery files for all dirty buffers.
*
- * Should be called by the interface every teco_ring_backup_interval seconds.
+ * Should be called by the interface every teco_ring_recovery_interval seconds.
* This does not generate or expect undo tokens, so it can be called
* even when idlying.
*/
void
-teco_ring_backup(void)
+teco_ring_dump_recovery(void)
{
- g_assert(teco_ring_backup_interval > 0);
+ g_assert(teco_ring_recovery_interval > 0);
for (teco_tailq_entry_t *cur = teco_ring_head.first; cur != NULL; cur = cur->next) {
teco_buffer_t *buffer = (teco_buffer_t *)cur;
@@ -333,14 +335,29 @@ teco_ring_backup(void)
if (buffer->state != TECO_BUFFER_DIRTY || !buffer->filename)
continue;
- g_autofree gchar *filename_backup = teco_buffer_get_backup(buffer);
+ g_autofree gchar *filename_recovery = teco_buffer_get_recovery(buffer);
+
+ g_autoptr(GIOChannel) channel = g_io_channel_new_file(filename_recovery, "w", NULL);
+ if (!channel)
+ continue;
+
/*
+ * teco_view_save_to_channel() expects a buffered and blocking channel.
+ */
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, TRUE);
+
+ /*
+ * This does not use teco_view_save_to_file() since we must not
+ * emit undo tokens.
+ *
* FIXME: Errors are silently ignored.
* Should we log warnings instead?
*/
- teco_view_save(buffer->view, filename_backup, NULL);
+ if (!teco_view_save_to_channel(buffer->view, channel, NULL))
+ continue;
- buffer->state = TECO_BUFFER_DIRTY_BACKEDUP;
+ buffer->state = TECO_BUFFER_DIRTY_DUMPED;
}
}
diff --git a/src/ring.h b/src/ring.h
index 1b1727e..e6b0658 100644
--- a/src/ring.h
+++ b/src/ring.h
@@ -29,8 +29,8 @@ typedef enum {
TECO_BUFFER_CLEAN = 0,
/** buffer modified */
TECO_BUFFER_DIRTY,
- /** modified and backup already written */
- TECO_BUFFER_DIRTY_BACKEDUP
+ /** buffer modified and recovery file already written */
+ TECO_BUFFER_DIRTY_DUMPED
} teco_buffer_state_t;
typedef struct teco_buffer_t {
@@ -83,9 +83,9 @@ void teco_ring_dirtify(void);
guint teco_ring_get_first_dirty(void);
gboolean teco_ring_save_all_dirty_buffers(GError **error);
-extern guint teco_ring_backup_interval;
+extern guint teco_ring_recovery_interval;
-void teco_ring_backup(void);
+void teco_ring_dump_recovery(void);
gboolean teco_ring_edit_by_name(const gchar *filename, GError **error);
gboolean teco_ring_edit_by_id(teco_int_t id, GError **error);
diff --git a/src/view.c b/src/view.c
index eeb4d3d..c41a4d4 100644
--- a/src/view.c
+++ b/src/view.c
@@ -508,6 +508,13 @@ teco_undo_remove_file_push(const gchar *filename)
strcpy(ctx, filename);
}
+/**
+ * Save the view's document to the given IO channel.
+ *
+ * @note This must not emit undo tokens as it is also used by teco_ring_dump_recovery().
+ *
+ * @memberof teco_view_t
+ */
gboolean
teco_view_save_to_channel(teco_view_t *ctx, GIOChannel *channel, GError **error)
{