aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobin Haberkorn <rhaberkorn@fmsbw.de>2025-12-19 23:25:48 +0100
committerRobin Haberkorn <rhaberkorn@fmsbw.de>2025-12-19 23:25:48 +0100
commit2592ef74ab2eba57c32fe21993ce01e9698b106f (patch)
treeb733680e79aab862c0ba748bb91016a752c8773a
parent714875f3c0c22ed01a8e777755b281c97f2b52b8 (diff)
fixup: renamed "backups" to "recovery files"HEADmaster-fmsbw-cimaster
* Other editors call "backup files" previous copies of saved files. This role would be served by savepoint files in SciTECO. * Likewise filename~ would point to such a backup file. It therefore makes sense that savepoint files also end in tildes (.teco-n-filename~). * Security copies of modified buffers would be called "auto-saves" (Emacs) or "swap files" (Vim). Both of these terms is IMHO misleading, so SciTECO now uses the term "recovery file". * "Recovery files" are now named #filename# just like in Emacs.
-rw-r--r--README4
-rw-r--r--TODO6
-rw-r--r--doc/sciteco.1.in20
-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.c46
-rw-r--r--src/ring.h8
-rw-r--r--src/view.c2
9 files changed, 83 insertions, 70 deletions
diff --git a/README b/README
index 4f3b7b5..d110285 100644
--- a/README
+++ b/README
@@ -73,7 +73,9 @@ Features
This makes it even harder to destroy work by accident than in most other
editors.
Rubbed out commands can be re-inserted (redo).
-* Timing-based backup mechanism
+* Timing-based recovery mechanism:
+ Modified buffers are regularily dumped into **#**_files_**#** to protect against
+ crashes and unexpected restarts etc.
* Munging: Macros may be munged, that is executed in batch mode. In other words, SciTECO
can be used for scripting.
By default, a profile is munged.
diff --git a/TODO b/TODO
index ed3efda..6aca77e 100644
--- a/TODO
+++ b/TODO
@@ -481,9 +481,9 @@ Features:
* Touch restored save point files - should perhaps be configurable.
This is important when working with Makefiles, as make looks
at the modification times of files.
- * Could we somehow offer to open backup~ files?
- opener.tes and session.tes operate in batch mode and interactively
- asking what to do wouldn't always work in the GUI variants.
+ * Could we somehow offer to open #recovery# files?
+ This would require a hook on interactive startup and/or after
+ command-line termination.
Also, we'd need a command to fetch the modification timestamp
as well.
* Error handling in SciTECO macros: Allow throwing errors with
diff --git a/doc/sciteco.1.in b/doc/sciteco.1.in
index 0370c50..e47ca93 100644
--- a/doc/sciteco.1.in
+++ b/doc/sciteco.1.in
@@ -450,20 +450,22 @@ and opening files specified on the command line.
.B $SCITECOPATH/*.tes
Standard library macros.
.TP
-.SCITECO_TOPIC backup
-.IB filename ~
-Backup file:
-After a configurable backup interval (5 minutes by default)
-\*(ST backs up all modified buffers, that have not been backed up
-previously.
-These files should be ignored by version control systems.
+.BI # filename #
+Recovery file:
+After a configurable recovery interval \*(ST dumps up all modified buffers
+(unsaved changes), that have not been dumped previously.
+These files should be ignored by version control systems and may
+be left around after crashes and unexpected restarts.
+See \fB6EJ\fP for more details.
.TP
-.SCITECO_TOPIC savepoint
+.SCITECO_TOPIC savepoint backup
.BI .teco- n - filename ~
Save point files created by \*(ST when saving files
during interactive execution have this format.
On Windows, these files have the hidden attribute set.
-Savepoint files are temporary and should be ignored by version
+They are internally used when rubbing out file saves
+and are conceptually similar to backup files in other editors.
+However they are temporary and should be ignored by version
control systems, etc.
.TP
.SCITECO_TOPIC ".teco_session" session
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 afd6b25..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,9 +335,9 @@ 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_backup, "w", NULL);
+ g_autoptr(GIOChannel) channel = g_io_channel_new_file(filename_recovery, "w", NULL);
if (!channel)
continue;
@@ -355,7 +357,7 @@ teco_ring_backup(void)
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 d5b5d14..c41a4d4 100644
--- a/src/view.c
+++ b/src/view.c
@@ -511,7 +511,7 @@ teco_undo_remove_file_push(const gchar *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_backup().
+ * @note This must not emit undo tokens as it is also used by teco_ring_dump_recovery().
*
* @memberof teco_view_t
*/