Blame gtksourceview/gtksourcebufferoutputstream.c

Packit a7d494
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
Packit a7d494
/* gtksourcebufferoutputstream.c
Packit a7d494
 * This file is part of GtkSourceView
Packit a7d494
 *
Packit a7d494
 * Copyright (C) 2010 - Ignacio Casal Quinteiro
Packit a7d494
 * Copyright (C) 2014 - Sébastien Wilmet <swilmet@gnome.org>
Packit a7d494
 *
Packit a7d494
 * GtkSourceView is free software; you can redistribute it and/or
Packit a7d494
 * modify it under the terms of the GNU Lesser General Public
Packit a7d494
 * License as published by the Free Software Foundation; either
Packit a7d494
 * version 2.1 of the License, or (at your option) any later version.
Packit a7d494
 *
Packit a7d494
 * GtkSourceView is distributed in the hope that it will be useful,
Packit a7d494
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a7d494
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a7d494
 * Lesser General Public License for more details.
Packit a7d494
 *
Packit a7d494
 * You should have received a copy of the GNU Lesser General Public
Packit a7d494
 * License along with this library; if not, write to the Free Software
Packit a7d494
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit a7d494
 */
Packit a7d494
Packit a7d494
#ifdef HAVE_CONFIG_H
Packit a7d494
#include <config.h>
Packit a7d494
#endif
Packit a7d494
Packit a7d494
#include <string.h>
Packit a7d494
#include <errno.h>
Packit a7d494
#include "gtksourcebufferoutputstream.h"
Packit a7d494
#include "gtksourcebuffer.h"
Packit a7d494
#include "gtksourcebuffer-private.h"
Packit a7d494
#include "gtksourceencoding.h"
Packit a7d494
#include "gtksourcefileloader.h"
Packit a7d494
#include "gtksourceview-i18n.h"
Packit a7d494
Packit a7d494
/* NOTE: never use async methods on this stream, the stream is just
Packit a7d494
 * a wrapper around GtkTextBuffer api so that we can use GIO Stream
Packit a7d494
 * methods, but the underlying code operates on a GtkTextBuffer, so
Packit a7d494
 * there is no I/O involved and should be accessed only by the main
Packit a7d494
 * thread.
Packit a7d494
 */
Packit a7d494
Packit a7d494
/* NOTE2: welcome to a really big headache. At the beginning this was
Packit a7d494
 * split in several classes, one for encoding detection, another
Packit a7d494
 * for UTF-8 conversion and another for validation. The reason this is
Packit a7d494
 * all together is because we need specific information from all parts
Packit a7d494
 * in other to be able to mark characters as invalid if there was some
Packit a7d494
 * specific problem on the conversion.
Packit a7d494
 */
Packit a7d494
Packit a7d494
/* The code comes from gedit, the class was GeditDocumentOutputStream. */
Packit a7d494
Packit a7d494
#if 0
Packit a7d494
#define DEBUG(x) (x)
Packit a7d494
#else
Packit a7d494
#define DEBUG(x)
Packit a7d494
#endif
Packit a7d494
Packit a7d494
#define MAX_UNICHAR_LEN 6
Packit a7d494
Packit a7d494
struct _GtkSourceBufferOutputStreamPrivate
Packit a7d494
{
Packit a7d494
	GtkSourceBuffer *source_buffer;
Packit a7d494
	GtkTextIter pos;
Packit a7d494
Packit a7d494
	gchar *buffer;
Packit a7d494
	gsize buflen;
Packit a7d494
Packit a7d494
	gchar *iconv_buffer;
Packit a7d494
	gsize iconv_buflen;
Packit a7d494
Packit a7d494
	/* Encoding detection */
Packit a7d494
	GIConv iconv;
Packit a7d494
	GCharsetConverter *charset_conv;
Packit a7d494
Packit a7d494
	GSList *encodings;
Packit a7d494
	GSList *current_encoding;
Packit a7d494
Packit a7d494
	gint error_offset;
Packit a7d494
	guint n_fallback_errors;
Packit a7d494
Packit a7d494
	guint is_utf8 : 1;
Packit a7d494
	guint use_first : 1;
Packit a7d494
Packit a7d494
	guint is_initialized : 1;
Packit a7d494
	guint is_closed : 1;
Packit a7d494
Packit a7d494
	guint remove_trailing_newline : 1;
Packit a7d494
};
Packit a7d494
Packit a7d494
enum
Packit a7d494
{
Packit a7d494
	PROP_0,
Packit a7d494
	PROP_BUFFER,
Packit a7d494
	PROP_REMOVE_TRAILING_NEWLINE
Packit a7d494
};
Packit a7d494
Packit a7d494
G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceBufferOutputStream, gtk_source_buffer_output_stream, G_TYPE_OUTPUT_STREAM)
Packit a7d494
Packit a7d494
static gssize gtk_source_buffer_output_stream_write   (GOutputStream  *stream,
Packit a7d494
						       const void     *buffer,
Packit a7d494
						       gsize           count,
Packit a7d494
						       GCancellable   *cancellable,
Packit a7d494
						       GError        **error);
Packit a7d494
Packit a7d494
static gboolean gtk_source_buffer_output_stream_close (GOutputStream  *stream,
Packit a7d494
						       GCancellable   *cancellable,
Packit a7d494
						       GError        **error);
Packit a7d494
Packit a7d494
static gboolean gtk_source_buffer_output_stream_flush (GOutputStream  *stream,
Packit a7d494
						       GCancellable   *cancellable,
Packit a7d494
						       GError        **error);
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_buffer_output_stream_set_property (GObject      *object,
Packit a7d494
					      guint         prop_id,
Packit a7d494
					      const GValue *value,
Packit a7d494
					      GParamSpec   *pspec)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *stream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (object);
Packit a7d494
Packit a7d494
	switch (prop_id)
