From 4573ecbf9df598d54f8c464b54d632063fe3b720 Mon Sep 17 00:00:00 2001 From: Steven Jackson Date: Tue, 9 Sep 2014 00:28:06 +0100 Subject: [PATCH] Save files atomically to prevent data loss and corruption. --- mousepad/mousepad-file.c | 197 ++++++++++++++++++----------------------------- 1 file changed, 77 insertions(+), 120 deletions(-) diff --git a/mousepad/mousepad-file.c b/mousepad/mousepad-file.c index 667492c..e88a0fd 100644 --- a/mousepad/mousepad-file.c +++ b/mousepad/mousepad-file.c @@ -697,15 +697,13 @@ gboolean mousepad_file_save (MousepadFile *file, GError **error) { - gint fd; gboolean succeed = FALSE; gchar *contents, *p; gchar *encoded; const gchar *charset; GtkTextIter start_iter, end_iter; - gint l, m, length; + gint length; gsize written; - struct stat statb; gchar **chunks; g_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE); @@ -713,144 +711,103 @@ mousepad_file_save (MousepadFile *file, g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_return_val_if_fail (file->filename != NULL, FALSE); - /* open the file */ - fd = g_open (file->filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (G_LIKELY (fd != -1)) - { - /* get the buffer bounds */ - gtk_text_buffer_get_bounds (file->buffer, &start_iter, &end_iter); - - /* get the buffer contents */ - contents = gtk_text_buffer_get_slice (file->buffer, &start_iter, &end_iter, TRUE); - - if (G_LIKELY (contents)) - { - /* get the content length */ - length = strlen (contents); - - /* handle line endings */ - if (file->line_ending == MOUSEPAD_EOL_MAC) - { - /* replace the unix with a mac line ending */ - for (p = contents; *p != '\0'; p++) - if (G_UNLIKELY (*p == '\n')) - *p = '\r'; - } - else if (file->line_ending == MOUSEPAD_EOL_DOS) - { - /* split the contents into chunks */ - chunks = g_strsplit (contents, "\n", -1); - - /* cleanup */ - g_free (contents); - - /* join the chunks with dos line endings in between */ - contents = g_strjoinv ("\r\n", chunks); + /* get the buffer bounds */ + gtk_text_buffer_get_bounds (file->buffer, &start_iter, &end_iter); - /* cleanup */ - g_strfreev (chunks); + /* get the buffer contents */ + contents = gtk_text_buffer_get_slice (file->buffer, &start_iter, &end_iter, TRUE); - /* new contents length */ - length = strlen (contents); - } + if (G_LIKELY (contents)) + { + /* get the content length */ + length = strlen (contents); - /* add and utf-8 bom at the start of the contents if needed */ - if (file->write_bom && mousepad_encoding_is_unicode (file->encoding)) - { - /* realloc the contents string */ - contents = g_realloc (contents, length + 4); + /* handle line endings */ + if (file->line_ending == MOUSEPAD_EOL_MAC) + { + /* replace the unix with a mac line ending */ + for (p = contents; *p != '\0'; p++) + if (G_UNLIKELY (*p == '\n')) + *p = '\r'; + } + else if (file->line_ending == MOUSEPAD_EOL_DOS) + { + /* split the contents into chunks */ + chunks = g_strsplit (contents, "\n", -1); + /* cleanup */ + g_free (contents); - /* move the existing contents 3 bytes */ - g_memmove (contents + 3, contents, length + 1); + /* join the chunks with dos line endings in between */ + contents = g_strjoinv ("\r\n", chunks); + /* cleanup */ + g_strfreev (chunks); - /* write an utf-8 bom at the start of the contents */ - contents[0] = (guchar) 0xef; - contents[1] = (guchar) 0xbb; - contents[2] = (guchar) 0xbf; + /* new contents length */ + length = strlen (contents); + } - /* increase the length */ - length += 3; - } + /* add and utf-8 bom at the start of the contents if needed */ + if (file->write_bom && mousepad_encoding_is_unicode (file->encoding)) + { + /* realloc the contents string */ + contents = g_realloc (contents, length + 4); - /* convert to the encoding if set */ - if (G_UNLIKELY (file->encoding != MOUSEPAD_ENCODING_UTF_8)) - { - /* get the charset */ - charset = mousepad_encoding_get_charset (file->encoding); - if (G_UNLIKELY (charset == NULL)) - goto failed; + /* move the existing contents 3 bytes */ + g_memmove (contents + 3, contents, length + 1); - /* convert the content to the user encoding */ - encoded = g_convert (contents, length, charset, "UTF-8", NULL, &written, error); + /* write an utf-8 bom at the start of the contents */ + contents[0] = (guchar) 0xef; + contents[1] = (guchar) 0xbb; + contents[2] = (guchar) 0xbf; - /* return if nothing was encoded */ - if (G_UNLIKELY (encoded == NULL)) - goto failed; + /* increase the length */ + length += 3; + } - /* cleanup */ - g_free (contents); + /* convert to the encoding if set */ + if (G_UNLIKELY (file->encoding != MOUSEPAD_ENCODING_UTF_8)) + { + /* get the charset */ + charset = mousepad_encoding_get_charset (file->encoding); + if (G_UNLIKELY (charset == NULL)) + goto failed; - /* set the new contents */ - contents = encoded; - length = written; - } + /* convert the content to the user encoding */ + encoded = g_convert (contents, length, charset, "UTF-8", NULL, &written, error); - /* write the buffer to the file */ - for (m = 0; m < length;) - { - /* write */ - l = write (fd, contents + m, length - m); + /* return if nothing was encoded */ + if (G_UNLIKELY (encoded == NULL)) + goto failed; - if (G_UNLIKELY (l < 0)) - { - /* just try again on EAGAIN/EINTR */ - if (G_LIKELY (errno != EAGAIN && errno != EINTR)) - { - /* set an error */ - g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s", g_strerror (errno)); - - /* bail out */ - goto failed; - } - } - else - { - /* advance the offset */ - m += l; - } - } + /* cleanup */ + g_free (contents); - /* set the new modification time */ - if (G_LIKELY (fstat (fd, &statb) == 0)) - file->mtime = statb.st_mtime; + /* set the new contents */ + contents = encoded; + length = written; + } - /* everything has been saved */ - gtk_text_buffer_set_modified (file->buffer, FALSE); + if(G_UNLIKELY(!g_file_set_contents(file->filename, contents, length, error))) + goto failed; - /* we saved succesfully */ - mousepad_file_set_readonly (file, FALSE); + /* everything has been saved */ + gtk_text_buffer_set_modified (file->buffer, FALSE); - /* if the user hasn't set the filetype, try and re-guess it now - * that we have a new filename to go by */ - if (! file->user_set_language) - mousepad_file_set_language (file, mousepad_file_guess_language (file)); + /* we saved succesfully */ + mousepad_file_set_readonly (file, FALSE); - /* everything went file */ - succeed = TRUE; + /* if the user hasn't set the filetype, try and re-guess it now + * that we have a new filename to go by */ + if (! file->user_set_language) + mousepad_file_set_language (file, mousepad_file_guess_language (file)); - failed: + /* everything went file */ + succeed = TRUE; - /* cleanup */ - g_free (contents); + failed: - /* close the file */ - close (fd); - } - } - else - { - /* set an error */ - g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s", g_strerror (errno)); + /* cleanup */ + g_free (contents); } return succeed; -- 1.9.1