diff options
| author | Robin Haberkorn <rhaberkorn@fmsbw.de> | 2025-12-19 23:25:48 +0100 |
|---|---|---|
| committer | Robin Haberkorn <rhaberkorn@fmsbw.de> | 2025-12-19 23:25:48 +0100 |
| commit | 2592ef74ab2eba57c32fe21993ce01e9698b106f (patch) | |
| tree | b733680e79aab862c0ba748bb91016a752c8773a | |
| parent | 714875f3c0c22ed01a8e777755b281c97f2b52b8 (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-- | README | 4 | ||||
| -rw-r--r-- | TODO | 6 | ||||
| -rw-r--r-- | doc/sciteco.1.in | 20 | ||||
| -rw-r--r-- | src/core-commands.c | 25 | ||||
| -rw-r--r-- | src/interface-curses/interface.c | 28 | ||||
| -rw-r--r-- | src/interface-gtk/interface.c | 14 | ||||
| -rw-r--r-- | src/ring.c | 46 | ||||
| -rw-r--r-- | src/ring.h | 8 | ||||
| -rw-r--r-- | src/view.c | 2 |
9 files changed, 83 insertions, 70 deletions
@@ -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. @@ -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; } @@ -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; } } @@ -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); @@ -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 */ |