Packit a7d494
	{
Packit a7d494
		case PROP_BUFFER:
Packit a7d494
			g_assert (stream->priv->source_buffer == NULL);
Packit a7d494
			stream->priv->source_buffer = g_value_dup_object (value);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_REMOVE_TRAILING_NEWLINE:
Packit a7d494
			stream->priv->remove_trailing_newline = g_value_get_boolean (value);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		default:
Packit a7d494
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit a7d494
			break;
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_buffer_output_stream_get_property (GObject    *object,
Packit a7d494
					      guint       prop_id,
Packit a7d494
					      GValue     *value,
Packit a7d494
					      GParamSpec *pspec)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *stream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (object);
Packit a7d494
Packit a7d494
	switch (prop_id)
Packit a7d494
	{
Packit a7d494
		case PROP_BUFFER:
Packit a7d494
			g_value_set_object (value, stream->priv->source_buffer);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_REMOVE_TRAILING_NEWLINE:
Packit a7d494
			g_value_set_boolean (value, stream->priv->remove_trailing_newline);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		default:
Packit a7d494
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit a7d494
			break;
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_buffer_output_stream_dispose (GObject *object)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *stream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (object);
Packit a7d494
Packit a7d494
	g_clear_object (&stream->priv->source_buffer);
Packit a7d494
	g_clear_object (&stream->priv->charset_conv);
Packit a7d494
Packit a7d494
	G_OBJECT_CLASS (gtk_source_buffer_output_stream_parent_class)->dispose (object);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_buffer_output_stream_finalize (GObject *object)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *stream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (object);
Packit a7d494
Packit a7d494
	g_free (stream->priv->buffer);
Packit a7d494
	g_free (stream->priv->iconv_buffer);
Packit a7d494
	g_slist_free (stream->priv->encodings);
Packit a7d494
Packit a7d494
	G_OBJECT_CLASS (gtk_source_buffer_output_stream_parent_class)->finalize (object);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_buffer_output_stream_constructed (GObject *object)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *stream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (object);
Packit a7d494
Packit a7d494
	if (stream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		g_critical ("This should never happen, a problem happened constructing the Buffer Output Stream!");
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	gtk_source_buffer_begin_not_undoable_action (stream->priv->source_buffer);
Packit a7d494
Packit a7d494
	gtk_text_buffer_set_text (GTK_TEXT_BUFFER (stream->priv->source_buffer), "", 0);
Packit a7d494
	gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (stream->priv->source_buffer), FALSE);
Packit a7d494
Packit a7d494
	gtk_source_buffer_end_not_undoable_action (stream->priv->source_buffer);
Packit a7d494
Packit a7d494
	G_OBJECT_CLASS (gtk_source_buffer_output_stream_parent_class)->constructed (object);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_buffer_output_stream_class_init (GtkSourceBufferOutputStreamClass *klass)
Packit a7d494
{
Packit a7d494
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit a7d494
	GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
Packit a7d494
Packit a7d494
	object_class->get_property = gtk_source_buffer_output_stream_get_property;
Packit a7d494
	object_class->set_property = gtk_source_buffer_output_stream_set_property;
Packit a7d494
	object_class->dispose = gtk_source_buffer_output_stream_dispose;
Packit a7d494
	object_class->finalize = gtk_source_buffer_output_stream_finalize;
Packit a7d494
	object_class->constructed = gtk_source_buffer_output_stream_constructed;
Packit a7d494
Packit a7d494
	stream_class->write_fn = gtk_source_buffer_output_stream_write;
Packit a7d494
	stream_class->close_fn = gtk_source_buffer_output_stream_close;
Packit a7d494
	stream_class->flush = gtk_source_buffer_output_stream_flush;
Packit a7d494
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
					 PROP_BUFFER,
Packit a7d494
					 g_param_spec_object ("buffer",
Packit a7d494
							      "GtkSourceBuffer",
Packit a7d494
							      "",
Packit a7d494
							      GTK_SOURCE_TYPE_BUFFER,
Packit a7d494
							      G_PARAM_READWRITE |
Packit a7d494
							      G_PARAM_CONSTRUCT_ONLY |
Packit a7d494
							      G_PARAM_STATIC_STRINGS));
Packit a7d494
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
	                                 PROP_REMOVE_TRAILING_NEWLINE,
Packit a7d494
	                                 g_param_spec_boolean ("remove-trailing-newline",
Packit a7d494
	                                                       "Remove trailing newline",
Packit a7d494
	                                                       "",
Packit a7d494
	                                                       TRUE,
Packit a7d494
	                                                       G_PARAM_READWRITE |
Packit a7d494
	                                                       G_PARAM_CONSTRUCT_ONLY |
Packit a7d494
	                                                       G_PARAM_STATIC_STRINGS));
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_buffer_output_stream_init (GtkSourceBufferOutputStream *stream)
Packit a7d494
{
Packit a7d494
	stream->priv = gtk_source_buffer_output_stream_get_instance_private (stream);
Packit a7d494
Packit a7d494
	stream->priv->buffer = NULL;
Packit a7d494
	stream->priv->buflen = 0;
Packit a7d494
Packit a7d494
	stream->priv->charset_conv = NULL;
Packit a7d494
	stream->priv->encodings = NULL;
Packit a7d494
	stream->priv->current_encoding = NULL;
Packit a7d494
Packit a7d494
	stream->priv->error_offset = -1;
Packit a7d494
Packit a7d494
	stream->priv->is_initialized = FALSE;
Packit a7d494
	stream->priv->is_closed = FALSE;
Packit a7d494
	stream->priv->is_utf8 = FALSE;
Packit a7d494
	stream->priv->use_first = FALSE;
Packit a7d494
}
Packit a7d494
Packit a7d494
static const GtkSourceEncoding *
Packit a7d494
get_encoding (GtkSourceBufferOutputStream *stream)
Packit a7d494
{
Packit a7d494
	if (stream->priv->current_encoding == NULL)
Packit a7d494
	{
Packit a7d494
		stream->priv->current_encoding = stream->priv->encodings;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		stream->priv->current_encoding = g_slist_next (stream->priv->current_encoding);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (stream->priv->current_encoding != NULL)
Packit a7d494
	{
Packit a7d494
		return stream->priv->current_encoding->data;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	stream->priv->use_first = TRUE;
Packit a7d494
	stream->priv->current_encoding = stream->priv->encodings;
Packit a7d494
Packit a7d494
	return stream->priv->current_encoding->data;
Packit a7d494
}
Packit a7d494
Packit a7d494
static gboolean
Packit a7d494
try_convert (GCharsetConverter *converter,
Packit a7d494
             const void        *inbuf,
Packit a7d494
             gsize              inbuf_size)
Packit a7d494
{
Packit a7d494
	GError *err;
Packit a7d494
	gsize bytes_read, nread;
Packit a7d494
	gsize bytes_written, nwritten;
Packit a7d494
	GConverterResult res;
Packit a7d494
	gchar *out;
Packit a7d494
	gboolean ret;
Packit a7d494
	gsize out_size;
Packit a7d494
Packit a7d494
	if (inbuf == NULL || inbuf_size == 0)
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	err = NULL;
Packit a7d494
	nread = 0;
Packit a7d494
	nwritten = 0;
Packit a7d494
	out_size = inbuf_size * 4;
Packit a7d494
	out = g_malloc (out_size);
Packit a7d494
Packit a7d494
	do
Packit a7d494
	{
Packit a7d494
		res = g_converter_convert (G_CONVERTER (converter),
Packit a7d494
		                           (gchar *)inbuf + nread,
Packit a7d494
		                           inbuf_size - nread,
Packit a7d494
		                           (gchar *)out + nwritten,
Packit a7d494
		                           out_size - nwritten,
Packit a7d494
		                           G_CONVERTER_INPUT_AT_END,
Packit a7d494
		                           &bytes_read,
Packit a7d494
		                           &bytes_written,
Packit a7d494
		                           &err;;
Packit a7d494
Packit a7d494
		nread += bytes_read;
Packit a7d494
		nwritten += bytes_written;
Packit a7d494
	} while (res != G_CONVERTER_FINISHED && res != G_CONVERTER_ERROR && err == NULL);
Packit a7d494
Packit a7d494
	if (err != NULL)
Packit a7d494
	{
Packit a7d494
		if (err->code == G_CONVERT_ERROR_PARTIAL_INPUT)
Packit a7d494
		{
Packit a7d494
			/* FIXME We can get partial input while guessing the
Packit a7d494
			   encoding because we just take some amount of text
Packit a7d494
			   to guess from. */
Packit a7d494
			ret = TRUE;
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			ret = FALSE;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		g_error_free (err);
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		ret = TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* FIXME: Check the remainder? */
Packit a7d494
	if (ret == TRUE && !g_utf8_validate (out, nwritten, NULL))
Packit a7d494
	{
Packit a7d494
		ret = FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	g_free (out);
Packit a7d494
Packit a7d494
	return ret;
Packit a7d494
}
Packit a7d494
Packit a7d494
static GCharsetConverter *
Packit a7d494
guess_encoding (GtkSourceBufferOutputStream *stream,
Packit a7d494
	       	const void                  *inbuf,
Packit a7d494
	       	gsize                        inbuf_size)
Packit a7d494
{
Packit a7d494
	GCharsetConverter *conv = NULL;
Packit a7d494
Packit a7d494
	if (inbuf == NULL || inbuf_size == 0)
Packit a7d494
	{
Packit a7d494
		stream->priv->is_utf8 = TRUE;
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (stream->priv->encodings != NULL &&
Packit a7d494
	    stream->priv->encodings->next == NULL)
Packit a7d494
	{
Packit a7d494
		stream->priv->use_first = TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* We just check the first block */
Packit a7d494
	while (TRUE)
Packit a7d494
	{
Packit a7d494
		const GtkSourceEncoding *enc;
Packit a7d494
Packit a7d494
		g_clear_object (&conv;;
Packit a7d494
Packit a7d494
		/* We get an encoding from the list */
Packit a7d494
		enc = get_encoding (stream);
Packit a7d494
Packit a7d494
		/* if it is NULL we didn't guess anything */
Packit a7d494
		if (enc == NULL)
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		DEBUG ({
Packit a7d494
		       g_print ("trying charset: %s\n",
Packit a7d494
				gtk_source_encoding_get_charset (stream->priv->current_encoding->data));
Packit a7d494
		});
Packit a7d494
Packit a7d494
		if (enc == gtk_source_encoding_get_utf8 ())
Packit a7d494
		{
Packit a7d494
			gsize remainder;
Packit a7d494
			const gchar *end;
Packit a7d494
Packit a7d494
			if (g_utf8_validate (inbuf, inbuf_size, &end) ||
Packit a7d494
			    stream->priv->use_first)
Packit a7d494
			{
Packit a7d494
				stream->priv->is_utf8 = TRUE;
Packit a7d494
				break;
Packit a7d494
			}
Packit a7d494
Packit a7d494
			/* Check if the end is less than one char */
Packit a7d494
			remainder = inbuf_size - (end - (gchar *)inbuf);
Packit a7d494
			if (remainder < 6)
Packit a7d494
			{
Packit a7d494
				stream->priv->is_utf8 = TRUE;
Packit a7d494
				break;
Packit a7d494
			}
Packit a7d494
Packit a7d494
			continue;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		conv = g_charset_converter_new ("UTF-8",
Packit a7d494
						gtk_source_encoding_get_charset (enc),
Packit a7d494
						NULL);
Packit a7d494
Packit a7d494
		/* If we tried all encodings we use the first one */
Packit a7d494
		if (stream->priv->use_first)
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* Try to convert */
Packit a7d494
		if (try_convert (conv, inbuf, inbuf_size))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (conv != NULL)
Packit a7d494
	{
Packit a7d494
		g_converter_reset (G_CONVERTER (conv));
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return conv;
Packit a7d494
}
Packit a7d494
Packit a7d494
static GtkSourceNewlineType
Packit a7d494
get_newline_type (GtkTextIter *end)
Packit a7d494
{
Packit a7d494
	GtkSourceNewlineType res;
Packit a7d494
	GtkTextIter copy;
Packit a7d494
	gunichar c;
Packit a7d494
Packit a7d494
	copy = *end;
Packit a7d494
	c = gtk_text_iter_get_char (©);
Packit a7d494
Packit a7d494
	if (g_unichar_break_type (c) == G_UNICODE_BREAK_CARRIAGE_RETURN)
Packit a7d494
	{
Packit a7d494
		if (gtk_text_iter_forward_char (&copy) &&
Packit a7d494
		    g_unichar_break_type (gtk_text_iter_get_char (&copy)) == G_UNICODE_BREAK_LINE_FEED)
Packit a7d494
		{
Packit a7d494
			res = GTK_SOURCE_NEWLINE_TYPE_CR_LF;
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			res = GTK_SOURCE_NEWLINE_TYPE_CR;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		res = GTK_SOURCE_NEWLINE_TYPE_LF;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return res;
Packit a7d494
}
Packit a7d494
Packit a7d494
GtkSourceBufferOutputStream *
Packit a7d494
gtk_source_buffer_output_stream_new (GtkSourceBuffer *buffer,
Packit a7d494
				     GSList          *candidate_encodings,
Packit a7d494
				     gboolean         remove_trailing_newline)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *stream;
Packit a7d494
Packit a7d494
	stream = g_object_new (GTK_SOURCE_TYPE_BUFFER_OUTPUT_STREAM,
Packit a7d494
	                       "buffer", buffer,
Packit a7d494
	                       "remove-trailing-newline", remove_trailing_newline,
Packit a7d494
	                       NULL);
Packit a7d494
Packit a7d494
	stream->priv->encodings = g_slist_copy (candidate_encodings);
Packit a7d494
Packit a7d494
	return stream;
Packit a7d494
}
Packit a7d494
Packit a7d494
GtkSourceNewlineType
Packit a7d494
gtk_source_buffer_output_stream_detect_newline_type (GtkSourceBufferOutputStream *stream)
Packit a7d494
{
Packit a7d494
	GtkSourceNewlineType type;
Packit a7d494
	GtkTextIter iter;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream),
Packit a7d494
			      GTK_SOURCE_NEWLINE_TYPE_DEFAULT);
Packit a7d494
Packit a7d494
	if (stream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return GTK_SOURCE_NEWLINE_TYPE_DEFAULT;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	type = GTK_SOURCE_NEWLINE_TYPE_DEFAULT;
Packit a7d494
Packit a7d494
	gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (stream->priv->source_buffer),
Packit a7d494
					&iter);
Packit a7d494
Packit a7d494
	if (gtk_text_iter_ends_line (&iter) || gtk_text_iter_forward_to_line_end (&iter))
Packit a7d494
	{
Packit a7d494
		type = get_newline_type (&iter);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return type;
Packit a7d494
}
Packit a7d494
Packit a7d494
const GtkSourceEncoding *
Packit a7d494
gtk_source_buffer_output_stream_get_guessed (GtkSourceBufferOutputStream *stream)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream), NULL);
Packit a7d494
Packit a7d494
	if (stream->priv->current_encoding != NULL)
Packit a7d494
	{
Packit a7d494
		return stream->priv->current_encoding->data;
Packit a7d494
	}
Packit a7d494
	else if (stream->priv->is_utf8 || !stream->priv->is_initialized)
Packit a7d494
	{
Packit a7d494
		/* If it is not initialized we assume that we are trying to
Packit a7d494
		 * convert the empty string.
Packit a7d494
		 */
Packit a7d494
		return gtk_source_encoding_get_utf8 ();
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return NULL;
Packit a7d494
}
Packit a7d494
Packit a7d494
guint
Packit a7d494
gtk_source_buffer_output_stream_get_num_fallbacks (GtkSourceBufferOutputStream *stream)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream), 0);
Packit a7d494
Packit a7d494
	return stream->priv->n_fallback_errors;
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
apply_error_tag (GtkSourceBufferOutputStream *stream)
Packit a7d494
{
Packit a7d494
	GtkTextIter start;
Packit a7d494
Packit a7d494
	if (stream->priv->error_offset == -1 ||
Packit a7d494
	    stream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (stream->priv->source_buffer),
Packit a7d494
	                                    &start, stream->priv->error_offset);
Packit a7d494
Packit a7d494
	_gtk_source_buffer_set_as_invalid_character (stream->priv->source_buffer,
Packit a7d494
						     &start,
Packit a7d494
						     &stream->priv->pos);
Packit a7d494
Packit a7d494
	stream->priv->error_offset = -1;
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
insert_fallback (GtkSourceBufferOutputStream *stream,
Packit a7d494
		 const gchar                 *buffer)
Packit a7d494
{
Packit a7d494
	guint8 out[4];
Packit a7d494
	guint8 v;
Packit a7d494
	const gchar hex[] = "0123456789ABCDEF";
Packit a7d494
Packit a7d494
	if (stream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* If we are here it is because we are pointing to an invalid char so we
Packit a7d494
	 * substitute it by an hex value.
Packit a7d494
	 */
Packit a7d494
	v = *(guint8 *)buffer;
Packit a7d494
	out[0] = '\\';
Packit a7d494
	out[1] = hex[(v & 0xf0) >> 4];
Packit a7d494
	out[2] = hex[(v & 0x0f) >> 0];
Packit a7d494
	out[3] = '\0';
Packit a7d494
Packit a7d494
	gtk_text_buffer_insert (GTK_TEXT_BUFFER (stream->priv->source_buffer),
Packit a7d494
	                        &stream->priv->pos, (const gchar *)out, 3);
Packit a7d494
Packit a7d494
	++stream->priv->n_fallback_errors;
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
validate_and_insert (GtkSourceBufferOutputStream *stream,
Packit a7d494
		     gchar                       *buffer,
Packit a7d494
		     gsize                        count,
Packit a7d494
		     gboolean                     owned)
Packit a7d494
{
Packit a7d494
	GtkTextBuffer *text_buffer;
Packit a7d494
	GtkTextIter *iter;
Packit a7d494
	gsize len;
Packit a7d494
	gchar *free_text = NULL;
Packit a7d494
Packit a7d494
	if (stream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	text_buffer = GTK_TEXT_BUFFER (stream->priv->source_buffer);
Packit a7d494
	iter = &stream->priv->pos;
Packit a7d494
	len = count;
Packit a7d494
Packit a7d494
	while (len != 0)
Packit a7d494
	{
Packit a7d494
		const gchar *end;
Packit a7d494
		gboolean valid;
Packit a7d494
		gsize nvalid;
Packit a7d494
Packit a7d494
		/* validate */
Packit a7d494
		valid = g_utf8_validate (buffer, len, &end;;
Packit a7d494
		nvalid = end - buffer;
Packit a7d494
Packit a7d494
		/* Note: this is a workaround for a 'bug' in GtkTextBuffer where
Packit a7d494
		   inserting first a \r and then in a second insert, a \n,
Packit a7d494
		   will result in two lines being added instead of a single
Packit a7d494
		   one */
Packit a7d494
Packit a7d494
		if (valid)
Packit a7d494
		{
Packit a7d494
			gchar *ptr;
Packit a7d494
Packit a7d494
			ptr = g_utf8_find_prev_char (buffer, buffer + len);
Packit a7d494
Packit a7d494
			if (ptr && *ptr == '\r' && ptr - buffer == (glong)len - 1)
Packit a7d494
			{
Packit a7d494
				stream->priv->buffer = g_new (gchar, 2);
Packit a7d494
				stream->priv->buffer[0] = '\r';
Packit a7d494
				stream->priv->buffer[1] = '\0';
Packit a7d494
				stream->priv->buflen = 1;
Packit a7d494
Packit a7d494
				/* Decrease also the len so in the check
Packit a7d494
				   nvalid == len we get out of this method */
Packit a7d494
				--nvalid;
Packit a7d494
				--len;
Packit a7d494
			}
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* if we've got any valid char we must tag the invalid chars */
Packit a7d494
		if (nvalid > 0)
Packit a7d494
		{
Packit a7d494
			gchar orig_non_null = '\0';
Packit a7d494
Packit a7d494
			apply_error_tag (stream);
Packit a7d494
Packit a7d494
			if ((nvalid != len || !owned) && buffer[nvalid] != '\0')
Packit a7d494
			{
Packit a7d494
				/* make sure the buffer is always properly null
Packit a7d494
				 * terminated. This is needed, at least for now,
Packit a7d494
				 * to avoid issues with pygobject marshalling of
Packit a7d494
				 * the insert-text signal of gtktextbuffer
Packit a7d494
				 *
Packit a7d494
				 * https://bugzilla.gnome.org/show_bug.cgi?id=726689
Packit a7d494
				 */
Packit a7d494
				if (!owned)
Packit a7d494
				{
Packit a7d494
					/* forced to make a copy */
Packit a7d494
					free_text = g_new (gchar, len + 1);
Packit a7d494
					memcpy (free_text, buffer, len);
Packit a7d494
					free_text[len] = '\0';
Packit a7d494
Packit a7d494
					buffer = free_text;
Packit a7d494
					owned = TRUE;
Packit a7d494
				}
Packit a7d494
Packit a7d494
				orig_non_null = buffer[nvalid];
Packit a7d494
				buffer[nvalid] = '\0';
Packit a7d494
			}
Packit a7d494
Packit a7d494
			gtk_text_buffer_insert (text_buffer, iter, buffer, nvalid);
Packit a7d494
Packit a7d494
			if (orig_non_null != '\0')
Packit a7d494
			{
Packit a7d494
				/* restore null terminated replaced byte */
Packit a7d494
				buffer[nvalid] = orig_non_null;
Packit a7d494
			}
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* If we inserted all return */
Packit a7d494
		if (nvalid == len)
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		buffer += nvalid;
Packit a7d494
		len = len - nvalid;
Packit a7d494
Packit a7d494
		if ((len < MAX_UNICHAR_LEN) &&
Packit a7d494
		    (g_utf8_get_char_validated (buffer, len) == (gunichar)-2))
Packit a7d494
		{
Packit a7d494
			stream->priv->buffer = g_strndup (end, len);
Packit a7d494
			stream->priv->buflen = len;
Packit a7d494
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* we need the start of the chunk of invalid chars */
Packit a7d494
		if (stream->priv->error_offset == -1)
Packit a7d494
		{
Packit a7d494
			stream->priv->error_offset = gtk_text_iter_get_offset (&stream->priv->pos);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		insert_fallback (stream, buffer);
Packit a7d494
		++buffer;
Packit a7d494
		--len;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	g_free (free_text);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
remove_trailing_newline (GtkSourceBufferOutputStream *stream)
Packit a7d494
{
Packit a7d494
	GtkTextIter end;
Packit a7d494
	GtkTextIter start;
Packit a7d494
Packit a7d494
	if (stream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (stream->priv->source_buffer), &end;;
Packit a7d494
	start = end;
Packit a7d494
Packit a7d494
	gtk_text_iter_set_line_offset (&start, 0);
Packit a7d494
Packit a7d494
	if (gtk_text_iter_ends_line (&start) &&
Packit a7d494
	    gtk_text_iter_backward_line (&start))
Packit a7d494
	{
Packit a7d494
		if (!gtk_text_iter_ends_line (&start))
Packit a7d494
		{
Packit a7d494
			gtk_text_iter_forward_to_line_end (&start;;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		gtk_text_buffer_delete (GTK_TEXT_BUFFER (stream->priv->source_buffer),
Packit a7d494
		                        &start,
Packit a7d494
		                        &end;;
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
end_append_text_to_document (GtkSourceBufferOutputStream *stream)
Packit a7d494
{
Packit a7d494
	if (stream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (stream->priv->remove_trailing_newline)
Packit a7d494
	{
Packit a7d494
		remove_trailing_newline (stream);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (stream->priv->source_buffer),
Packit a7d494
	                              FALSE);
Packit a7d494
Packit a7d494
	gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (stream->priv->source_buffer));
Packit a7d494
	gtk_source_buffer_end_not_undoable_action (stream->priv->source_buffer);
Packit a7d494
}
Packit a7d494
Packit a7d494
static gboolean
Packit a7d494
convert_text (GtkSourceBufferOutputStream  *stream,
Packit a7d494
	      const gchar                  *inbuf,
Packit a7d494
	      gsize                         inbuf_len,
Packit a7d494
	      gchar                       **outbuf,
Packit a7d494
	      gsize                        *outbuf_len,
Packit a7d494
	      GError                      **error)
Packit a7d494
{
Packit a7d494
	gchar *out, *dest;
Packit a7d494
	gsize in_left, out_left, outbuf_size, res;
Packit a7d494
	gint errsv;
Packit a7d494
	gboolean done, have_error;
Packit a7d494
Packit a7d494
	in_left = inbuf_len;
Packit a7d494
	/* set an arbitrary length if inbuf_len is 0, this is needed to flush
Packit a7d494
	   the iconv data */
Packit a7d494
	outbuf_size = (inbuf_len > 0) ? inbuf_len : 100;
Packit a7d494
Packit a7d494
	out_left = outbuf_size;
Packit a7d494
Packit a7d494
	/* keep room for null termination */
Packit a7d494
	out = dest = g_malloc (sizeof (gchar) * (outbuf_size + 1));
Packit a7d494
Packit a7d494
	done = FALSE;
Packit a7d494
	have_error = FALSE;
Packit a7d494
Packit a7d494
	while (!done && !have_error)
Packit a7d494
	{
Packit a7d494
		/* If we reached here is because we need to convert the text,
Packit a7d494
		   so we convert it using iconv.
Packit a7d494
		   See that if inbuf is NULL the data will be flushed */
Packit a7d494
		res = g_iconv (stream->priv->iconv,
Packit a7d494
		               (gchar **)&inbuf, &in_left,
Packit a7d494
		               &out, &out_left);
Packit a7d494
Packit a7d494
		/* something went wrong */
Packit a7d494
		if (res == (gsize)-1)
Packit a7d494
		{
Packit a7d494
			errsv = errno;
Packit a7d494
Packit a7d494
			switch (errsv)
Packit a7d494
			{
Packit a7d494
				case EINVAL:
Packit a7d494
					/* Incomplete text, do not report an error */
Packit a7d494
					stream->priv->iconv_buffer = g_strndup (inbuf, in_left);
Packit a7d494
					stream->priv->iconv_buflen = in_left;
Packit a7d494
					done = TRUE;
Packit a7d494
					break;
Packit a7d494
Packit a7d494
				case E2BIG:
Packit a7d494
					{
Packit a7d494
						/* allocate more space */
Packit a7d494
						gsize used = out - dest;
Packit a7d494
Packit a7d494
						outbuf_size *= 2;
Packit a7d494
Packit a7d494
						/* make sure to allocate room for
Packit a7d494
						   terminating null byte */
Packit a7d494
						dest = g_realloc (dest, outbuf_size + 1);
Packit a7d494
Packit a7d494
						out = dest + used;
Packit a7d494
						out_left = outbuf_size - used;
Packit a7d494
					}
Packit a7d494
					break;
Packit a7d494
Packit a7d494
				case EILSEQ:
Packit a7d494
					g_set_error_literal (error, G_CONVERT_ERROR,
Packit a7d494
					                     G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
Packit a7d494
					                     _("Invalid byte sequence in conversion input"));
Packit a7d494
					have_error = TRUE;
Packit a7d494
					break;
Packit a7d494
Packit a7d494
				default:
Packit a7d494
					g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
Packit a7d494
					             _("Error during conversion: %s"),
Packit a7d494
					             g_strerror (errsv));
Packit a7d494
					have_error = TRUE;
Packit a7d494
					break;
Packit a7d494
			}
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			done = TRUE;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (have_error)
Packit a7d494
	{
Packit a7d494
		g_free (dest);
Packit a7d494
		*outbuf = NULL;
Packit a7d494
		*outbuf_len = 0;
Packit a7d494
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	*outbuf_len = out - dest;
Packit a7d494
	dest[*outbuf_len] = '\0';
Packit a7d494
Packit a7d494
	*outbuf = dest;
Packit a7d494
	return TRUE;
Packit a7d494
}
Packit a7d494
Packit a7d494
static gssize
Packit a7d494
gtk_source_buffer_output_stream_write (GOutputStream  *stream,
Packit a7d494
				       const void     *buffer,
Packit a7d494
				       gsize           count,
Packit a7d494
				       GCancellable   *cancellable,
Packit a7d494
				       GError        **error)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *ostream;
Packit a7d494
	gchar *text;
Packit a7d494
	gsize len;
Packit a7d494
	gboolean freetext = FALSE;
Packit a7d494
Packit a7d494
	ostream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (stream);
Packit a7d494
Packit a7d494
	if (g_cancellable_set_error_if_cancelled (cancellable, error) ||
Packit a7d494
	    ostream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return -1;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (!ostream->priv->is_initialized)
Packit a7d494
	{
Packit a7d494
		ostream->priv->charset_conv = guess_encoding (ostream, buffer, count);
Packit a7d494
Packit a7d494
		/* If we still have the previous case is that we didn't guess
Packit a7d494
		   anything */
Packit a7d494
		if (ostream->priv->charset_conv == NULL &&
Packit a7d494
		    !ostream->priv->is_utf8)
Packit a7d494
		{
Packit a7d494
			g_set_error_literal (error, GTK_SOURCE_FILE_LOADER_ERROR,
Packit a7d494
			                     GTK_SOURCE_FILE_LOADER_ERROR_ENCODING_AUTO_DETECTION_FAILED,
Packit a7d494
			                     "It is not possible to detect the encoding automatically");
Packit a7d494
Packit a7d494
			return -1;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* Do not initialize iconv if we are not going to convert anything */
Packit a7d494
		if (!ostream->priv->is_utf8)
Packit a7d494
		{
Packit a7d494
			gchar *from_charset;
Packit a7d494
Packit a7d494
			/* Initialize iconv */
Packit a7d494
			g_object_get (G_OBJECT (ostream->priv->charset_conv),
Packit a7d494
				      "from-charset", &from_charset,
Packit a7d494
				      NULL);
Packit a7d494
Packit a7d494
			ostream->priv->iconv = g_iconv_open ("UTF-8", from_charset);
Packit a7d494
Packit a7d494
			if (ostream->priv->iconv == (GIConv)-1)
Packit a7d494
			{
Packit a7d494
				if (errno == EINVAL)
Packit a7d494
				{
Packit a7d494
					g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
Packit a7d494
						     _("Conversion from character set “%s” to “UTF-8” is not supported"),
Packit a7d494
						     from_charset);
Packit a7d494
				}
Packit a7d494
				else
Packit a7d494
				{
Packit a7d494
					g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
Packit a7d494
						     _("Could not open converter from “%s” to “UTF-8”"),
Packit a7d494
						     from_charset);
Packit a7d494
				}
Packit a7d494
Packit a7d494
				g_free (from_charset);
Packit a7d494
				g_clear_object (&ostream->priv->charset_conv);
Packit a7d494
Packit a7d494
				return -1;
Packit a7d494
			}
Packit a7d494
Packit a7d494
			g_free (from_charset);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* Begin not undoable action. Begin also a normal user action,
Packit a7d494
		 * since we load the file chunk by chunk and it should be seen
Packit a7d494
		 * as only one action, for the features that rely on the user
Packit a7d494
		 * action.
Packit a7d494
		 */
Packit a7d494
		gtk_source_buffer_begin_not_undoable_action (ostream->priv->source_buffer);
Packit a7d494
		gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (ostream->priv->source_buffer));
Packit a7d494
Packit a7d494
		gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (ostream->priv->source_buffer),
Packit a7d494
		                                &ostream->priv->pos);
Packit a7d494
Packit a7d494
		ostream->priv->is_initialized = TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (ostream->priv->buflen > 0)
Packit a7d494
	{
Packit a7d494
		len = ostream->priv->buflen + count;
Packit a7d494
		text = g_malloc (len + 1);
Packit a7d494
Packit a7d494
		memcpy (text, ostream->priv->buffer, ostream->priv->buflen);
Packit a7d494
		memcpy (text + ostream->priv->buflen, buffer, count);
Packit a7d494
Packit a7d494
		text[len] = '\0';
Packit a7d494
Packit a7d494
		g_free (ostream->priv->buffer);
Packit a7d494
Packit a7d494
		ostream->priv->buffer = NULL;
Packit a7d494
		ostream->priv->buflen = 0;
Packit a7d494
Packit a7d494
		freetext = TRUE;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		text = (gchar *) buffer;
Packit a7d494
		len = count;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (!ostream->priv->is_utf8)
Packit a7d494
	{
Packit a7d494
		gchar *outbuf;
Packit a7d494
		gsize outbuf_len;
Packit a7d494
Packit a7d494
		/* check if iconv was correctly initializated, this shouldn't
Packit a7d494
		   happen but better be safe */
Packit a7d494
		if (ostream->priv->iconv == NULL)
Packit a7d494
		{
Packit a7d494
			g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
Packit a7d494
			                     _("Invalid object, not initialized"));
Packit a7d494
Packit a7d494
			if (freetext)
Packit a7d494
			{
Packit a7d494
				g_free (text);
Packit a7d494
			}
Packit a7d494
Packit a7d494
			return -1;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* manage the previous conversion buffer */
Packit a7d494
		if (ostream->priv->iconv_buflen > 0)
Packit a7d494
		{
Packit a7d494
			gchar *text2;
Packit a7d494
			gsize len2;
Packit a7d494
Packit a7d494
			len2 = len + ostream->priv->iconv_buflen;
Packit a7d494
			text2 = g_malloc (len2 + 1);
Packit a7d494
Packit a7d494
			memcpy (text2, ostream->priv->iconv_buffer, ostream->priv->iconv_buflen);
Packit a7d494
			memcpy (text2 + ostream->priv->iconv_buflen, text, len);
Packit a7d494
Packit a7d494
			text2[len2] = '\0';
Packit a7d494
Packit a7d494
			if (freetext)
Packit a7d494
			{
Packit a7d494
				g_free (text);
Packit a7d494
			}
Packit a7d494
Packit a7d494
			text = text2;
Packit a7d494
			len = len2;
Packit a7d494
Packit a7d494
			g_free (ostream->priv->iconv_buffer);
Packit a7d494
Packit a7d494
			ostream->priv->iconv_buffer = NULL;
Packit a7d494
			ostream->priv->iconv_buflen = 0;
Packit a7d494
Packit a7d494
			freetext = TRUE;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		if (!convert_text (ostream, text, len, &outbuf, &outbuf_len, error))
Packit a7d494
		{
Packit a7d494
			if (freetext)
Packit a7d494
			{
Packit a7d494
				g_free (text);
Packit a7d494
			}
Packit a7d494
Packit a7d494
			return -1;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		if (freetext)
Packit a7d494
		{
Packit a7d494
			g_free (text);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* set the converted text as the text to validate */
Packit a7d494
		text = outbuf;
Packit a7d494
		len = outbuf_len;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	validate_and_insert (ostream, text, len, freetext);
Packit a7d494
Packit a7d494
	if (freetext)
Packit a7d494
	{
Packit a7d494
		g_free (text);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return count;
Packit a7d494
}
Packit a7d494
Packit a7d494
static gboolean
Packit a7d494
gtk_source_buffer_output_stream_flush (GOutputStream  *stream,
Packit a7d494
				       GCancellable   *cancellable,
Packit a7d494
				       GError        **error)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *ostream;
Packit a7d494
Packit a7d494
	ostream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (stream);
Packit a7d494
Packit a7d494
	if (ostream->priv->is_closed ||
Packit a7d494
	    ostream->priv->source_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* if we have converted something flush residual data, validate and insert */
Packit a7d494
	if (ostream->priv->iconv != NULL)
Packit a7d494
	{
Packit a7d494
		gchar *outbuf;
Packit a7d494
		gsize outbuf_len;
Packit a7d494
Packit a7d494
		if (convert_text (ostream, NULL, 0, &outbuf, &outbuf_len, error))
Packit a7d494
		{
Packit a7d494
			validate_and_insert (ostream, outbuf, outbuf_len, TRUE);
Packit a7d494
			g_free (outbuf);
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			return FALSE;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (ostream->priv->buflen > 0 && *ostream->priv->buffer != '\r')
Packit a7d494
	{
Packit a7d494
		/* If we reached here is because the last insertion was a half
Packit a7d494
		   correct char, which has to be inserted as fallback */
Packit a7d494
		gchar *text;
Packit a7d494
Packit a7d494
		if (ostream->priv->error_offset == -1)
Packit a7d494
		{
Packit a7d494
			ostream->priv->error_offset = gtk_text_iter_get_offset (&ostream->priv->pos);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		text = ostream->priv->buffer;
Packit a7d494
		while (ostream->priv->buflen != 0)
Packit a7d494
		{
Packit a7d494
			insert_fallback (ostream, text);
Packit a7d494
			++text;
Packit a7d494
			--ostream->priv->buflen;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		g_free (ostream->priv->buffer);
Packit a7d494
		ostream->priv->buffer = NULL;
Packit a7d494
	}
Packit a7d494
	else if (ostream->priv->buflen == 1 && *ostream->priv->buffer == '\r')
Packit a7d494
	{
Packit a7d494
		/* The previous chars can be invalid */
Packit a7d494
		apply_error_tag (ostream);
Packit a7d494
Packit a7d494
		/* See special case above, flush this */
Packit a7d494
		gtk_text_buffer_insert (GTK_TEXT_BUFFER (ostream->priv->source_buffer),
Packit a7d494
		                        &ostream->priv->pos,
Packit a7d494
		                        "\r",
Packit a7d494
		                        1);
Packit a7d494
Packit a7d494
		g_free (ostream->priv->buffer);
Packit a7d494
		ostream->priv->buffer = NULL;
Packit a7d494
		ostream->priv->buflen = 0;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (ostream->priv->iconv_buflen > 0 )
Packit a7d494
	{
Packit a7d494
		/* If we reached here is because the last insertion was a half
Packit a7d494
		   correct char, which has to be inserted as fallback */
Packit a7d494
		gchar *text;
Packit a7d494
Packit a7d494
		if (ostream->priv->error_offset == -1)
Packit a7d494
		{
Packit a7d494
			ostream->priv->error_offset = gtk_text_iter_get_offset (&ostream->priv->pos);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		text = ostream->priv->iconv_buffer;
Packit a7d494
		while (ostream->priv->iconv_buflen != 0)
Packit a7d494
		{
Packit a7d494
			insert_fallback (ostream, text);
Packit a7d494
			++text;
Packit a7d494
			--ostream->priv->iconv_buflen;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		g_free (ostream->priv->iconv_buffer);
Packit a7d494
		ostream->priv->iconv_buffer = NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	apply_error_tag (ostream);
Packit a7d494
Packit a7d494
	return TRUE;
Packit a7d494
}
Packit a7d494
Packit a7d494
static gboolean
Packit a7d494
gtk_source_buffer_output_stream_close (GOutputStream  *stream,
Packit a7d494
				       GCancellable   *cancellable,
Packit a7d494
				       GError        **error)
Packit a7d494
{
Packit a7d494
	GtkSourceBufferOutputStream *ostream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (stream);
Packit a7d494
Packit a7d494
	if (!ostream->priv->is_closed && ostream->priv->is_initialized)
Packit a7d494
	{
Packit a7d494
		end_append_text_to_document (ostream);
Packit a7d494
Packit a7d494
		if (ostream->priv->iconv != NULL)
Packit a7d494
		{
Packit a7d494
			g_iconv_close (ostream->priv->iconv);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		ostream->priv->is_closed = TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (ostream->priv->buflen > 0 || ostream->priv->iconv_buflen > 0)
Packit a7d494
	{
Packit a7d494
		g_set_error (error,
Packit a7d494
		             G_IO_ERROR,
Packit a7d494
		             G_IO_ERROR_INVALID_DATA,
Packit a7d494
		             _("Incomplete UTF-8 sequence in input"));
Packit a7d494
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return TRUE;
Packit a7d494
}