summaryrefslogtreecommitdiff
path: root/pluma
diff options
context:
space:
mode:
authormbkma <[email protected]>2020-07-24 11:27:31 +0200
committerGitHub <[email protected]>2020-07-24 11:27:31 +0200
commit8f02e21f3703c9549fb357986f77c9534186f2ab (patch)
treead79924d518d989fae3775edd902469f1ee5bf26 /pluma
parentadb2ddb89a4d8c55db5ab576b085de05ef048068 (diff)
downloadpluma-8f02e21f3703c9549fb357986f77c9534186f2ab.tar.bz2
pluma-8f02e21f3703c9549fb357986f77c9534186f2ab.tar.xz
Merge gio document loader and saver into document loader and saver
Backport from: https://gitlab.gnome.org/GNOME/gedit/-/commit/4bd74a1f47a3fa41385ffae3bb78aeb5afabb564 See: https://bugzilla.gnome.org/show_bug.cgi?id=617215
Diffstat (limited to 'pluma')
-rw-r--r--pluma/Makefile.am4
-rw-r--r--pluma/pluma-document-loader.c982
-rw-r--r--pluma/pluma-document-loader.h60
-rw-r--r--pluma/pluma-document-saver.c1101
-rw-r--r--pluma/pluma-document-saver.h79
-rw-r--r--pluma/pluma-gio-document-loader.c702
-rw-r--r--pluma/pluma-gio-document-loader.h79
-rw-r--r--pluma/pluma-gio-document-saver.c778
-rw-r--r--pluma/pluma-gio-document-saver.h76
9 files changed, 1721 insertions, 2140 deletions
diff --git a/pluma/Makefile.am b/pluma/Makefile.am
index 9e4857bf..e7c0639d 100644
--- a/pluma/Makefile.am
+++ b/pluma/Makefile.am
@@ -48,8 +48,6 @@ NOINST_H_FILES = \
pluma-document-saver.h \
pluma-documents-panel.h \
pluma-file-chooser-dialog.h \
- pluma-gio-document-loader.h \
- pluma-gio-document-saver.h \
pluma-history-entry.h \
pluma-io-error-message-area.h \
pluma-language-manager.h \
@@ -113,9 +111,7 @@ libpluma_c_files = \
pluma-document-input-stream.c \
pluma-document-loader.c \
pluma-document-output-stream.c \
- pluma-gio-document-loader.c \
pluma-document-saver.c \
- pluma-gio-document-saver.c \
pluma-documents-panel.c \
pluma-encodings.c \
pluma-encodings-combo-box.c \
diff --git a/pluma/pluma-document-loader.c b/pluma/pluma-document-loader.c
index 5b50b779..aea0296f 100644
--- a/pluma/pluma-document-loader.c
+++ b/pluma/pluma-document-loader.c
@@ -34,24 +34,36 @@
#endif
#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
#include "pluma-document-loader.h"
+#include "pluma-document-output-stream.h"
+#include "pluma-smart-charset-converter.h"
+#include "pluma-prefs-manager.h"
#include "pluma-debug.h"
#include "pluma-metadata-manager.h"
#include "pluma-utils.h"
#include "pluma-marshal.h"
#include "pluma-enum-types.h"
-/* Those are for the the pluma_document_loader_new() factory */
-#include "pluma-gio-document-loader.h"
+#ifndef ENABLE_GVFS_METADATA
+#include "pluma-metadata-manager.h"
+#endif
-G_DEFINE_ABSTRACT_TYPE(PlumaDocumentLoader, pluma_document_loader, G_TYPE_OBJECT)
+typedef struct
+{
+ PlumaDocumentLoader *loader;
+ GCancellable *cancellable;
+ gssize read;
+ gboolean tried_mount;
+} AsyncData;
/* Signals */
enum {
- LOADING,
- LAST_SIGNAL
+ LOADING,
+ LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
@@ -60,298 +72,866 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum
{
- PROP_0,
- PROP_DOCUMENT,
- PROP_URI,
- PROP_ENCODING,
- PROP_NEWLINE_TYPE
+ PROP_0,
+ PROP_DOCUMENT,
+ PROP_URI,
+ PROP_ENCODING,
+ PROP_NEWLINE_TYPE
};
+#define READ_CHUNK_SIZE 8192
+#define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
+ G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
+ G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
+ G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," \
+ G_FILE_ATTRIBUTE_STANDARD_SIZE "," \
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," \
+ PLUMA_METADATA_ATTRIBUTE_ENCODING
+
+static void open_async_read (AsyncData *async);
+
+struct _PlumaDocumentLoaderPrivate
+{
+ PlumaDocument *document;
+ gboolean used;
+
+ /* Info on the current file */
+ GFileInfo *info;
+ gchar *uri;
+ const PlumaEncoding *encoding;
+ const PlumaEncoding *auto_detected_encoding;
+ PlumaDocumentNewlineType auto_detected_newline_type;
+ GFile *gfile;
+ goffset bytes_read;
+
+ /* Handle for remote files */
+ GCancellable *cancellable;
+ GInputStream *stream;
+ GOutputStream *output;
+ PlumaSmartCharsetConverter *converter;
+
+ gchar buffer[READ_CHUNK_SIZE];
+
+ GError *error;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (PlumaDocumentLoader, pluma_document_loader, G_TYPE_OBJECT)
+
static void
pluma_document_loader_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- PlumaDocumentLoader *loader = PLUMA_DOCUMENT_LOADER (object);
-
- switch (prop_id)
- {
- case PROP_DOCUMENT:
- g_return_if_fail (loader->document == NULL);
- loader->document = g_value_get_object (value);
- break;
- case PROP_URI:
- g_return_if_fail (loader->uri == NULL);
- loader->uri = g_value_dup_string (value);
- break;
- case PROP_ENCODING:
- g_return_if_fail (loader->encoding == NULL);
- loader->encoding = g_value_get_boxed (value);
- break;
- case PROP_NEWLINE_TYPE:
- loader->auto_detected_newline_type = g_value_get_enum (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PlumaDocumentLoader *loader = PLUMA_DOCUMENT_LOADER (object);
+
+ switch (prop_id)
+ {
+ case PROP_DOCUMENT:
+ g_return_if_fail (loader->priv->document == NULL);
+ loader->priv->document = g_value_get_object (value);
+ break;
+ case PROP_URI:
+ g_return_if_fail (loader->priv->uri == NULL);
+ loader->priv->uri = g_value_dup_string (value);
+ break;
+ case PROP_ENCODING:
+ g_return_if_fail (loader->priv->encoding == NULL);
+ loader->priv->encoding = g_value_get_boxed (value);
+ break;
+ case PROP_NEWLINE_TYPE:
+ loader->priv->auto_detected_newline_type = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
pluma_document_loader_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- PlumaDocumentLoader *loader = PLUMA_DOCUMENT_LOADER (object);
-
- switch (prop_id)
- {
- case PROP_DOCUMENT:
- g_value_set_object (value, loader->document);
- break;
- case PROP_URI:
- g_value_set_string (value, loader->uri);
- break;
- case PROP_ENCODING:
- g_value_set_boxed (value, pluma_document_loader_get_encoding (loader));
- break;
- case PROP_NEWLINE_TYPE:
- g_value_set_enum (value, loader->auto_detected_newline_type);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PlumaDocumentLoader *loader = PLUMA_DOCUMENT_LOADER (object);
+
+ switch (prop_id)
+ {
+ case PROP_DOCUMENT:
+ g_value_set_object (value, loader->priv->document);
+ break;
+ case PROP_URI:
+ g_value_set_string (value, loader->priv->uri);
+ break;
+ case PROP_ENCODING:
+ g_value_set_boxed (value, pluma_document_loader_get_encoding (loader));
+ break;
+ case PROP_NEWLINE_TYPE:
+ g_value_set_enum (value, loader->priv->auto_detected_newline_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
pluma_document_loader_finalize (GObject *object)
{
- PlumaDocumentLoader *loader = PLUMA_DOCUMENT_LOADER (object);
-
- g_free (loader->uri);
+ PlumaDocumentLoaderPrivate *priv = pluma_document_loader_get_instance_private (PLUMA_DOCUMENT_LOADER(object));
- if (loader->info)
- g_object_unref (loader->info);
+ g_free (priv->uri);
- G_OBJECT_CLASS (pluma_document_loader_parent_class)->finalize (object);
+ G_OBJECT_CLASS (pluma_document_loader_parent_class)->finalize (object);
}
static void
pluma_document_loader_dispose (GObject *object)
{
- PlumaDocumentLoader *loader = PLUMA_DOCUMENT_LOADER (object);
-
- if (loader->info != NULL)
- {
- g_object_unref (loader->info);
- loader->info = NULL;
- }
-
- G_OBJECT_CLASS (pluma_document_loader_parent_class)->dispose (object);
+ PlumaDocumentLoaderPrivate *priv = pluma_document_loader_get_instance_private (PLUMA_DOCUMENT_LOADER(object));
+
+ if (priv->cancellable != NULL)
+ {
+ g_cancellable_cancel (priv->cancellable);
+ g_object_unref (priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
+ if (priv->stream != NULL)
+ {
+ g_object_unref (priv->stream);
+ priv->stream = NULL;
+ }
+
+ if (priv->output != NULL)
+ {
+ g_object_unref (priv->output);
+ priv->output = NULL;
+ }
+
+ if (priv->converter != NULL)
+ {
+ g_object_unref (priv->converter);
+ priv->converter = NULL;
+ }
+
+ if (priv->gfile != NULL)
+ {
+ g_object_unref (priv->gfile);
+ priv->gfile = NULL;
+ }
+
+ if (priv->error != NULL)
+ {
+ g_error_free (priv->error);
+ priv->error = NULL;
+ }
+
+ if (priv->info != NULL)
+ {
+ g_object_unref (priv->info);
+ priv->info = NULL;
+ }
+
+ G_OBJECT_CLASS (pluma_document_loader_parent_class)->dispose (object);
}
static void
pluma_document_loader_class_init (PlumaDocumentLoaderClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->finalize = pluma_document_loader_finalize;
- object_class->dispose = pluma_document_loader_dispose;
- object_class->get_property = pluma_document_loader_get_property;
- object_class->set_property = pluma_document_loader_set_property;
-
- g_object_class_install_property (object_class,
- PROP_DOCUMENT,
- g_param_spec_object ("document",
- "Document",
- "The PlumaDocument this PlumaDocumentLoader is associated with",
- PLUMA_TYPE_DOCUMENT,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (object_class,
- PROP_URI,
- g_param_spec_string ("uri",
- "URI",
- "The URI this PlumaDocumentLoader loads the document from",
- "",
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property (object_class,
- PROP_ENCODING,
- g_param_spec_boxed ("encoding",
- "Encoding",
- "The encoding of the saved file",
- PLUMA_TYPE_ENCODING,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (object_class, PROP_NEWLINE_TYPE,
- g_param_spec_enum ("newline-type",
- "Newline type",
- "The accepted types of line ending",
- PLUMA_TYPE_DOCUMENT_NEWLINE_TYPE,
- PLUMA_DOCUMENT_NEWLINE_TYPE_LF,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB));
-
- signals[LOADING] =
- g_signal_new ("loading",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PlumaDocumentLoaderClass, loading),
- NULL, NULL,
- pluma_marshal_VOID__BOOLEAN_POINTER,
- G_TYPE_NONE,
- 2,
- G_TYPE_BOOLEAN,
- G_TYPE_POINTER);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = pluma_document_loader_dispose;
+ object_class->finalize = pluma_document_loader_finalize;
+ object_class->get_property = pluma_document_loader_get_property;
+ object_class->set_property = pluma_document_loader_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_DOCUMENT,
+ g_param_spec_object ("document",
+ "Document",
+ "The PlumaDocument this PlumaDocumentLoader is associated with",
+ PLUMA_TYPE_DOCUMENT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_URI,
+ g_param_spec_string ("uri",
+ "URI",
+ "The URI this PlumaDocumentLoader loads the document from",
+ "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_ENCODING,
+ g_param_spec_boxed ("encoding",
+ "Encoding",
+ "The encoding of the saved file",
+ PLUMA_TYPE_ENCODING,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_NEWLINE_TYPE,
+ g_param_spec_enum ("newline-type",
+ "Newline type",
+ "The accepted types of line ending",
+ PLUMA_TYPE_DOCUMENT_NEWLINE_TYPE,
+ PLUMA_DOCUMENT_NEWLINE_TYPE_LF,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB));
+
+ signals[LOADING] =
+ g_signal_new ("loading",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PlumaDocumentLoaderClass, loading),
+ NULL, NULL,
+ pluma_marshal_VOID__BOOLEAN_POINTER,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_BOOLEAN,
+ G_TYPE_POINTER);
}
static void
pluma_document_loader_init (PlumaDocumentLoader *loader)
{
- loader->used = FALSE;
- loader->auto_detected_newline_type = PLUMA_DOCUMENT_NEWLINE_TYPE_DEFAULT;
+ loader->priv = pluma_document_loader_get_instance_private (loader);
+
+ loader->priv->used = FALSE;
+ loader->priv->auto_detected_newline_type = PLUMA_DOCUMENT_NEWLINE_TYPE_DEFAULT;
+ loader->priv->converter = NULL;
+ loader->priv->error = NULL;
}
-void
-pluma_document_loader_loading (PlumaDocumentLoader *loader,
- gboolean completed,
- GError *error)
+PlumaDocumentLoader *
+pluma_document_loader_new (PlumaDocument *doc,
+ const gchar *uri,
+ const PlumaEncoding *encoding)
{
- /* the object will be unrefed in the callback of the loading signal
- * (when completed == TRUE), so we need to prevent finalization.
- */
- if (completed)
- {
- g_object_ref (loader);
- }
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
- g_signal_emit (loader, signals[LOADING], 0, completed, error);
+ return PLUMA_DOCUMENT_LOADER (g_object_new (PLUMA_TYPE_DOCUMENT_LOADER,
+ "document", doc,
+ "uri", uri,
+ "encoding", encoding,
+ NULL));
+}
- if (completed)
- {
- if (error == NULL)
- pluma_debug_message (DEBUG_LOADER, "load completed");
- else
- pluma_debug_message (DEBUG_LOADER, "load failed");
+static AsyncData *
+async_data_new (PlumaDocumentLoader *loader)
+{
+ AsyncData *async;
- g_object_unref (loader);
- }
+ async = g_slice_new (AsyncData);
+ async->loader = loader;
+ async->cancellable = g_object_ref (loader->priv->cancellable);
+ async->tried_mount = FALSE;
+
+ return async;
}
-/* This is a factory method that returns an appopriate loader
- * for the given uri.
- */
-PlumaDocumentLoader *
-pluma_document_loader_new (PlumaDocument *doc,
- const gchar *uri,
- const PlumaEncoding *encoding)
+static void
+async_data_free (AsyncData *async)
+{
+ g_object_unref (async->cancellable);
+ g_slice_free (AsyncData, async);
+}
+
+static const PlumaEncoding *
+get_metadata_encoding (PlumaDocumentLoader *loader)
+{
+ const PlumaEncoding *enc = NULL;
+
+#ifndef ENABLE_GVFS_METADATA
+ gchar *charset;
+ const gchar *uri;
+
+ uri = pluma_document_loader_get_uri (loader);
+
+ charset = pluma_metadata_manager_get (uri, "encoding");
+
+ if (charset == NULL)
+ return NULL;
+
+ enc = pluma_encoding_get_from_charset (charset);
+
+ g_free (charset);
+#else
+ GFileInfo *info;
+
+ info = pluma_document_loader_get_info (loader);
+
+ /* check if the encoding was set in the metadata */
+ if (g_file_info_has_attribute (info, PLUMA_METADATA_ATTRIBUTE_ENCODING))
+ {
+ const gchar *charset;
+
+ charset = g_file_info_get_attribute_string (info, PLUMA_METADATA_ATTRIBUTE_ENCODING);
+
+ if (charset == NULL)
+ return NULL;
+
+ enc = pluma_encoding_get_from_charset (charset);
+ }
+#endif
+
+ return enc;
+}
+
+static void
+remote_load_completed_or_failed (PlumaDocumentLoader *loader, AsyncData *async)
+{
+ pluma_document_loader_loading (loader,
+ TRUE,
+ loader->priv->error);
+
+ if (async)
+ async_data_free (async);
+}
+
+static void
+async_failed (AsyncData *async, GError *error)
+{
+ g_propagate_error (&async->loader->priv->error, error);
+ remote_load_completed_or_failed (async->loader, async);
+}
+
+static void
+close_input_stream_ready_cb (GInputStream *stream,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ GError *error = NULL;
+
+ pluma_debug (DEBUG_LOADER);
+
+ /* check cancelled state manually */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ pluma_debug_message (DEBUG_SAVER, "Finished closing input stream");
+
+ if (!g_input_stream_close_finish (stream, res, &error))
+ {
+ pluma_debug_message (DEBUG_SAVER, "Closing input stream error: %s", error->message);
+
+ async_failed (async, error);
+ return;
+ }
+
+ pluma_debug_message (DEBUG_SAVER, "Close output stream");
+ if (!g_output_stream_close (async->loader->priv->output, async->cancellable, &error))
+ {
+ async_failed (async, error);
+ return;
+ }
+
+ remote_load_completed_or_failed (async->loader, async);
+}
+
+static void
+write_complete (AsyncData *async)
+{
+ if (async->loader->priv->stream)
+ g_input_stream_close_async (G_INPUT_STREAM (async->loader->priv->stream),
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback)close_input_stream_ready_cb,
+ async);
+}
+
+/* prototype, because they call each other... isn't C lovely */
+static void read_file_chunk (AsyncData *async);
+
+static void
+write_file_chunk (AsyncData *async)
{
- PlumaDocumentLoader *loader;
- GType loader_type;
+ PlumaDocumentLoader *loader;
+ gssize bytes_written;
+ GError *error = NULL;
+
+ loader = async->loader;
+
+ /* we use sync methods on doc stream since it is in memory. Using async
+ would be racy and we can endup with invalidated iters */
+ bytes_written = g_output_stream_write (G_OUTPUT_STREAM (loader->priv->output),
+ loader->priv->buffer,
+ async->read,
+ async->cancellable,
+ &error);
+
+ pluma_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written);
+ if (bytes_written == -1)
+ {
+ pluma_debug_message (DEBUG_SAVER, "Write error: %s", error->message);
+ async_failed (async, error);
+ return;
+ }
+
+ /* note that this signal blocks the read... check if it isn't
+ * a performance problem
+ */
+ pluma_document_loader_loading (loader, FALSE, NULL);
+
+ read_file_chunk (async);
+}
+
+static void
+async_read_cb (GInputStream *stream,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ pluma_debug (DEBUG_LOADER);
+ PlumaDocumentLoader *loader;
+ GError *error = NULL;
+
+ pluma_debug (DEBUG_LOADER);
+
+ /* manually check cancelled state */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ loader = async->loader;
+
+ async->read = g_input_stream_read_finish (stream, res, &error);
+
+ /* error occurred */
+ if (async->read == -1)
+ {
+ async_failed (async, error);
+ return;
+ }
+
+ /* Check for the extremely unlikely case where the file size overflows. */
+ if (loader->priv->bytes_read + async->read < loader->priv->bytes_read)
+ {
+ g_set_error (&loader->priv->error,
+ PLUMA_DOCUMENT_ERROR,
+ PLUMA_DOCUMENT_ERROR_TOO_BIG,
+ "File too big");
+
+ async_failed (async, loader->priv->error);
+ return;
+ }
+
+ /* Bump the size. */
+ loader->priv->bytes_read += async->read;
+
+ /* end of the file, we are done! */
+ if (async->read == 0)
+ {
+ g_output_stream_flush (loader->priv->output,
+ NULL,
+ &loader->priv->error);
+
+ loader->priv->auto_detected_encoding =
+ pluma_smart_charset_converter_get_guessed (loader->priv->converter);
+
+ loader->priv->auto_detected_newline_type =
+ pluma_document_output_stream_detect_newline_type (PLUMA_DOCUMENT_OUTPUT_STREAM (loader->priv->output));
+
+ /* Check if we needed some fallback char, if so, check if there was
+ a previous error and if not set a fallback used error */
+ /* FIXME Uncomment this when we want to manage conversion fallback */
+ /*if ((pluma_smart_charset_converter_get_num_fallbacks (loader->priv->converter) != 0) &&
+ loader->priv->error == NULL)
+ {
+ g_set_error_literal (&loader->priv->error,
+ PLUMA_DOCUMENT_ERROR,
+ PLUMA_DOCUMENT_ERROR_CONVERSION_FALLBACK,
+ "There was a conversion error and it was "
+ "needed to use a fallback char");
+ }*/
+
+ write_complete (async);
+
+ return;
+ }
+
+ write_file_chunk (async);
+}
+
+static void
+read_file_chunk (AsyncData *async)
+{
+ PlumaDocumentLoader *loader;
+
+ loader = async->loader;
+
+ g_input_stream_read_async (G_INPUT_STREAM (loader->priv->stream),
+ loader->priv->buffer,
+ READ_CHUNK_SIZE,
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback) async_read_cb,
+ async);
+}
+
+static GSList *
+get_candidate_encodings (PlumaDocumentLoader *loader)
+{
+ const PlumaEncoding *metadata;
+ GSList *encodings = NULL;
+
+ encodings = pluma_prefs_manager_get_auto_detected_encodings ();
+
+ metadata = get_metadata_encoding (loader);
+ if (metadata != NULL)
+ {
+ encodings = g_slist_prepend (encodings, (gpointer)metadata);
+ }
+
+ return encodings;
+}
+
+static void
+finish_query_info (AsyncData *async)
+{
+ PlumaDocumentLoader *loader;
+ GInputStream *conv_stream;
+ GFileInfo *info;
+ GSList *candidate_encodings;
+
+ loader = async->loader;
+ info = loader->priv->info;
+
+ /* if it's not a regular file, error out... */
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) &&
+ g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
+ {
+ g_set_error (&loader->priv->error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_REGULAR_FILE,
+ "Not a regular file");
+
+ remote_load_completed_or_failed (loader, async);
+
+ return;
+ }
+
+ /* Get the candidate encodings */
+ if (loader->priv->encoding == NULL)
+ {
+ candidate_encodings = get_candidate_encodings (loader);
+ }
+ else
+ {
+ candidate_encodings = g_slist_prepend (NULL, (gpointer) loader->priv->encoding);
+ }
+
+ loader->priv->converter = pluma_smart_charset_converter_new (candidate_encodings);
+ g_slist_free (candidate_encodings);
+
+ conv_stream = g_converter_input_stream_new (loader->priv->stream,
+ G_CONVERTER (loader->priv->converter));
- g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
+ g_object_unref (loader->priv->stream);
- /* At the moment we just use gio loader in all cases...
- * In the future it would be great to have a PolicyKit
- * loader to get permission to save systen files etc */
- loader_type = PLUMA_TYPE_GIO_DOCUMENT_LOADER;
+ loader->priv->stream = conv_stream;
- loader = PLUMA_DOCUMENT_LOADER (g_object_new (loader_type,
- "document", doc,
- "uri", uri,
- "encoding", encoding,
- NULL));
+ /* Output stream */
+ loader->priv->output = pluma_document_output_stream_new (loader->priv->document);
- return loader;
+ /* start reading */
+ read_file_chunk (async);
+}
+
+static void
+query_info_cb (GFile *source,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ GFileInfo *info;
+ GError *error = NULL;
+
+ pluma_debug (DEBUG_LOADER);
+
+ /* manually check the cancelled state */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ /* finish the info query */
+ info = g_file_query_info_finish (async->loader->priv->gfile,
+ res,
+ &error);
+
+ if (info == NULL)
+ {
+ /* propagate the error and clean up */
+ async_failed (async, error);
+ return;
+ }
+
+ async->loader->priv->info = info;
+
+ finish_query_info (async);
+}
+
+static void
+mount_ready_callback (GFile *file,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ GError *error = NULL;
+ gboolean mounted;
+
+ pluma_debug (DEBUG_LOADER);
+
+ /* manual check for cancelled state */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
+
+ if (!mounted)
+ {
+ async_failed (async, error);
+ }
+ else
+ {
+ /* try again to open the file for reading */
+ open_async_read (async);
+ }
+}
+
+static void
+recover_not_mounted (AsyncData *async)
+{
+ PlumaDocument *doc;
+ GMountOperation *mount_operation;
+
+ pluma_debug (DEBUG_LOADER);
+
+ doc = pluma_document_loader_get_document (async->loader);
+ mount_operation = _pluma_document_create_mount_operation (doc);
+
+ async->tried_mount = TRUE;
+ g_file_mount_enclosing_volume (async->loader->priv->gfile,
+ G_MOUNT_MOUNT_NONE,
+ mount_operation,
+ async->cancellable,
+ (GAsyncReadyCallback) mount_ready_callback,
+ async);
+
+ g_object_unref (mount_operation);
+}
+
+static void
+async_read_ready_callback (GObject *source,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ GError *error = NULL;
+ PlumaDocumentLoader *loader;
+
+ pluma_debug (DEBUG_LOADER);
+
+ /* manual check for cancelled state */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ loader = async->loader;
+
+ loader->priv->stream = G_INPUT_STREAM (g_file_read_finish (loader->priv->gfile,
+ res, &error));
+
+ if (!loader->priv->stream)
+ {
+ if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount)
+ {
+ recover_not_mounted (async);
+ g_error_free (error);
+ return;
+ }
+
+ /* Propagate error */
+ g_propagate_error (&loader->priv->error, error);
+ pluma_document_loader_loading (loader,
+ TRUE,
+ loader->priv->error);
+
+ async_data_free (async);
+ return;
+ }
+
+ /* get the file info: note we cannot use
+ * g_file_input_stream_query_info_async since it is not able to get the
+ * content type etc, beside it is not supported by gvfs.
+ * Using the file instead of the stream is slightly racy, but for
+ * loading this is not too bad...
+ */
+ g_file_query_info_async (loader->priv->gfile,
+ REMOTE_QUERY_ATTRIBUTES,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback) query_info_cb,
+ async);
+}
+
+static void
+open_async_read (AsyncData *async)
+{
+ g_file_read_async (async->loader->priv->gfile,
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback) async_read_ready_callback,
+ async);
+}
+
+void
+pluma_document_loader_loading (PlumaDocumentLoader *loader,
+ gboolean completed,
+ GError *error)
+{
+ /* the object will be unrefed in the callback of the loading signal
+ * (when completed == TRUE), so we need to prevent finalization.
+ */
+ if (completed)
+ {
+ g_object_ref (loader);
+ }
+
+ g_signal_emit (loader, signals[LOADING], 0, completed, error);
+
+ if (completed)
+ {
+ if (error == NULL)
+ pluma_debug_message (DEBUG_LOADER, "load completed");
+ else
+ pluma_debug_message (DEBUG_LOADER, "load failed");
+
+ g_object_unref (loader);
+ }
}
/* If enconding == NULL, the encoding will be autodetected */
void
pluma_document_loader_load (PlumaDocumentLoader *loader)
{
- pluma_debug (DEBUG_LOADER);
+ AsyncData *async;
+
+ pluma_debug (DEBUG_LOADER);
- g_return_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader));
+ g_return_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader));
- /* the loader can be used just once, then it must be thrown away */
- g_return_if_fail (loader->used == FALSE);
- loader->used = TRUE;
+ /* the loader can be used just once, then it must be thrown away */
+ g_return_if_fail (loader->priv->used == FALSE);
+ loader->priv->used = TRUE;
- PLUMA_DOCUMENT_LOADER_GET_CLASS (loader)->load (loader);
+ /* make sure no load operation is currently running */
+ g_return_if_fail (loader->priv->cancellable == NULL);
+
+ loader->priv->gfile = g_file_new_for_uri (loader->priv->uri);
+
+ /* loading start */
+ pluma_document_loader_loading (PLUMA_DOCUMENT_LOADER (loader),
+ FALSE,
+ NULL);
+
+ loader->priv->cancellable = g_cancellable_new ();
+ async = async_data_new (loader);
+
+ open_async_read (async);
}
gboolean
pluma_document_loader_cancel (PlumaDocumentLoader *loader)
{
- pluma_debug (DEBUG_LOADER);
+ pluma_debug (DEBUG_LOADER);
+
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), FALSE);
+
+ if (loader->priv->cancellable == NULL)
+ return FALSE;
+
+ g_cancellable_cancel (loader->priv->cancellable);
+
+ g_set_error (&loader->priv->error,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "Operation cancelled");
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), FALSE);
+ remote_load_completed_or_failed (loader, NULL);
- return PLUMA_DOCUMENT_LOADER_GET_CLASS (loader)->cancel (loader);
+ return TRUE;
}
PlumaDocument *
pluma_document_loader_get_document (PlumaDocumentLoader *loader)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
- return loader->document;
+ return loader->priv->document;
}
/* Returns STDIN_URI if loading from stdin */
const gchar *
pluma_document_loader_get_uri (PlumaDocumentLoader *loader)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
- return loader->uri;
+ return loader->priv->uri;
}
goffset
pluma_document_loader_get_bytes_read (PlumaDocumentLoader *loader)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), 0);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), 0);
- return PLUMA_DOCUMENT_LOADER_GET_CLASS (loader)->get_bytes_read (loader);
+ return loader->priv->bytes_read;
}
const PlumaEncoding *
pluma_document_loader_get_encoding (PlumaDocumentLoader *loader)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
- if (loader->encoding != NULL)
- return loader->encoding;
+ if (loader->priv->encoding != NULL)
+ return loader->priv->encoding;
- g_return_val_if_fail (loader->auto_detected_encoding != NULL,
- pluma_encoding_get_current ());
+ g_return_val_if_fail (loader->priv->auto_detected_encoding != NULL,
+ pluma_encoding_get_current ());
- return loader->auto_detected_encoding;
+ return loader->priv->auto_detected_encoding;
}
PlumaDocumentNewlineType
pluma_document_loader_get_newline_type (PlumaDocumentLoader *loader)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader),
- PLUMA_DOCUMENT_NEWLINE_TYPE_LF);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader),
+ PLUMA_DOCUMENT_NEWLINE_TYPE_LF);
- return loader->auto_detected_newline_type;
+ return loader->priv->auto_detected_newline_type;
}
GFileInfo *
pluma_document_loader_get_info (PlumaDocumentLoader *loader)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
- return loader->info;
+ return loader->priv->info;
}
diff --git a/pluma/pluma-document-loader.h b/pluma/pluma-document-loader.h
index 6dd2249b..d9592ebb 100644
--- a/pluma/pluma-document-loader.h
+++ b/pluma/pluma-document-loader.h
@@ -56,17 +56,8 @@ typedef struct _PlumaDocumentLoader PlumaDocumentLoader;
struct _PlumaDocumentLoader
{
- GObject object;
-
- PlumaDocument *document;
- gboolean used;
-
- /* Info on the current file */
- GFileInfo *info;
- gchar *uri;
- const PlumaEncoding *encoding;
- const PlumaEncoding *auto_detected_encoding;
- PlumaDocumentNewlineType auto_detected_newline_type;
+ GObject object;
+ PlumaDocumentLoaderPrivate *priv;
};
/*
@@ -76,54 +67,49 @@ typedef struct _PlumaDocumentLoaderClass PlumaDocumentLoaderClass;
struct _PlumaDocumentLoaderClass
{
- GObjectClass parent_class;
-
- /* Signals */
- void (* loading) (PlumaDocumentLoader *loader,
- gboolean completed,
- const GError *error);
+ GObjectClass parent_class;
- /* VTable */
- void (* load) (PlumaDocumentLoader *loader);
- gboolean (* cancel) (PlumaDocumentLoader *loader);
- goffset (* get_bytes_read) (PlumaDocumentLoader *loader);
+ /* Signals */
+ void (* loading) (PlumaDocumentLoader *loader,
+ gboolean completed,
+ const GError *error);
};
/*
* Public methods
*/
-GType pluma_document_loader_get_type (void) G_GNUC_CONST;
+GType pluma_document_loader_get_type (void) G_GNUC_CONST;
/* If enconding == NULL, the encoding will be autodetected */
-PlumaDocumentLoader *pluma_document_loader_new (PlumaDocument *doc,
- const gchar *uri,
- const PlumaEncoding *encoding);
+PlumaDocumentLoader *pluma_document_loader_new (PlumaDocument *doc,
+ const gchar *uri,
+ const PlumaEncoding *encoding);
-void pluma_document_loader_loading (PlumaDocumentLoader *loader,
- gboolean completed,
- GError *error);
+void pluma_document_loader_loading (PlumaDocumentLoader *loader,
+ gboolean completed,
+ GError *error);
-void pluma_document_loader_load (PlumaDocumentLoader *loader);
+void pluma_document_loader_load (PlumaDocumentLoader *loader);
#if 0
-gboolean pluma_document_loader_load_from_stdin (PlumaDocumentLoader *loader);
+gboolean pluma_document_loader_load_from_stdin (PlumaDocumentLoader *loader);
#endif
-gboolean pluma_document_loader_cancel (PlumaDocumentLoader *loader);
+gboolean pluma_document_loader_cancel (PlumaDocumentLoader *loader);
-PlumaDocument *pluma_document_loader_get_document (PlumaDocumentLoader *loader);
+PlumaDocument *pluma_document_loader_get_document (PlumaDocumentLoader *loader);
/* Returns STDIN_URI if loading from stdin */
#define STDIN_URI "stdin:"
-const gchar *pluma_document_loader_get_uri (PlumaDocumentLoader *loader);
+const gchar *pluma_document_loader_get_uri (PlumaDocumentLoader *loader);
-const PlumaEncoding *pluma_document_loader_get_encoding (PlumaDocumentLoader *loader);
+const PlumaEncoding *pluma_document_loader_get_encoding (PlumaDocumentLoader *loader);
-PlumaDocumentNewlineType pluma_document_loader_get_newline_type (PlumaDocumentLoader *loader);
+PlumaDocumentNewlineType pluma_document_loader_get_newline_type (PlumaDocumentLoader *loader);
-goffset pluma_document_loader_get_bytes_read (PlumaDocumentLoader *loader);
+goffset pluma_document_loader_get_bytes_read (PlumaDocumentLoader *loader);
/* You can get from the info: content_type, time_modified, standard_size, access_can_write
and also the metadata*/
-GFileInfo *pluma_document_loader_get_info (PlumaDocumentLoader *loader);
+GFileInfo *pluma_document_loader_get_info (PlumaDocumentLoader *loader);
G_END_DECLS
diff --git a/pluma/pluma-document-saver.c b/pluma/pluma-document-saver.c
index 645daf41..913444d7 100644
--- a/pluma/pluma-document-saver.c
+++ b/pluma/pluma-document-saver.c
@@ -31,27 +31,26 @@
#include <config.h>
#endif
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-
#include <glib/gi18n.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <string.h>
#include "pluma-document-saver.h"
-#include "pluma-debug.h"
+#include "pluma-document-input-stream.h"
#include "pluma-prefs-manager.h"
+#include "pluma-debug.h"
#include "pluma-marshal.h"
#include "pluma-utils.h"
#include "pluma-enum-types.h"
-#include "pluma-gio-document-saver.h"
-G_DEFINE_ABSTRACT_TYPE(PlumaDocumentSaver, pluma_document_saver, G_TYPE_OBJECT)
+#define WRITE_CHUNK_SIZE 8192
/* Signals */
enum {
- SAVING,
- LAST_SIGNAL
+ SAVING,
+ LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
@@ -59,301 +58,971 @@ static guint signals[LAST_SIGNAL] = { 0 };
/* Properties */
enum {
- PROP_0,
- PROP_DOCUMENT,
- PROP_URI,
- PROP_ENCODING,
- PROP_NEWLINE_TYPE,
- PROP_FLAGS
+ PROP_0,
+ PROP_DOCUMENT,
+ PROP_URI,
+ PROP_ENCODING,
+ PROP_NEWLINE_TYPE,
+ PROP_FLAGS
+};
+
+typedef struct
+{
+ PlumaDocumentSaver *saver;
+ gchar buffer[WRITE_CHUNK_SIZE];
+ GCancellable *cancellable;
+ gboolean tried_mount;
+ gssize written;
+ gssize read;
+ GError *error;
+} AsyncData;
+
+#define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
+ G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
+ G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC
+
+static void check_modified_async (AsyncData *async);
+
+struct _PlumaDocumentSaverPrivate
+{
+ GFileInfo *info;
+ PlumaDocument *document;
+ gboolean used;
+
+ gchar *uri;
+ const PlumaEncoding *encoding;
+ PlumaDocumentNewlineType newline_type;
+
+ PlumaDocumentSaveFlags flags;
+
+ gboolean keep_backup;
+
+ gint64 old_mtime;
+
+ goffset size;
+ goffset bytes_written;
+
+ GFile *gfile;
+ GCancellable *cancellable;
+ GOutputStream *stream;
+ GInputStream *input;
+
+ GError *error;
};
+G_DEFINE_TYPE_WITH_PRIVATE (PlumaDocumentSaver, pluma_document_saver, G_TYPE_OBJECT)
+
static void
pluma_document_saver_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- PlumaDocumentSaver *saver = PLUMA_DOCUMENT_SAVER (object);
-
- switch (prop_id)
- {
- case PROP_DOCUMENT:
- g_return_if_fail (saver->document == NULL);
- saver->document = g_value_get_object (value);
- break;
- case PROP_URI:
- g_return_if_fail (saver->uri == NULL);
- saver->uri = g_value_dup_string (value);
- break;
- case PROP_ENCODING:
- g_return_if_fail (saver->encoding == NULL);
- saver->encoding = g_value_get_boxed (value);
- break;
- case PROP_NEWLINE_TYPE:
- saver->newline_type = g_value_get_enum (value);
- break;
- case PROP_FLAGS:
- saver->flags = g_value_get_flags (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PlumaDocumentSaver *saver = PLUMA_DOCUMENT_SAVER (object);
+
+ switch (prop_id)
+ {
+ case PROP_DOCUMENT:
+ g_return_if_fail (saver->priv->document == NULL);
+ saver->priv->document = g_value_get_object (value);
+ break;
+ case PROP_URI:
+ g_return_if_fail (saver->priv->uri == NULL);
+ saver->priv->uri = g_value_dup_string (value);
+ break;
+ case PROP_ENCODING:
+ g_return_if_fail (saver->priv->encoding == NULL);
+ saver->priv->encoding = g_value_get_boxed (value);
+ break;
+ case PROP_NEWLINE_TYPE:
+ saver->priv->newline_type = g_value_get_enum (value);
+ break;
+ case PROP_FLAGS:
+ saver->priv->flags = g_value_get_flags (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
pluma_document_saver_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- PlumaDocumentSaver *saver = PLUMA_DOCUMENT_SAVER (object);
-
- switch (prop_id)
- {
- case PROP_DOCUMENT:
- g_value_set_object (value, saver->document);
- break;
- case PROP_URI:
- g_value_set_string (value, saver->uri);
- break;
- case PROP_ENCODING:
- g_value_set_boxed (value, saver->encoding);
- break;
- case PROP_NEWLINE_TYPE:
- g_value_set_enum (value, saver->newline_type);
- break;
- case PROP_FLAGS:
- g_value_set_flags (value, saver->flags);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PlumaDocumentSaver *saver = PLUMA_DOCUMENT_SAVER (object);
+
+ switch (prop_id)
+ {
+ case PROP_DOCUMENT:
+ g_value_set_object (value, saver->priv->document);
+ break;
+ case PROP_URI:
+ g_value_set_string (value, saver->priv->uri);
+ break;
+ case PROP_ENCODING:
+ g_value_set_boxed (value, saver->priv->encoding);
+ break;
+ case PROP_NEWLINE_TYPE:
+ g_value_set_enum (value, saver->priv->newline_type);
+ break;
+ case PROP_FLAGS:
+ g_value_set_flags (value, saver->priv->flags);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
pluma_document_saver_finalize (GObject *object)
{
- PlumaDocumentSaver *saver = PLUMA_DOCUMENT_SAVER (object);
+ PlumaDocumentSaverPrivate *priv = pluma_document_saver_get_instance_private (PLUMA_DOCUMENT_SAVER(object));
- g_free (saver->uri);
+ g_free (priv->uri);
- G_OBJECT_CLASS (pluma_document_saver_parent_class)->finalize (object);
+ G_OBJECT_CLASS (pluma_document_saver_parent_class)->finalize (object);
}
static void
pluma_document_saver_dispose (GObject *object)
{
- PlumaDocumentSaver *saver = PLUMA_DOCUMENT_SAVER (object);
+ PlumaDocumentSaverPrivate *priv = pluma_document_saver_get_instance_private (PLUMA_DOCUMENT_SAVER(object));
+
+ if (priv->cancellable != NULL)
+ {
+ g_cancellable_cancel (priv->cancellable);
+ g_object_unref (priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
+ if (priv->gfile != NULL)
+ {
+ g_object_unref (priv->gfile);
+ priv->gfile = NULL;
+ }
+
+ if (priv->error != NULL)
+ {
+ g_error_free (priv->error);
+ priv->error = NULL;
+ }
+
+ if (priv->stream != NULL)
+ {
+ g_object_unref (priv->stream);
+ priv->stream = NULL;
+ }
- if (saver->info != NULL)
- {
- g_object_unref (saver->info);
- saver->info = NULL;
- }
+ if (priv->info != NULL)
+ {
+ g_object_unref (priv->info);
+ priv->info = NULL;
+ }
- G_OBJECT_CLASS (pluma_document_saver_parent_class)->dispose (object);
+ G_OBJECT_CLASS (pluma_document_saver_parent_class)->dispose (object);
+}
+
+static AsyncData *
+async_data_new (PlumaDocumentSaver *gvsaver)
+{
+ AsyncData *async;
+
+ async = g_slice_new (AsyncData);
+ async->saver = gvsaver;
+ async->cancellable = g_object_ref (gvsaver->priv->cancellable);
+
+ async->tried_mount = FALSE;
+ async->written = 0;
+ async->read = 0;
+
+ async->error = NULL;
+
+ return async;
+}
+
+static void
+async_data_free (AsyncData *async)
+{
+ g_object_unref (async->cancellable);
+
+ if (async->error)
+ {
+ g_error_free (async->error);
+ }
+
+ g_slice_free (AsyncData, async);
}
static void
pluma_document_saver_class_init (PlumaDocumentSaverClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->finalize = pluma_document_saver_finalize;
- object_class->dispose = pluma_document_saver_dispose;
- object_class->set_property = pluma_document_saver_set_property;
- object_class->get_property = pluma_document_saver_get_property;
-
- g_object_class_install_property (object_class,
- PROP_DOCUMENT,
- g_param_spec_object ("document",
- "Document",
- "The PlumaDocument this PlumaDocumentSaver is associated with",
- PLUMA_TYPE_DOCUMENT,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (object_class,
- PROP_URI,
- g_param_spec_string ("uri",
- "URI",
- "The URI this PlumaDocumentSaver saves the document to",
- "",
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (object_class,
- PROP_ENCODING,
- g_param_spec_boxed ("encoding",
- "URI",
- "The encoding of the saved file",
- PLUMA_TYPE_ENCODING,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (object_class,
- PROP_NEWLINE_TYPE,
- g_param_spec_enum ("newline-type",
- "Newline type",
- "The accepted types of line ending",
- PLUMA_TYPE_DOCUMENT_NEWLINE_TYPE,
- PLUMA_DOCUMENT_NEWLINE_TYPE_LF,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB |
- G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property (object_class,
- PROP_FLAGS,
- g_param_spec_flags ("flags",
- "Flags",
- "The flags for the saving operation",
- PLUMA_TYPE_DOCUMENT_SAVE_FLAGS,
- 0,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
-
- signals[SAVING] =
- g_signal_new ("saving",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PlumaDocumentSaverClass, saving),
- NULL, NULL,
- pluma_marshal_VOID__BOOLEAN_POINTER,
- G_TYPE_NONE,
- 2,
- G_TYPE_BOOLEAN,
- G_TYPE_POINTER);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = pluma_document_saver_dispose;
+ object_class->finalize = pluma_document_saver_finalize;
+ object_class->set_property = pluma_document_saver_set_property;
+ object_class->get_property = pluma_document_saver_get_property;
+
+ g_object_class_install_property (object_class,
+ PROP_DOCUMENT,
+ g_param_spec_object ("document",
+ "Document",
+ "The PlumaDocument this PlumaDocumentSaver is associated with",
+ PLUMA_TYPE_DOCUMENT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_URI,
+ g_param_spec_string ("uri",
+ "URI",
+ "The URI this PlumaDocumentSaver saves the document to",
+ "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_ENCODING,
+ g_param_spec_boxed ("encoding",
+ "URI",
+ "The encoding of the saved file",
+ PLUMA_TYPE_ENCODING,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_NEWLINE_TYPE,
+ g_param_spec_enum ("newline-type",
+ "Newline type",
+ "The accepted types of line ending",
+ PLUMA_TYPE_DOCUMENT_NEWLINE_TYPE,
+ PLUMA_DOCUMENT_NEWLINE_TYPE_LF,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_FLAGS,
+ g_param_spec_flags ("flags",
+ "Flags",
+ "The flags for the saving operation",
+ PLUMA_TYPE_DOCUMENT_SAVE_FLAGS,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ signals[SAVING] =
+ g_signal_new ("saving",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PlumaDocumentSaverClass, saving),
+ NULL, NULL,
+ pluma_marshal_VOID__BOOLEAN_POINTER,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_BOOLEAN,
+ G_TYPE_POINTER);
}
static void
pluma_document_saver_init (PlumaDocumentSaver *saver)
{
- saver->used = FALSE;
+ saver->priv = pluma_document_saver_get_instance_private (saver);
+
+ saver->priv->cancellable = g_cancellable_new ();
+ saver->priv->error = NULL;
+ saver->priv->used = FALSE;
}
PlumaDocumentSaver *
pluma_document_saver_new (PlumaDocument *doc,
- const gchar *uri,
- const PlumaEncoding *encoding,
- PlumaDocumentNewlineType newline_type,
- PlumaDocumentSaveFlags flags)
+ const gchar *uri,
+ const PlumaEncoding *encoding,
+ PlumaDocumentNewlineType newline_type,
+ PlumaDocumentSaveFlags flags)
{
- PlumaDocumentSaver *saver;
- GType saver_type;
+ return PLUMA_DOCUMENT_SAVER (g_object_new (PLUMA_TYPE_DOCUMENT_SAVER,
+ "document", doc,
+ "uri", uri,
+ "encoding", encoding,
+ "newline_type", newline_type,
+ "flags", flags,
+ NULL));
- g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
+}
- saver_type = PLUMA_TYPE_GIO_DOCUMENT_SAVER;
+static void
+remote_save_completed_or_failed (PlumaDocumentSaver *gvsaver,
+ AsyncData *async)
+{
+ pluma_document_saver_saving (PLUMA_DOCUMENT_SAVER (gvsaver),
+ TRUE,
+ gvsaver->priv->error);
- if (encoding == NULL)
- encoding = pluma_encoding_get_utf8 ();
+ if (async)
+ async_data_free (async);
+}
- saver = PLUMA_DOCUMENT_SAVER (g_object_new (saver_type,
- "document", doc,
- "uri", uri,
- "encoding", encoding,
- "newline_type", newline_type,
- "flags", flags,
- NULL));
+static void
+async_failed (AsyncData *async,
+ GError *error)
+{
+ g_propagate_error (&async->saver->priv->error, error);
+ remote_save_completed_or_failed (async->saver, async);
+}
+
+/* BEGIN NOTE:
+ *
+ * This fixes an issue in GOutputStream that applies the atomic replace
+ * save strategy. The stream moves the written file to the original file
+ * when the stream is closed. However, there is no way currently to tell
+ * the stream that the save should be aborted (there could be a
+ * conversion error). The patch explicitly closes the output stream
+ * in all these cases with a GCancellable in the cancelled state, causing
+ * the output stream to close, but not move the file. This makes use
+ * of an implementation detail in the local file stream and should be
+ * properly fixed by adding the appropriate API in . Until then, at least
+ * we prevent data corruption for now.
+ *
+ * Relevant bug reports:
+ *
+ * Bug 615110 - write file ignore encoding errors (pluma)
+ * https://bugzilla.gnome.org/show_bug.cgi?id=615110
+ *
+ * Bug 602412 - g_file_replace does not restore original file when there is
+ * errors while writing (glib/)
+ * https://bugzilla.gnome.org/show_bug.cgi?id=602412
+ */
+static void
+cancel_output_stream_ready_cb (GOutputStream *stream,
+ GAsyncResult *result,
+ AsyncData *async)
+{
+ GError *error;
+
+ g_output_stream_close_finish (stream, result, NULL);
+
+ /* check cancelled state manually */
+ if (g_cancellable_is_cancelled (async->cancellable) || async->error == NULL)
+ {
+ async_data_free (async);
+ return;
+ }
- return saver;
+ error = async->error;
+ async->error = NULL;
+
+ async_failed (async, error);
}
-void
-pluma_document_saver_saving (PlumaDocumentSaver *saver,
- gboolean completed,
- GError *error)
+static void
+cancel_output_stream (AsyncData *async)
+{
+ GCancellable *cancellable;
+
+ pluma_debug_message (DEBUG_SAVER, "Cancel output stream");
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+
+ g_output_stream_close_async (async->saver->priv->stream,
+ G_PRIORITY_HIGH,
+ cancellable,
+ (GAsyncReadyCallback)cancel_output_stream_ready_cb,
+ async);
+
+ g_object_unref (cancellable);
+}
+
+static void
+cancel_output_stream_and_fail (AsyncData *async,
+ GError *error)
+{
+
+ pluma_debug_message (DEBUG_SAVER, "Cancel output stream and fail");
+
+ g_propagate_error (&async->error, error);
+ cancel_output_stream (async);
+}
+
+/*
+ * END NOTE
+ */
+
+static void
+remote_get_info_cb (GFile *source,
+ GAsyncResult *res,
+ AsyncData *async)
{
- /* the object will be unrefed in the callback of the saving
- * signal, so we need to prevent finalization.
- */
- if (completed)
- {
- g_object_ref (saver);
- }
+ PlumaDocumentSaver *saver;
+ GFileInfo *info;
+ GError *error = NULL;
+
+ pluma_debug (DEBUG_SAVER);
+
+ /* check cancelled state manually */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
- g_signal_emit (saver, signals[SAVING], 0, completed, error);
+ saver = async->saver;
- if (completed)
- {
- if (error == NULL)
- pluma_debug_message (DEBUG_SAVER, "save completed");
- else
- pluma_debug_message (DEBUG_SAVER, "save failed");
+ pluma_debug_message (DEBUG_SAVER, "Finished query info on file");
+ info = g_file_query_info_finish (source, res, &error);
- g_object_unref (saver);
- }
+ if (info != NULL)
+ {
+ if (saver->priv->info != NULL)
+ g_object_unref (saver->priv->info);
+
+ saver->priv->info = info;
+ }
+ else
+ {
+ pluma_debug_message (DEBUG_SAVER, "Query info failed: %s", error->message);
+ g_propagate_error (&saver->priv->error, error);
+ }
+
+ remote_save_completed_or_failed (saver, async);
+}
+
+static void
+close_async_ready_get_info_cb (GOutputStream *stream,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ GError *error = NULL;
+
+ pluma_debug (DEBUG_SAVER);
+
+ /* check cancelled state manually */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ pluma_debug_message (DEBUG_SAVER, "Finished closing stream");
+
+ if (!g_output_stream_close_finish (stream, res, &error))
+ {
+ pluma_debug_message (DEBUG_SAVER, "Closing stream error: %s", error->message);
+
+ async_failed (async, error);
+ return;
+ }
+
+ /* get the file info: note we cannot use
+ * g_file_output_stream_query_info_async since it is not able to get the
+ * content type etc, beside it is not supported by gvfs.
+ * I'm not sure this is actually necessary, can't we just use
+ * g_content_type_guess (since we have the file name and the data)
+ */
+ pluma_debug_message (DEBUG_SAVER, "Query info on file");
+ g_file_query_info_async (async->saver->priv->gfile,
+ REMOTE_QUERY_ATTRIBUTES,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback) remote_get_info_cb,
+ async);
+}
+
+static void
+write_complete (AsyncData *async)
+{
+ GError *error = NULL;
+
+ /* first we close the input stream */
+ pluma_debug_message (DEBUG_SAVER, "Close input stream");
+ if (!g_input_stream_close (async->saver->priv->input,
+ async->cancellable, &error))
+ {
+ pluma_debug_message (DEBUG_SAVER, "Closing input stream error: %s", error->message);
+ cancel_output_stream_and_fail (async, error);
+ return;
+ }
+
+ /* now we close the output stream */
+ pluma_debug_message (DEBUG_SAVER, "Close output stream");
+ g_output_stream_close_async (async->saver->priv->stream,
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback)close_async_ready_get_info_cb,
+ async);
+}
+
+/* prototype, because they call each other... isn't C lovely */
+static void read_file_chunk (AsyncData *async);
+static void write_file_chunk (AsyncData *async);
+
+static void
+async_write_cb (GOutputStream *stream,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ PlumaDocumentSaver *saver;
+ gssize bytes_written;
+ GError *error = NULL;
+
+ pluma_debug (DEBUG_SAVER);
+
+ /* Check cancelled state manually */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ cancel_output_stream (async);
+ return;
+ }
+
+ bytes_written = g_output_stream_write_finish (stream, res, &error);
+
+ pluma_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written);
+
+ if (bytes_written == -1)
+ {
+ pluma_debug_message (DEBUG_SAVER, "Write error: %s", error->message);
+ cancel_output_stream_and_fail (async, error);
+ return;
+ }
+
+ saver = async->saver;
+ async->written += bytes_written;
+
+ /* write again */
+ if (async->written != async->read)
+ {
+ write_file_chunk (async);
+ return;
+ }
+
+ /* note that this signal blocks the write... check if it isn't
+ * a performance problem
+ */
+ pluma_document_saver_saving (saver, FALSE, NULL);
+
+ read_file_chunk (async);
+}
+
+static void
+write_file_chunk (AsyncData *async)
+{
+ PlumaDocumentSaver *saver;
+
+ pluma_debug (DEBUG_SAVER);
+
+ saver = async->saver;
+
+ g_output_stream_write_async (G_OUTPUT_STREAM (saver->priv->stream),
+ async->buffer + async->written,
+ async->read - async->written,
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback) async_write_cb,
+ async);
+}
+
+static void
+read_file_chunk (AsyncData *async)
+{
+ PlumaDocumentSaver *saver;
+ PlumaDocumentInputStream *dstream;
+ GError *error = NULL;
+
+ pluma_debug (DEBUG_SAVER);
+
+ saver = async->saver;
+ async->written = 0;
+
+ /* we use sync methods on doc stream since it is in memory. Using async
+ would be racy and we can endup with invalidated iters */
+ async->read = g_input_stream_read (saver->priv->input,
+ async->buffer,
+ WRITE_CHUNK_SIZE,
+ async->cancellable,
+ &error);
+
+ if (error != NULL)
+ {
+ cancel_output_stream_and_fail (async, error);
+ return;
+ }
+
+ /* Check if we finished reading and writing */
+ if (async->read == 0)
+ {
+ write_complete (async);
+ return;
+ }
+
+ /* Get how many chars have been read */
+ dstream = PLUMA_DOCUMENT_INPUT_STREAM (saver->priv->input);
+ saver->priv->bytes_written = pluma_document_input_stream_tell (dstream);
+
+ write_file_chunk (async);
+}
+
+static void
+async_replace_ready_callback (GFile *source,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ PlumaDocumentSaver *saver;
+ GCharsetConverter *converter;
+ GFileOutputStream *file_stream;
+ GError *error = NULL;
+
+ pluma_debug (DEBUG_SAVER);
+
+ /* Check cancelled state manually */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ saver = async->saver;
+ file_stream = g_file_replace_finish (source, res, &error);
+
+ /* handle any error that might occur */
+ if (!file_stream)
+ {
+ pluma_debug_message (DEBUG_SAVER, "Opening file failed: %s", error->message);
+ async_failed (async, error);
+ return;
+ }
+
+ /* FIXME: manage converter error? */
+ pluma_debug_message (DEBUG_SAVER, "Encoding charset: %s",
+ pluma_encoding_get_charset (saver->priv->encoding));
+
+ if (saver->priv->encoding != pluma_encoding_get_utf8 ())
+ {
+ converter = g_charset_converter_new (pluma_encoding_get_charset (saver->priv->encoding),
+ "UTF-8",
+ NULL);
+ saver->priv->stream = g_converter_output_stream_new (G_OUTPUT_STREAM (file_stream),
+ G_CONVERTER (converter));
+
+ g_object_unref (file_stream);
+ g_object_unref (converter);
+ }
+ else
+ {
+ saver->priv->stream = G_OUTPUT_STREAM (file_stream);
+ }
+
+ saver->priv->input = pluma_document_input_stream_new (GTK_TEXT_BUFFER (saver->priv->document),
+ saver->priv->newline_type);
+
+ saver->priv->size = pluma_document_input_stream_get_total_size (PLUMA_DOCUMENT_INPUT_STREAM (saver->priv->input));
+
+ read_file_chunk (async);
+}
+
+static void
+begin_write (AsyncData *async)
+{
+ PlumaDocumentSaver *saver;
+ gboolean backup;
+
+ pluma_debug_message (DEBUG_SAVER, "Start replacing file contents");
+
+ /* For remote files we simply use g_file_replace_async. There is no
+ * backup as of yet
+ */
+ saver = async->saver;
+
+ /* Do not make backups for remote files so they do not clutter remote systems */
+ backup = (saver->priv->keep_backup && pluma_document_is_local (saver->priv->document));
+
+ pluma_debug_message (DEBUG_SAVER, "File contents size: %" G_GINT64_FORMAT, saver->priv->size);
+ pluma_debug_message (DEBUG_SAVER, "Calling replace_async");
+ pluma_debug_message (DEBUG_SAVER, backup ? "Keep backup" : "Discard backup");
+
+ g_file_replace_async (saver->priv->gfile,
+ NULL,
+ backup,
+ G_FILE_CREATE_NONE,
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback) async_replace_ready_callback,
+ async);
+}
+
+static void
+mount_ready_callback (GFile *file,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ GError *error = NULL;
+ gboolean mounted;
+
+ pluma_debug (DEBUG_SAVER);
+
+ /* manual check for cancelled state */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
+
+ if (!mounted)
+ {
+ async_failed (async, error);
+ }
+ else
+ {
+ /* try again to get the modified state */
+ check_modified_async (async);
+ }
+}
+
+static void
+recover_not_mounted (AsyncData *async)
+{
+ PlumaDocument *doc;
+ GMountOperation *mount_operation;
+
+ pluma_debug (DEBUG_LOADER);
+
+ doc = pluma_document_saver_get_document (async->saver);
+ mount_operation = _pluma_document_create_mount_operation (doc);
+
+ async->tried_mount = TRUE;
+ g_file_mount_enclosing_volume (async->saver->priv->gfile,
+ G_MOUNT_MOUNT_NONE,
+ mount_operation,
+ async->cancellable,
+ (GAsyncReadyCallback) mount_ready_callback,
+ async);
+
+ g_object_unref (mount_operation);
+}
+
+static void
+check_modification_callback (GFile *source,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ PlumaDocumentSaver *saver;
+ GError *error = NULL;
+ GFileInfo *info;
+
+ pluma_debug (DEBUG_SAVER);
+
+ /* manually check cancelled state */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_data_free (async);
+ return;
+ }
+
+ saver = async->saver;
+ info = g_file_query_info_finish (source, res, &error);
+ if (info == NULL)
+ {
+ if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount)
+ {
+ recover_not_mounted (async);
+ g_error_free (error);
+ return;
+ }
+
+ /* it's perfectly fine if the file doesn't exist yet */
+ if (error->code != G_IO_ERROR_NOT_FOUND)
+ {
+ pluma_debug_message (DEBUG_SAVER, "Error getting modification: %s", error->message);
+
+ async_failed (async, error);
+ return;
+ }
+ }
+
+ /* check if the mtime is > what we know about it (if we have it) */
+ if (info != NULL && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
+ {
+ guint64 mtime;
+ gint64 old_mtime;
+
+ mtime = g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED) * G_USEC_PER_SEC;
+ if (g_file_info_has_attribute (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC))
+ {
+ guint32 usec = g_file_info_get_attribute_uint32 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
+ mtime += (guint64) usec;
+ }
+
+ old_mtime = saver->priv->old_mtime;
+
+ if ((old_mtime > 0 || ((gint64) mtime) > 0) && (((gint64) mtime) != old_mtime) &&
+ (saver->priv->flags & PLUMA_DOCUMENT_SAVE_IGNORE_MTIME) == 0)
+ {
+ pluma_debug_message (DEBUG_SAVER, "File is externally modified");
+ g_set_error (&saver->priv->error,
+ PLUMA_DOCUMENT_ERROR,
+ PLUMA_DOCUMENT_ERROR_EXTERNALLY_MODIFIED,
+ "Externally modified");
+
+ remote_save_completed_or_failed (saver, async);
+ g_object_unref (info);
+
+ return;
+ }
+ }
+
+ if (info != NULL)
+ g_object_unref (info);
+
+ /* modification check passed, start write */
+ begin_write (async);
+}
+
+static void
+check_modified_async (AsyncData *async)
+{
+ pluma_debug_message (DEBUG_SAVER, "Check externally modified");
+
+ g_file_query_info_async (async->saver->priv->gfile,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_HIGH,
+ async->cancellable,
+ (GAsyncReadyCallback) check_modification_callback,
+ async);
+}
+
+static gboolean
+save_remote_file_real (PlumaDocumentSaver *saver)
+{
+ AsyncData *async;
+
+ pluma_debug_message (DEBUG_SAVER, "Starting save");
+
+ /* First find out if the file is modified externally. This requires
+ * a stat, but I don't think we can do this any other way
+ */
+ async = async_data_new (saver);
+
+ check_modified_async (async);
+
+ /* return false to stop timeout */
+ return FALSE;
}
void
pluma_document_saver_save (PlumaDocumentSaver *saver,
gint64 *old_mtime)
{
- pluma_debug (DEBUG_SAVER);
+ pluma_debug (DEBUG_SAVER);
+
+ g_return_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver));
+ g_return_if_fail (saver->priv->uri != NULL && strlen (saver->priv->uri) > 0);
+
+ g_return_if_fail (saver->priv->used == FALSE);
+ saver->priv->used = TRUE;
+
+ /* CHECK:
+ * - sanity check a max len for the uri?
+ * report async (in an idle handler) or sync (bool ret)
+ * async is extra work here, sync is special casing in the caller
+ */
+
+ /* never keep backup of autosaves */
+ if ((saver->priv->flags & PLUMA_DOCUMENT_SAVE_PRESERVE_BACKUP) != 0)
+ saver->priv->keep_backup = FALSE;
+ else
+ saver->priv->keep_backup = pluma_prefs_manager_get_create_backup_copy ();
- g_return_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver));
- g_return_if_fail (saver->uri != NULL && strlen (saver->uri) > 0);
+ saver->priv->old_mtime = *old_mtime;
+ saver->priv->gfile = g_file_new_for_uri (saver->priv->uri);
- g_return_if_fail (saver->used == FALSE);
- saver->used = TRUE;
+ /* saving start */
+ pluma_document_saver_saving (saver, FALSE, NULL);
+
+ g_timeout_add_full (G_PRIORITY_HIGH,
+ 0,
+ (GSourceFunc) save_remote_file_real,
+ saver,
+ NULL);
+}
+
+void
+pluma_document_saver_saving (PlumaDocumentSaver *saver,
+ gboolean completed,
+ GError *error)
+{
+ /* the object will be unrefed in the callback of the saving
+ * signal, so we need to prevent finalization.
+ */
+ if (completed)
+ {
+ g_object_ref (saver);
+ }
- // CHECK:
- // - sanity check a max len for the uri?
- // report async (in an idle handler) or sync (bool ret)
- // async is extra work here, sync is special casing in the caller
+ g_signal_emit (saver, signals[SAVING], 0, completed, error);
- /* never keep backup of autosaves */
- if ((saver->flags & PLUMA_DOCUMENT_SAVE_PRESERVE_BACKUP) != 0)
- saver->keep_backup = FALSE;
- else
- saver->keep_backup = pluma_prefs_manager_get_create_backup_copy ();
+ if (completed)
+ {
+ if (error == NULL)
+ pluma_debug_message (DEBUG_SAVER, "save completed");
+ else
+ pluma_debug_message (DEBUG_SAVER, "save failed");
- PLUMA_DOCUMENT_SAVER_GET_CLASS (saver)->save (saver, old_mtime);
+ g_object_unref (saver);
+ }
}
PlumaDocument *
pluma_document_saver_get_document (PlumaDocumentSaver *saver)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), NULL);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), NULL);
- return saver->document;
+ return saver->priv->document;
}
const gchar *
pluma_document_saver_get_uri (PlumaDocumentSaver *saver)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), NULL);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), NULL);
- return saver->uri;
+ return saver->priv->uri;
}
/* Returns 0 if file size is unknown */
goffset
pluma_document_saver_get_file_size (PlumaDocumentSaver *saver)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), 0);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), 0);
- return PLUMA_DOCUMENT_SAVER_GET_CLASS (saver)->get_file_size (saver);
+ return saver->priv->size;
}
goffset
pluma_document_saver_get_bytes_written (PlumaDocumentSaver *saver)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), 0);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), 0);
- return PLUMA_DOCUMENT_SAVER_GET_CLASS (saver)->get_bytes_written (saver);
+ return saver->priv->bytes_written;
}
GFileInfo *
pluma_document_saver_get_info (PlumaDocumentSaver *saver)
{
- g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), NULL);
+ g_return_val_if_fail (PLUMA_IS_DOCUMENT_SAVER (saver), NULL);
- return saver->info;
+ return saver->priv->info;
}
diff --git a/pluma/pluma-document-saver.h b/pluma/pluma-document-saver.h
index 27859039..31994faa 100644
--- a/pluma/pluma-document-saver.h
+++ b/pluma/pluma-document-saver.h
@@ -46,6 +46,9 @@ G_BEGIN_DECLS
#define PLUMA_IS_DOCUMENT_SAVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLUMA_TYPE_DOCUMENT_SAVER))
#define PLUMA_DOCUMENT_SAVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PLUMA_TYPE_DOCUMENT_SAVER, PlumaDocumentSaverClass))
+/* Private structure type */
+typedef struct _PlumaDocumentSaverPrivate PlumaDocumentSaverPrivate;
+
/*
* Main object structure
*/
@@ -53,20 +56,8 @@ typedef struct _PlumaDocumentSaver PlumaDocumentSaver;
struct _PlumaDocumentSaver
{
- GObject object;
-
- /*< private >*/
- GFileInfo *info;
- PlumaDocument *document;
- gboolean used;
-
- gchar *uri;
- const PlumaEncoding *encoding;
- PlumaDocumentNewlineType newline_type;
-
- PlumaDocumentSaveFlags flags;
-
- gboolean keep_backup;
+ GObject object;
+ PlumaDocumentSaverPrivate *priv;
};
/*
@@ -76,57 +67,51 @@ typedef struct _PlumaDocumentSaverClass PlumaDocumentSaverClass;
struct _PlumaDocumentSaverClass
{
- GObjectClass parent_class;
-
- /* Signals */
- void (* saving) (PlumaDocumentSaver *saver,
- gboolean completed,
- const GError *error);
-
- /* VTable */
- void (* save) (PlumaDocumentSaver *saver,
- gint64 *old_mtime);
- goffset (* get_file_size) (PlumaDocumentSaver *saver);
- goffset (* get_bytes_written) (PlumaDocumentSaver *saver);
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (* saving) (PlumaDocumentSaver *saver,
+ gboolean completed,
+ const GError *error);
};
/*
* Public methods
*/
-GType pluma_document_saver_get_type (void) G_GNUC_CONST;
+GType pluma_document_saver_get_type (void) G_GNUC_CONST;
/* If enconding == NULL, the encoding will be autodetected */
-PlumaDocumentSaver *pluma_document_saver_new (PlumaDocument *doc,
- const gchar *uri,
- const PlumaEncoding *encoding,
- PlumaDocumentNewlineType newline_type,
- PlumaDocumentSaveFlags flags);
-
-void pluma_document_saver_saving (PlumaDocumentSaver *saver,
- gboolean completed,
- GError *error);
-void pluma_document_saver_save (PlumaDocumentSaver *saver,
- gint64 *old_mtime);
+PlumaDocumentSaver *pluma_document_saver_new (PlumaDocument *doc,
+ const gchar *uri,
+ const PlumaEncoding *encoding,
+ PlumaDocumentNewlineType newline_type,
+ PlumaDocumentSaveFlags flags);
+
+void pluma_document_saver_saving (PlumaDocumentSaver *saver,
+ gboolean completed,
+ GError *error);
+void pluma_document_saver_save (PlumaDocumentSaver *saver,
+ gint64 *old_mtime);
#if 0
-void pluma_document_saver_cancel (PlumaDocumentSaver *saver);
+void pluma_document_saver_cancel (PlumaDocumentSaver *saver);
#endif
-PlumaDocument *pluma_document_saver_get_document (PlumaDocumentSaver *saver);
+PlumaDocument *pluma_document_saver_get_document (PlumaDocumentSaver *saver);
-const gchar *pluma_document_saver_get_uri (PlumaDocumentSaver *saver);
+const gchar *pluma_document_saver_get_uri (PlumaDocumentSaver *saver);
/* If backup_uri is NULL no backup will be made */
-const gchar *pluma_document_saver_get_backup_uri (PlumaDocumentSaver *saver);
-void *pluma_document_saver_set_backup_uri (PlumaDocumentSaver *saver,
- const gchar *backup_uri);
+const gchar *pluma_document_saver_get_backup_uri (PlumaDocumentSaver *saver);
+void *pluma_document_saver_set_backup_uri (PlumaDocumentSaver *saver,
+ const gchar *backup_uri);
/* Returns 0 if file size is unknown */
-goffset pluma_document_saver_get_file_size (PlumaDocumentSaver *saver);
+goffset pluma_document_saver_get_file_size (PlumaDocumentSaver *saver);
-goffset pluma_document_saver_get_bytes_written (PlumaDocumentSaver *saver);
+goffset pluma_document_saver_get_bytes_written (PlumaDocumentSaver *saver);
-GFileInfo *pluma_document_saver_get_info (PlumaDocumentSaver *saver);
+GFileInfo *pluma_document_saver_get_info (PlumaDocumentSaver *saver);
G_END_DECLS
diff --git a/pluma/pluma-gio-document-loader.c b/pluma/pluma-gio-document-loader.c
deleted file mode 100644
index 0891af7e..00000000
--- a/pluma/pluma-gio-document-loader.c
+++ /dev/null
@@ -1,702 +0,0 @@
-/*
- * pluma-gio-document-loader.c
- * This file is part of pluma
- *
- * Copyright (C) 2005 - Paolo Maggi
- * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux
- * Copyright (C) 2008 - Jesse van den Kieboom
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-/*
- * Modified by the pluma Team, 2005-2008. See the AUTHORS file for a
- * list of people on the pluma Team.
- * See the ChangeLog files for a list of changes.
- *
- * $Id$
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-#include <gio/gio.h>
-
-#include "pluma-gio-document-loader.h"
-#include "pluma-document-output-stream.h"
-#include "pluma-smart-charset-converter.h"
-#include "pluma-prefs-manager.h"
-#include "pluma-debug.h"
-#include "pluma-utils.h"
-
-#ifndef ENABLE_GVFS_METADATA
-#include "pluma-metadata-manager.h"
-#endif
-
-typedef struct
-{
- PlumaGioDocumentLoader *loader;
- GCancellable *cancellable;
-
- gssize read;
- gboolean tried_mount;
-} AsyncData;
-
-#define READ_CHUNK_SIZE 8192
-#define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
- G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
- G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
- G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," \
- G_FILE_ATTRIBUTE_STANDARD_SIZE "," \
- G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," \
- PLUMA_METADATA_ATTRIBUTE_ENCODING
-
-static void pluma_gio_document_loader_load (PlumaDocumentLoader *loader);
-static gboolean pluma_gio_document_loader_cancel (PlumaDocumentLoader *loader);
-static goffset pluma_gio_document_loader_get_bytes_read (PlumaDocumentLoader *loader);
-
-static void open_async_read (AsyncData *async);
-
-struct _PlumaGioDocumentLoaderPrivate
-{
- /* Info on the current file */
- GFile *gfile;
-
- goffset bytes_read;
-
- /* Handle for remote files */
- GCancellable *cancellable;
- GInputStream *stream;
- GOutputStream *output;
- PlumaSmartCharsetConverter *converter;
-
- gchar buffer[READ_CHUNK_SIZE];
-
- GError *error;
-};
-
-G_DEFINE_TYPE_WITH_PRIVATE (PlumaGioDocumentLoader, pluma_gio_document_loader, PLUMA_TYPE_DOCUMENT_LOADER)
-
-static void
-pluma_gio_document_loader_dispose (GObject *object)
-{
- PlumaGioDocumentLoaderPrivate *priv;
-
- priv = PLUMA_GIO_DOCUMENT_LOADER (object)->priv;
-
- if (priv->cancellable != NULL)
- {
- g_cancellable_cancel (priv->cancellable);
- g_object_unref (priv->cancellable);
- priv->cancellable = NULL;
- }
-
- if (priv->stream != NULL)
- {
- g_object_unref (priv->stream);
- priv->stream = NULL;
- }
-
- if (priv->output != NULL)
- {
- g_object_unref (priv->output);
- priv->output = NULL;
- }
-
- if (priv->converter != NULL)
- {
- g_object_unref (priv->converter);
- priv->converter = NULL;
- }
-
- if (priv->gfile != NULL)
- {
- g_object_unref (priv->gfile);
- priv->gfile = NULL;
- }
-
- if (priv->error != NULL)
- {
- g_error_free (priv->error);
- priv->error = NULL;
- }
-
- G_OBJECT_CLASS (pluma_gio_document_loader_parent_class)->dispose (object);
-}
-
-static void
-pluma_gio_document_loader_class_init (PlumaGioDocumentLoaderClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- PlumaDocumentLoaderClass *loader_class = PLUMA_DOCUMENT_LOADER_CLASS (klass);
-
- object_class->dispose = pluma_gio_document_loader_dispose;
-
- loader_class->load = pluma_gio_document_loader_load;
- loader_class->cancel = pluma_gio_document_loader_cancel;
- loader_class->get_bytes_read = pluma_gio_document_loader_get_bytes_read;
-}
-
-static void
-pluma_gio_document_loader_init (PlumaGioDocumentLoader *gvloader)
-{
- gvloader->priv = pluma_gio_document_loader_get_instance_private (gvloader);
-
- gvloader->priv->converter = NULL;
- gvloader->priv->error = NULL;
-}
-
-static AsyncData *
-async_data_new (PlumaGioDocumentLoader *gvloader)
-{
- AsyncData *async;
-
- async = g_slice_new (AsyncData);
- async->loader = gvloader;
- async->cancellable = g_object_ref (gvloader->priv->cancellable);
- async->tried_mount = FALSE;
-
- return async;
-}
-
-static void
-async_data_free (AsyncData *async)
-{
- g_object_unref (async->cancellable);
- g_slice_free (AsyncData, async);
-}
-
-static const PlumaEncoding *
-get_metadata_encoding (PlumaDocumentLoader *loader)
-{
- const PlumaEncoding *enc = NULL;
-
-#ifndef ENABLE_GVFS_METADATA
- gchar *charset;
- const gchar *uri;
-
- uri = pluma_document_loader_get_uri (loader);
-
- charset = pluma_metadata_manager_get (uri, "encoding");
-
- if (charset == NULL)
- return NULL;
-
- enc = pluma_encoding_get_from_charset (charset);
-
- g_free (charset);
-#else
- GFileInfo *info;
-
- info = pluma_document_loader_get_info (loader);
-
- /* check if the encoding was set in the metadata */
- if (g_file_info_has_attribute (info, PLUMA_METADATA_ATTRIBUTE_ENCODING))
- {
- const gchar *charset;
-
- charset = g_file_info_get_attribute_string (info,
- PLUMA_METADATA_ATTRIBUTE_ENCODING);
-
- if (charset == NULL)
- return NULL;
-
- enc = pluma_encoding_get_from_charset (charset);
- }
-#endif
-
- return enc;
-}
-
-static void
-remote_load_completed_or_failed (PlumaGioDocumentLoader *loader, AsyncData *async)
-{
- pluma_document_loader_loading (PLUMA_DOCUMENT_LOADER (loader),
- TRUE,
- loader->priv->error);
-
- if (async)
- async_data_free (async);
-}
-
-static void
-async_failed (AsyncData *async, GError *error)
-{
- g_propagate_error (&async->loader->priv->error, error);
- remote_load_completed_or_failed (async->loader, async);
-}
-
-static void
-close_input_stream_ready_cb (GInputStream *stream,
- GAsyncResult *res,
- AsyncData *async)
-{
- GError *error = NULL;
-
- pluma_debug (DEBUG_LOADER);
-
- /* check cancelled state manually */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- pluma_debug_message (DEBUG_SAVER, "Finished closing input stream");
-
- if (!g_input_stream_close_finish (stream, res, &error))
- {
- pluma_debug_message (DEBUG_SAVER, "Closing input stream error: %s", error->message);
-
- async_failed (async, error);
- return;
- }
-
- pluma_debug_message (DEBUG_SAVER, "Close output stream");
- if (!g_output_stream_close (async->loader->priv->output,
- async->cancellable, &error))
- {
- async_failed (async, error);
- return;
- }
-
- remote_load_completed_or_failed (async->loader, async);
-}
-
-static void
-write_complete (AsyncData *async)
-{
- if (async->loader->priv->stream)
- g_input_stream_close_async (G_INPUT_STREAM (async->loader->priv->stream),
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback)close_input_stream_ready_cb,
- async);
-}
-
-/* prototype, because they call each other... isn't C lovely */
-static void read_file_chunk (AsyncData *async);
-
-static void
-write_file_chunk (AsyncData *async)
-{
- PlumaGioDocumentLoader *gvloader;
- gssize bytes_written;
- GError *error = NULL;
-
- gvloader = async->loader;
-
- /* we use sync methods on doc stream since it is in memory. Using async
- would be racy and we can endup with invalidated iters */
- bytes_written = g_output_stream_write (G_OUTPUT_STREAM (gvloader->priv->output),
- gvloader->priv->buffer,
- async->read,
- async->cancellable,
- &error);
-
- pluma_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written);
- if (bytes_written == -1)
- {
- pluma_debug_message (DEBUG_SAVER, "Write error: %s", error->message);
- async_failed (async, error);
- return;
- }
-
- /* note that this signal blocks the read... check if it isn't
- * a performance problem
- */
- pluma_document_loader_loading (PLUMA_DOCUMENT_LOADER (gvloader),
- FALSE,
- NULL);
-
- read_file_chunk (async);
-}
-
-static void
-async_read_cb (GInputStream *stream,
- GAsyncResult *res,
- AsyncData *async)
-{
- pluma_debug (DEBUG_LOADER);
- PlumaGioDocumentLoader *gvloader;
- GError *error = NULL;
-
- pluma_debug (DEBUG_LOADER);
-
- /* manually check cancelled state */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- gvloader = async->loader;
-
- async->read = g_input_stream_read_finish (stream, res, &error);
-
- /* error occurred */
- if (async->read == -1)
- {
- async_failed (async, error);
- return;
- }
-
- /* Check for the extremely unlikely case where the file size overflows. */
- if (gvloader->priv->bytes_read + async->read < gvloader->priv->bytes_read)
- {
- g_set_error (&gvloader->priv->error,
- PLUMA_DOCUMENT_ERROR,
- PLUMA_DOCUMENT_ERROR_TOO_BIG,
- "File too big");
-
- async_failed (async, gvloader->priv->error);
- return;
- }
-
- /* Bump the size. */
- gvloader->priv->bytes_read += async->read;
-
- /* end of the file, we are done! */
- if (async->read == 0)
- {
- PlumaDocumentLoader *loader;
-
- loader = PLUMA_DOCUMENT_LOADER (gvloader);
-
- g_output_stream_flush (gvloader->priv->output,
- NULL,
- &gvloader->priv->error);
-
- loader->auto_detected_encoding =
- pluma_smart_charset_converter_get_guessed (gvloader->priv->converter);
-
- loader->auto_detected_newline_type =
- pluma_document_output_stream_detect_newline_type (PLUMA_DOCUMENT_OUTPUT_STREAM (gvloader->priv->output));
-
- /* Check if we needed some fallback char, if so, check if there was
- a previous error and if not set a fallback used error */
- /* FIXME Uncomment this when we want to manage conversion fallback */
- /*if ((pluma_smart_charset_converter_get_num_fallbacks (gvloader->priv->converter) != 0) &&
- gvloader->priv->error == NULL)
- {
- g_set_error_literal (&gvloader->priv->error,
- PLUMA_DOCUMENT_ERROR,
- PLUMA_DOCUMENT_ERROR_CONVERSION_FALLBACK,
- "There was a conversion error and it was "
- "needed to use a fallback char");
- }*/
-
- write_complete (async);
-
- return;
- }
-
- write_file_chunk (async);
-}
-
-static void
-read_file_chunk (AsyncData *async)
-{
- PlumaGioDocumentLoader *gvloader;
-
- gvloader = async->loader;
-
- g_input_stream_read_async (G_INPUT_STREAM (gvloader->priv->stream),
- gvloader->priv->buffer,
- READ_CHUNK_SIZE,
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback) async_read_cb,
- async);
-}
-
-static GSList *
-get_candidate_encodings (PlumaGioDocumentLoader *gvloader)
-{
- const PlumaEncoding *metadata;
- GSList *encodings = NULL;
-
- encodings = pluma_prefs_manager_get_auto_detected_encodings ();
-
- metadata = get_metadata_encoding (PLUMA_DOCUMENT_LOADER (gvloader));
- if (metadata != NULL)
- {
- encodings = g_slist_prepend (encodings, (gpointer)metadata);
- }
-
- return encodings;
-}
-
-static void
-finish_query_info (AsyncData *async)
-{
- PlumaGioDocumentLoader *gvloader;
- PlumaDocumentLoader *loader;
- GInputStream *conv_stream;
- GFileInfo *info;
- GSList *candidate_encodings;
-
- gvloader = async->loader;
- loader = PLUMA_DOCUMENT_LOADER (gvloader);
- info = loader->info;
-
- /* if it's not a regular file, error out... */
- if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) &&
- g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
- {
- g_set_error (&gvloader->priv->error,
- G_IO_ERROR,
- G_IO_ERROR_NOT_REGULAR_FILE,
- "Not a regular file");
-
- remote_load_completed_or_failed (gvloader, async);
-
- return;
- }
-
- /* Get the candidate encodings */
- if (loader->encoding == NULL)
- {
- candidate_encodings = get_candidate_encodings (gvloader);
- }
- else
- {
- candidate_encodings = g_slist_prepend (NULL, (gpointer) loader->encoding);
- }
-
- gvloader->priv->converter = pluma_smart_charset_converter_new (candidate_encodings);
- g_slist_free (candidate_encodings);
-
- conv_stream = g_converter_input_stream_new (gvloader->priv->stream,
- G_CONVERTER (gvloader->priv->converter));
- g_object_unref (gvloader->priv->stream);
-
- gvloader->priv->stream = conv_stream;
-
- /* Output stream */
- gvloader->priv->output = pluma_document_output_stream_new (loader->document);
-
- /* start reading */
- read_file_chunk (async);
-}
-
-static void
-query_info_cb (GFile *source,
- GAsyncResult *res,
- AsyncData *async)
-{
- PlumaGioDocumentLoader *gvloader;
- GFileInfo *info;
- GError *error = NULL;
-
- pluma_debug (DEBUG_LOADER);
-
- /* manually check the cancelled state */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- gvloader = async->loader;
-
- /* finish the info query */
- info = g_file_query_info_finish (gvloader->priv->gfile,
- res,
- &error);
-
- if (info == NULL)
- {
- /* propagate the error and clean up */
- async_failed (async, error);
- return;
- }
-
- PLUMA_DOCUMENT_LOADER (gvloader)->info = info;
-
- finish_query_info (async);
-}
-
-static void
-mount_ready_callback (GFile *file,
- GAsyncResult *res,
- AsyncData *async)
-{
- GError *error = NULL;
- gboolean mounted;
-
- pluma_debug (DEBUG_LOADER);
-
- /* manual check for cancelled state */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
-
- if (!mounted)
- {
- async_failed (async, error);
- }
- else
- {
- /* try again to open the file for reading */
- open_async_read (async);
- }
-}
-
-static void
-recover_not_mounted (AsyncData *async)
-{
- PlumaDocument *doc;
- GMountOperation *mount_operation;
-
- pluma_debug (DEBUG_LOADER);
-
- doc = pluma_document_loader_get_document (PLUMA_DOCUMENT_LOADER (async->loader));
- mount_operation = _pluma_document_create_mount_operation (doc);
-
- async->tried_mount = TRUE;
- g_file_mount_enclosing_volume (async->loader->priv->gfile,
- G_MOUNT_MOUNT_NONE,
- mount_operation,
- async->cancellable,
- (GAsyncReadyCallback) mount_ready_callback,
- async);
-
- g_object_unref (mount_operation);
-}
-
-static void
-async_read_ready_callback (GObject *source,
- GAsyncResult *res,
- AsyncData *async)
-{
- GError *error = NULL;
- PlumaGioDocumentLoader *gvloader;
-
- pluma_debug (DEBUG_LOADER);
-
- /* manual check for cancelled state */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- gvloader = async->loader;
-
- gvloader->priv->stream = G_INPUT_STREAM (g_file_read_finish (gvloader->priv->gfile,
- res, &error));
-
- if (!gvloader->priv->stream)
- {
- if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount)
- {
- recover_not_mounted (async);
- g_error_free (error);
- return;
- }
-
- /* Propagate error */
- g_propagate_error (&gvloader->priv->error, error);
- pluma_document_loader_loading (PLUMA_DOCUMENT_LOADER (gvloader),
- TRUE,
- gvloader->priv->error);
-
- async_data_free (async);
- return;
- }
-
- /* get the file info: note we cannot use
- * g_file_input_stream_query_info_async since it is not able to get the
- * content type etc, beside it is not supported by gvfs.
- * Using the file instead of the stream is slightly racy, but for
- * loading this is not too bad...
- */
- g_file_query_info_async (gvloader->priv->gfile,
- REMOTE_QUERY_ATTRIBUTES,
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback) query_info_cb,
- async);
-}
-
-static void
-open_async_read (AsyncData *async)
-{
- g_file_read_async (async->loader->priv->gfile,
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback) async_read_ready_callback,
- async);
-}
-
-static void
-pluma_gio_document_loader_load (PlumaDocumentLoader *loader)
-{
- PlumaGioDocumentLoader *gvloader = PLUMA_GIO_DOCUMENT_LOADER (loader);
- AsyncData *async;
-
- pluma_debug (DEBUG_LOADER);
-
- /* make sure no load operation is currently running */
- g_return_if_fail (gvloader->priv->cancellable == NULL);
-
- gvloader->priv->gfile = g_file_new_for_uri (loader->uri);
-
- /* loading start */
- pluma_document_loader_loading (PLUMA_DOCUMENT_LOADER (gvloader),
- FALSE,
- NULL);
-
- gvloader->priv->cancellable = g_cancellable_new ();
- async = async_data_new (gvloader);
-
- open_async_read (async);
-}
-
-static goffset
-pluma_gio_document_loader_get_bytes_read (PlumaDocumentLoader *loader)
-{
- return PLUMA_GIO_DOCUMENT_LOADER (loader)->priv->bytes_read;
-}
-
-static gboolean
-pluma_gio_document_loader_cancel (PlumaDocumentLoader *loader)
-{
- PlumaGioDocumentLoader *gvloader = PLUMA_GIO_DOCUMENT_LOADER (loader);
-
- if (gvloader->priv->cancellable == NULL)
- return FALSE;
-
- g_cancellable_cancel (gvloader->priv->cancellable);
-
- g_set_error (&gvloader->priv->error,
- G_IO_ERROR,
- G_IO_ERROR_CANCELLED,
- "Operation cancelled");
-
- remote_load_completed_or_failed (gvloader, NULL);
-
- return TRUE;
-}
diff --git a/pluma/pluma-gio-document-loader.h b/pluma/pluma-gio-document-loader.h
deleted file mode 100644
index a350d8ce..00000000
--- a/pluma/pluma-gio-document-loader.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * pluma-gio-document-loader.h
- * This file is part of pluma
- *
- * Copyright (C) 2005 - Paolo Maggi
- * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux
- * Copyright (C) 2008 - Jesse van den Kieboom
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-/*
- * Modified by the pluma Team, 2005-2008. See the AUTHORS file for a
- * list of people on the pluma Team.
- * See the ChangeLog files for a list of changes.
- *
- * $Id$
- */
-
-#ifndef __PLUMA_GIO_DOCUMENT_LOADER_H__
-#define __PLUMA_GIO_DOCUMENT_LOADER_H__
-
-#include <pluma/pluma-document.h>
-#include "pluma-document-loader.h"
-
-G_BEGIN_DECLS
-
-/*
- * Type checking and casting macros
- */
-#define PLUMA_TYPE_GIO_DOCUMENT_LOADER (pluma_gio_document_loader_get_type())
-#define PLUMA_GIO_DOCUMENT_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PLUMA_TYPE_GIO_DOCUMENT_LOADER, PlumaGioDocumentLoader))
-#define PLUMA_GIO_DOCUMENT_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PLUMA_TYPE_GIO_DOCUMENT_LOADER, PlumaGioDocumentLoaderClass))
-#define PLUMA_IS_GIO_DOCUMENT_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PLUMA_TYPE_GIO_DOCUMENT_LOADER))
-#define PLUMA_IS_GIO_DOCUMENT_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLUMA_TYPE_GIO_DOCUMENT_LOADER))
-#define PLUMA_GIO_DOCUMENT_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PLUMA_TYPE_GIO_DOCUMENT_LOADER, PlumaGioDocumentLoaderClass))
-
-/* Private structure type */
-typedef struct _PlumaGioDocumentLoaderPrivate PlumaGioDocumentLoaderPrivate;
-
-/*
- * Main object structure
- */
-typedef struct _PlumaGioDocumentLoader PlumaGioDocumentLoader;
-
-struct _PlumaGioDocumentLoader
-{
- PlumaDocumentLoader loader;
-
- /*< private > */
- PlumaGioDocumentLoaderPrivate *priv;
-};
-
-/*
- * Class definition
- */
-typedef PlumaDocumentLoaderClass PlumaGioDocumentLoaderClass;
-
-/*
- * Public methods
- */
-GType pluma_gio_document_loader_get_type (void) G_GNUC_CONST;
-
-G_END_DECLS
-
-#endif /* __PLUMA_GIO_DOCUMENT_LOADER_H__ */
diff --git a/pluma/pluma-gio-document-saver.c b/pluma/pluma-gio-document-saver.c
deleted file mode 100644
index ac1d9805..00000000
--- a/pluma/pluma-gio-document-saver.c
+++ /dev/null
@@ -1,778 +0,0 @@
-/*
- * pluma-gio-document-saver.c
- * This file is part of pluma
- *
- * Copyright (C) 2005-2006 - Paolo Borelli and Paolo Maggi
- * Copyright (C) 2007 - Paolo Borelli, Paolo Maggi, Steve Frécinaux
- * Copyright (C) 2008 - Jesse van den Kieboom
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-/*
- * Modified by the pluma Team, 2005-2006. See the AUTHORS file for a
- * list of people on the pluma Team.
- * See the ChangeLog files for a list of changes.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib/gi18n.h>
-#include <glib.h>
-#include <gio/gio.h>
-#include <string.h>
-
-#include "pluma-gio-document-saver.h"
-#include "pluma-document-input-stream.h"
-#include "pluma-debug.h"
-
-#define WRITE_CHUNK_SIZE 8192
-
-typedef struct
-{
- PlumaGioDocumentSaver *saver;
- gchar buffer[WRITE_CHUNK_SIZE];
- GCancellable *cancellable;
- gboolean tried_mount;
- gssize written;
- gssize read;
- GError *error;
-} AsyncData;
-
-#define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
- G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
- G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC
-
-static void pluma_gio_document_saver_save (PlumaDocumentSaver *saver,
- gint64 *old_mtime);
-static goffset pluma_gio_document_saver_get_file_size (PlumaDocumentSaver *saver);
-static goffset pluma_gio_document_saver_get_bytes_written (PlumaDocumentSaver *saver);
-
-
-static void check_modified_async (AsyncData *async);
-
-struct _PlumaGioDocumentSaverPrivate
-{
- gint64 old_mtime;
-
- goffset size;
- goffset bytes_written;
-
- GFile *gfile;
- GCancellable *cancellable;
- GOutputStream *stream;
- GInputStream *input;
-
- GError *error;
-};
-
-G_DEFINE_TYPE_WITH_PRIVATE (PlumaGioDocumentSaver, pluma_gio_document_saver, PLUMA_TYPE_DOCUMENT_SAVER)
-
-static void
-pluma_gio_document_saver_dispose (GObject *object)
-{
- PlumaGioDocumentSaverPrivate *priv = PLUMA_GIO_DOCUMENT_SAVER (object)->priv;
-
- if (priv->cancellable != NULL)
- {
- g_cancellable_cancel (priv->cancellable);
- g_object_unref (priv->cancellable);
- priv->cancellable = NULL;
- }
-
- if (priv->gfile != NULL)
- {
- g_object_unref (priv->gfile);
- priv->gfile = NULL;
- }
-
- if (priv->error != NULL)
- {
- g_error_free (priv->error);
- priv->error = NULL;
- }
-
- if (priv->stream != NULL)
- {
- g_object_unref (priv->stream);
- priv->stream = NULL;
- }
-
- if (priv->input != NULL)
- {
- g_object_unref (priv->input);
- priv->input = NULL;
- }
-
- G_OBJECT_CLASS (pluma_gio_document_saver_parent_class)->dispose (object);
-}
-
-static AsyncData *
-async_data_new (PlumaGioDocumentSaver *gvsaver)
-{
- AsyncData *async;
-
- async = g_slice_new (AsyncData);
- async->saver = gvsaver;
- async->cancellable = g_object_ref (gvsaver->priv->cancellable);
-
- async->tried_mount = FALSE;
- async->written = 0;
- async->read = 0;
-
- async->error = NULL;
-
- return async;
-}
-
-static void
-async_data_free (AsyncData *async)
-{
- g_object_unref (async->cancellable);
-
- if (async->error)
- {
- g_error_free (async->error);
- }
-
- g_slice_free (AsyncData, async);
-}
-
-static void
-pluma_gio_document_saver_class_init (PlumaGioDocumentSaverClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- PlumaDocumentSaverClass *saver_class = PLUMA_DOCUMENT_SAVER_CLASS (klass);
-
- object_class->dispose = pluma_gio_document_saver_dispose;
-
- saver_class->save = pluma_gio_document_saver_save;
- saver_class->get_file_size = pluma_gio_document_saver_get_file_size;
- saver_class->get_bytes_written = pluma_gio_document_saver_get_bytes_written;
-}
-
-static void
-pluma_gio_document_saver_init (PlumaGioDocumentSaver *gvsaver)
-{
- gvsaver->priv = pluma_gio_document_saver_get_instance_private (gvsaver);
-
- gvsaver->priv->cancellable = g_cancellable_new ();
- gvsaver->priv->error = NULL;
-}
-
-static void
-remote_save_completed_or_failed (PlumaGioDocumentSaver *gvsaver,
- AsyncData *async)
-{
- pluma_document_saver_saving (PLUMA_DOCUMENT_SAVER (gvsaver),
- TRUE,
- gvsaver->priv->error);
-
- if (async)
- async_data_free (async);
-}
-
-static void
-async_failed (AsyncData *async,
- GError *error)
-{
- g_propagate_error (&async->saver->priv->error, error);
- remote_save_completed_or_failed (async->saver, async);
-}
-
-/* BEGIN NOTE:
- *
- * This fixes an issue in GOutputStream that applies the atomic replace
- * save strategy. The stream moves the written file to the original file
- * when the stream is closed. However, there is no way currently to tell
- * the stream that the save should be aborted (there could be a
- * conversion error). The patch explicitly closes the output stream
- * in all these cases with a GCancellable in the cancelled state, causing
- * the output stream to close, but not move the file. This makes use
- * of an implementation detail in the local gio file stream and should be
- * properly fixed by adding the appropriate API in gio. Until then, at least
- * we prevent data corruption for now.
- *
- * Relevant bug reports:
- *
- * Bug 615110 - write file ignore encoding errors (pluma)
- * https://bugzilla.gnome.org/show_bug.cgi?id=615110
- *
- * Bug 602412 - g_file_replace does not restore original file when there is
- * errors while writing (glib/gio)
- * https://bugzilla.gnome.org/show_bug.cgi?id=602412
- */
-static void
-cancel_output_stream_ready_cb (GOutputStream *stream,
- GAsyncResult *result,
- AsyncData *async)
-{
- GError *error;
-
- g_output_stream_close_finish (stream, result, NULL);
-
- /* check cancelled state manually */
- if (g_cancellable_is_cancelled (async->cancellable) || async->error == NULL)
- {
- async_data_free (async);
- return;
- }
-
- error = async->error;
- async->error = NULL;
-
- async_failed (async, error);
-}
-
-static void
-cancel_output_stream (AsyncData *async)
-{
- GCancellable *cancellable;
-
- pluma_debug_message (DEBUG_SAVER, "Cancel output stream");
-
- cancellable = g_cancellable_new ();
- g_cancellable_cancel (cancellable);
-
- g_output_stream_close_async (async->saver->priv->stream,
- G_PRIORITY_HIGH,
- cancellable,
- (GAsyncReadyCallback)cancel_output_stream_ready_cb,
- async);
-
- g_object_unref (cancellable);
-}
-
-static void
-cancel_output_stream_and_fail (AsyncData *async,
- GError *error)
-{
-
- pluma_debug_message (DEBUG_SAVER, "Cancel output stream and fail");
-
- g_propagate_error (&async->error, error);
- cancel_output_stream (async);
-}
-
-/*
- * END NOTE
- */
-
-static void
-remote_get_info_cb (GFile *source,
- GAsyncResult *res,
- AsyncData *async)
-{
- PlumaGioDocumentSaver *saver;
- GFileInfo *info;
- GError *error = NULL;
-
- pluma_debug (DEBUG_SAVER);
-
- /* check cancelled state manually */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- saver = async->saver;
-
- pluma_debug_message (DEBUG_SAVER, "Finished query info on file");
- info = g_file_query_info_finish (source, res, &error);
-
- if (info != NULL)
- {
- if (PLUMA_DOCUMENT_SAVER (saver)->info != NULL)
- g_object_unref (PLUMA_DOCUMENT_SAVER (saver)->info);
-
- PLUMA_DOCUMENT_SAVER (saver)->info = info;
- }
- else
- {
- pluma_debug_message (DEBUG_SAVER, "Query info failed: %s", error->message);
- g_propagate_error (&saver->priv->error, error);
- }
-
- remote_save_completed_or_failed (saver, async);
-}
-
-static void
-close_async_ready_get_info_cb (GOutputStream *stream,
- GAsyncResult *res,
- AsyncData *async)
-{
- GError *error = NULL;
-
- pluma_debug (DEBUG_SAVER);
-
- /* check cancelled state manually */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- pluma_debug_message (DEBUG_SAVER, "Finished closing stream");
-
- if (!g_output_stream_close_finish (stream, res, &error))
- {
- pluma_debug_message (DEBUG_SAVER, "Closing stream error: %s", error->message);
-
- async_failed (async, error);
- return;
- }
-
- /* get the file info: note we cannot use
- * g_file_output_stream_query_info_async since it is not able to get the
- * content type etc, beside it is not supported by gvfs.
- * I'm not sure this is actually necessary, can't we just use
- * g_content_type_guess (since we have the file name and the data)
- */
- pluma_debug_message (DEBUG_SAVER, "Query info on file");
- g_file_query_info_async (async->saver->priv->gfile,
- REMOTE_QUERY_ATTRIBUTES,
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback) remote_get_info_cb,
- async);
-}
-
-static void
-write_complete (AsyncData *async)
-{
- GError *error = NULL;
-
- /* first we close the input stream */
- pluma_debug_message (DEBUG_SAVER, "Close input stream");
- if (!g_input_stream_close (async->saver->priv->input,
- async->cancellable, &error))
- {
- pluma_debug_message (DEBUG_SAVER, "Closing input stream error: %s", error->message);
- cancel_output_stream_and_fail (async, error);
- return;
- }
-
- /* now we close the output stream */
- pluma_debug_message (DEBUG_SAVER, "Close output stream");
- g_output_stream_close_async (async->saver->priv->stream,
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback)close_async_ready_get_info_cb,
- async);
-}
-
-/* prototype, because they call each other... isn't C lovely */
-static void read_file_chunk (AsyncData *async);
-static void write_file_chunk (AsyncData *async);
-
-static void
-async_write_cb (GOutputStream *stream,
- GAsyncResult *res,
- AsyncData *async)
-{
- PlumaGioDocumentSaver *gvsaver;
- gssize bytes_written;
- GError *error = NULL;
-
- pluma_debug (DEBUG_SAVER);
-
- /* Check cancelled state manually */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- cancel_output_stream (async);
- return;
- }
-
- bytes_written = g_output_stream_write_finish (stream, res, &error);
-
- pluma_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written);
-
- if (bytes_written == -1)
- {
- pluma_debug_message (DEBUG_SAVER, "Write error: %s", error->message);
- cancel_output_stream_and_fail (async, error);
- return;
- }
-
- gvsaver = async->saver;
- async->written += bytes_written;
-
- /* write again */
- if (async->written != async->read)
- {
- write_file_chunk (async);
- return;
- }
-
- /* note that this signal blocks the write... check if it isn't
- * a performance problem
- */
- pluma_document_saver_saving (PLUMA_DOCUMENT_SAVER (gvsaver),
- FALSE,
- NULL);
-
- read_file_chunk (async);
-}
-
-static void
-write_file_chunk (AsyncData *async)
-{
- PlumaGioDocumentSaver *gvsaver;
-
- pluma_debug (DEBUG_SAVER);
-
- gvsaver = async->saver;
-
- g_output_stream_write_async (G_OUTPUT_STREAM (gvsaver->priv->stream),
- async->buffer + async->written,
- async->read - async->written,
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback) async_write_cb,
- async);
-}
-
-static void
-read_file_chunk (AsyncData *async)
-{
- PlumaGioDocumentSaver *gvsaver;
- PlumaDocumentInputStream *dstream;
- GError *error = NULL;
-
- pluma_debug (DEBUG_SAVER);
-
- gvsaver = async->saver;
- async->written = 0;
-
- /* we use sync methods on doc stream since it is in memory. Using async
- would be racy and we can endup with invalidated iters */
- async->read = g_input_stream_read (gvsaver->priv->input,
- async->buffer,
- WRITE_CHUNK_SIZE,
- async->cancellable,
- &error);
-
- if (error != NULL)
- {
- cancel_output_stream_and_fail (async, error);
- return;
- }
-
- /* Check if we finished reading and writing */
- if (async->read == 0)
- {
- write_complete (async);
- return;
- }
-
- /* Get how many chars have been read */
- dstream = PLUMA_DOCUMENT_INPUT_STREAM (gvsaver->priv->input);
- gvsaver->priv->bytes_written = pluma_document_input_stream_tell (dstream);
-
- write_file_chunk (async);
-}
-
-static void
-async_replace_ready_callback (GFile *source,
- GAsyncResult *res,
- AsyncData *async)
-{
- PlumaGioDocumentSaver *gvsaver;
- PlumaDocumentSaver *saver;
- GCharsetConverter *converter;
- GFileOutputStream *file_stream;
- GError *error = NULL;
-
- pluma_debug (DEBUG_SAVER);
-
- /* Check cancelled state manually */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- gvsaver = async->saver;
- saver = PLUMA_DOCUMENT_SAVER (gvsaver);
- file_stream = g_file_replace_finish (source, res, &error);
-
- /* handle any error that might occur */
- if (!file_stream)
- {
- pluma_debug_message (DEBUG_SAVER, "Opening file failed: %s", error->message);
- async_failed (async, error);
- return;
- }
-
- /* FIXME: manage converter error? */
- pluma_debug_message (DEBUG_SAVER, "Encoding charset: %s",
- pluma_encoding_get_charset (saver->encoding));
-
- if (saver->encoding != pluma_encoding_get_utf8 ())
- {
- converter = g_charset_converter_new (pluma_encoding_get_charset (saver->encoding),
- "UTF-8",
- NULL);
- gvsaver->priv->stream = g_converter_output_stream_new (G_OUTPUT_STREAM (file_stream),
- G_CONVERTER (converter));
-
- g_object_unref (file_stream);
- g_object_unref (converter);
- }
- else
- {
- gvsaver->priv->stream = G_OUTPUT_STREAM (file_stream);
- }
-
- gvsaver->priv->input = pluma_document_input_stream_new (GTK_TEXT_BUFFER (saver->document),
- saver->newline_type);
-
- gvsaver->priv->size = pluma_document_input_stream_get_total_size (PLUMA_DOCUMENT_INPUT_STREAM (gvsaver->priv->input));
-
- read_file_chunk (async);
-}
-
-static void
-begin_write (AsyncData *async)
-{
- PlumaGioDocumentSaver *gvsaver;
- PlumaDocumentSaver *saver;
- gboolean backup;
-
- pluma_debug_message (DEBUG_SAVER, "Start replacing file contents");
-
- /* For remote files we simply use g_file_replace_async. There is no
- * backup as of yet
- */
- gvsaver = async->saver;
- saver = PLUMA_DOCUMENT_SAVER (gvsaver);
-
- /* Do not make backups for remote files so they do not clutter remote systems */
- backup = (saver->keep_backup && pluma_document_is_local (saver->document));
-
- pluma_debug_message (DEBUG_SAVER, "File contents size: %" G_GINT64_FORMAT, gvsaver->priv->size);
- pluma_debug_message (DEBUG_SAVER, "Calling replace_async");
- pluma_debug_message (DEBUG_SAVER, backup ? "Keep backup" : "Discard backup");
-
- g_file_replace_async (gvsaver->priv->gfile,
- NULL,
- backup,
- G_FILE_CREATE_NONE,
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback) async_replace_ready_callback,
- async);
-}
-
-static void
-mount_ready_callback (GFile *file,
- GAsyncResult *res,
- AsyncData *async)
-{
- GError *error = NULL;
- gboolean mounted;
-
- pluma_debug (DEBUG_SAVER);
-
- /* manual check for cancelled state */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
-
- if (!mounted)
- {
- async_failed (async, error);
- }
- else
- {
- /* try again to get the modified state */
- check_modified_async (async);
- }
-}
-
-static void
-recover_not_mounted (AsyncData *async)
-{
- PlumaDocument *doc;
- GMountOperation *mount_operation;
-
- pluma_debug (DEBUG_LOADER);
-
- doc = pluma_document_saver_get_document (PLUMA_DOCUMENT_SAVER (async->saver));
- mount_operation = _pluma_document_create_mount_operation (doc);
-
- async->tried_mount = TRUE;
- g_file_mount_enclosing_volume (async->saver->priv->gfile,
- G_MOUNT_MOUNT_NONE,
- mount_operation,
- async->cancellable,
- (GAsyncReadyCallback) mount_ready_callback,
- async);
-
- g_object_unref (mount_operation);
-}
-
-static void
-check_modification_callback (GFile *source,
- GAsyncResult *res,
- AsyncData *async)
-{
- PlumaGioDocumentSaver *gvsaver;
- GError *error = NULL;
- GFileInfo *info;
-
- pluma_debug (DEBUG_SAVER);
-
- /* manually check cancelled state */
- if (g_cancellable_is_cancelled (async->cancellable))
- {
- async_data_free (async);
- return;
- }
-
- gvsaver = async->saver;
- info = g_file_query_info_finish (source, res, &error);
- if (info == NULL)
- {
- if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount)
- {
- recover_not_mounted (async);
- g_error_free (error);
- return;
- }
-
- /* it's perfectly fine if the file doesn't exist yet */
- if (error->code != G_IO_ERROR_NOT_FOUND)
- {
- pluma_debug_message (DEBUG_SAVER, "Error getting modification: %s", error->message);
-
- async_failed (async, error);
- return;
- }
- }
-
- /* check if the mtime is > what we know about it (if we have it) */
- if (info != NULL && g_file_info_has_attribute (info,
- G_FILE_ATTRIBUTE_TIME_MODIFIED))
- {
- guint64 mtime;
- gint64 old_mtime;
-
- mtime = g_file_info_get_attribute_uint64 (info,
- G_FILE_ATTRIBUTE_TIME_MODIFIED) * G_USEC_PER_SEC;
- if (g_file_info_has_attribute (info,
- G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC))
- {
- guint32 usec = g_file_info_get_attribute_uint32 (info,
- G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
- mtime += (guint64) usec;
- }
-
- old_mtime = gvsaver->priv->old_mtime;
-
- if ((old_mtime > 0 || ((gint64) mtime) > 0) && (((gint64) mtime) != old_mtime) &&
- (PLUMA_DOCUMENT_SAVER (gvsaver)->flags & PLUMA_DOCUMENT_SAVE_IGNORE_MTIME) == 0)
- {
- pluma_debug_message (DEBUG_SAVER, "File is externally modified");
- g_set_error (&gvsaver->priv->error,
- PLUMA_DOCUMENT_ERROR,
- PLUMA_DOCUMENT_ERROR_EXTERNALLY_MODIFIED,
- "Externally modified");
-
- remote_save_completed_or_failed (gvsaver, async);
- g_object_unref (info);
-
- return;
- }
- }
-
- if (info != NULL)
- g_object_unref (info);
-
- /* modification check passed, start write */
- begin_write (async);
-}
-
-static void
-check_modified_async (AsyncData *async)
-{
- pluma_debug_message (DEBUG_SAVER, "Check externally modified");
-
- g_file_query_info_async (async->saver->priv->gfile,
- G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_HIGH,
- async->cancellable,
- (GAsyncReadyCallback) check_modification_callback,
- async);
-}
-
-static gboolean
-save_remote_file_real (PlumaGioDocumentSaver *gvsaver)
-{
- AsyncData *async;
-
- pluma_debug_message (DEBUG_SAVER, "Starting gio save");
-
- /* First find out if the file is modified externally. This requires
- * a stat, but I don't think we can do this any other way
- */
- async = async_data_new (gvsaver);
-
- check_modified_async (async);
-
- /* return false to stop timeout */
- return FALSE;
-}
-
-static void
-pluma_gio_document_saver_save (PlumaDocumentSaver *saver,
- gint64 *old_mtime)
-{
- PlumaGioDocumentSaver *gvsaver = PLUMA_GIO_DOCUMENT_SAVER (saver);
-
- gvsaver->priv->old_mtime = *old_mtime;
- gvsaver->priv->gfile = g_file_new_for_uri (saver->uri);
-
- /* saving start */
- pluma_document_saver_saving (saver, FALSE, NULL);
-
- g_timeout_add_full (G_PRIORITY_HIGH,
- 0,
- (GSourceFunc) save_remote_file_real,
- gvsaver,
- NULL);
-}
-
-static goffset
-pluma_gio_document_saver_get_file_size (PlumaDocumentSaver *saver)
-{
- return PLUMA_GIO_DOCUMENT_SAVER (saver)->priv->size;
-}
-
-static goffset
-pluma_gio_document_saver_get_bytes_written (PlumaDocumentSaver *saver)
-{
- return PLUMA_GIO_DOCUMENT_SAVER (saver)->priv->bytes_written;
-}
diff --git a/pluma/pluma-gio-document-saver.h b/pluma/pluma-gio-document-saver.h
deleted file mode 100644
index 21ecbb3e..00000000
--- a/pluma/pluma-gio-document-saver.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * pluma-gio-document-saver.h
- * This file is part of pluma
- *
- * Copyright (C) 2005 - Paolo Maggi
- * Copyrhing (C) 2007 - Paolo Maggi, Steve Frécinaux
- * Copyright (C) 2008 - Jesse van den Kieboom
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-/*
- * Modified by the pluma Team, 2005-2007. See the AUTHORS file for a
- * list of people on the pluma Team.
- * See the ChangeLog files for a list of changes.
- */
-
-#ifndef __PLUMA_GIO_DOCUMENT_SAVER_H__
-#define __PLUMA_GIO_DOCUMENT_SAVER_H__
-
-#include <pluma/pluma-document-saver.h>
-
-G_BEGIN_DECLS
-
-/*
- * Type checking and casting macros
- */
-#define PLUMA_TYPE_GIO_DOCUMENT_SAVER (pluma_gio_document_saver_get_type())
-#define PLUMA_GIO_DOCUMENT_SAVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PLUMA_TYPE_GIO_DOCUMENT_SAVER, PlumaGioDocumentSaver))
-#define PLUMA_GIO_DOCUMENT_SAVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PLUMA_TYPE_GIO_DOCUMENT_SAVER, PlumaGioDocumentSaverClass))
-#define PLUMA_IS_GIO_DOCUMENT_SAVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PLUMA_TYPE_GIO_DOCUMENT_SAVER))
-#define PLUMA_IS_GIO_DOCUMENT_SAVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLUMA_TYPE_GIO_DOCUMENT_SAVER))
-#define PLUMA_GIO_DOCUMENT_SAVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PLUMA_TYPE_GIO_DOCUMENT_SAVER, PlumaGioDocumentSaverClass))
-
-/* Private structure type */
-typedef struct _PlumaGioDocumentSaverPrivate PlumaGioDocumentSaverPrivate;
-
-/*
- * Main object structure
- */
-typedef struct _PlumaGioDocumentSaver PlumaGioDocumentSaver;
-
-struct _PlumaGioDocumentSaver
-{
- PlumaDocumentSaver saver;
-
- /*< private > */
- PlumaGioDocumentSaverPrivate *priv;
-};
-
-/*
- * Class definition
- */
-typedef PlumaDocumentSaverClass PlumaGioDocumentSaverClass;
-
-/*
- * Public methods
- */
-GType pluma_gio_document_saver_get_type (void) G_GNUC_CONST;
-
-G_END_DECLS
-
-#endif /* __PLUMA_GIO_DOCUMENT_SAVER_H__ */