summaryrefslogtreecommitdiff
path: root/src/dged/buffer.c
diff options
context:
space:
mode:
authorAlbert Cervin <albert@acervin.com>2025-11-01 22:35:36 +0100
committerAlbert Cervin <albert@acervin.com>2025-11-01 22:35:36 +0100
commit25bac4d2703a0c529b7b97cf86eb07b6264e1242 (patch)
treeab28c2a8406f0df4dfbb968384a1f246b1e4a84c /src/dged/buffer.c
parent0f0bbf4534bbd1d6987dc655952d873c12127a45 (diff)
downloaddged-25bac4d2703a0c529b7b97cf86eb07b6264e1242.tar.gz
dged-25bac4d2703a0c529b7b97cf86eb07b6264e1242.tar.xz
dged-25bac4d2703a0c529b7b97cf86eb07b6264e1242.zip
Implement safer file saving
It now uses the common approach of saving to a different file and then using rename to the desired filename after the file has been fully written, thus not corrupting the file in case of a crash when writing the file.
Diffstat (limited to 'src/dged/buffer.c')
-rw-r--r--src/dged/buffer.c24
1 files changed, 20 insertions, 4 deletions
diff --git a/src/dged/buffer.c b/src/dged/buffer.c
index dcaa42c..a45f982 100644
--- a/src/dged/buffer.c
+++ b/src/dged/buffer.c
@@ -396,11 +396,17 @@ void buffer_to_file(struct buffer *buffer) {
}
char *fullname = expanduser(buffer->filename);
- FILE *file = fopen(fullname, "w");
- free(fullname);
+ size_t namelen = strlen(fullname);
+ char *backupname = malloc(namelen + 6);
+ memcpy(backupname, fullname, namelen);
+ memcpy(backupname + namelen, ".save", 5);
+ backupname[namelen + 5] = '\0';
+ FILE *file = fopen(backupname, "w+");
if (file == NULL) {
- minibuffer_echo("failed to open file %s for writing: %s", buffer->filename,
- strerror(errno));
+ minibuffer_echo("failed to open file \"%s\" (\"%s\") for writing: %s",
+ buffer->filename, backupname, strerror(errno));
+ free(fullname);
+ free(backupname);
return;
}
@@ -417,6 +423,16 @@ void buffer_to_file(struct buffer *buffer) {
minibuffer_echo_timeout(4, "wrote %d lines to %s", nlines_to_write,
buffer->filename);
fclose(file);
+ if (rename(backupname, fullname) == -1) {
+ minibuffer_echo("failed to rename backup \"%s\" to \"%s\": %s", backupname,
+ fullname, strerror(errno));
+ free(fullname);
+ free(backupname);
+ return;
+ }
+
+ free(fullname);
+ free(backupname);
buffer->modified = false;
undo_push_boundary(&buffer->undo, (struct undo_boundary){.save_point = true});