diff options
| author | Albert Cervin <albert@acervin.com> | 2025-11-01 22:35:36 +0100 |
|---|---|---|
| committer | Albert Cervin <albert@acervin.com> | 2025-11-01 22:35:36 +0100 |
| commit | 25bac4d2703a0c529b7b97cf86eb07b6264e1242 (patch) | |
| tree | ab28c2a8406f0df4dfbb968384a1f246b1e4a84c /src/dged/buffer.c | |
| parent | 0f0bbf4534bbd1d6987dc655952d873c12127a45 (diff) | |
| download | dged-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.c | 24 |
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}); |
