| File: | _build/../ctksourceview/ctksourcebufferoutputstream.c |
| Warning: | line 283, column 9 Access to field 'data' results in a dereference of a null pointer (loaded from field 'current_encoding') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */ | |||
| 2 | /* | |||
| 3 | * This file is part of CtkSourceView | |||
| 4 | * | |||
| 5 | * Copyright (C) 2010 - Ignacio Casal Quinteiro | |||
| 6 | * Copyright (C) 2014 - Sébastien Wilmet <swilmet@gnome.org> | |||
| 7 | * | |||
| 8 | * CtkSourceView is free software; you can redistribute it and/or | |||
| 9 | * modify it under the terms of the GNU Lesser General Public | |||
| 10 | * License as published by the Free Software Foundation; either | |||
| 11 | * version 2.1 of the License, or (at your option) any later version. | |||
| 12 | * | |||
| 13 | * CtkSourceView is distributed in the hope that it will be useful, | |||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| 16 | * Lesser General Public License for more details. | |||
| 17 | * | |||
| 18 | * You should have received a copy of the GNU Lesser General Public License | |||
| 19 | * along with this library; if not, see <http://www.gnu.org/licenses/>. | |||
| 20 | */ | |||
| 21 | ||||
| 22 | #ifdef HAVE_CONFIG_H1 | |||
| 23 | #include <config.h> | |||
| 24 | #endif | |||
| 25 | ||||
| 26 | #include "ctksourcebufferoutputstream.h" | |||
| 27 | #include <string.h> | |||
| 28 | #include <errno(*__errno_location ()).h> | |||
| 29 | #include <glib/gi18n-lib.h> | |||
| 30 | #include "ctksourcebuffer.h" | |||
| 31 | #include "ctksourcebuffer-private.h" | |||
| 32 | #include "ctksourceencoding.h" | |||
| 33 | #include "ctksourcefileloader.h" | |||
| 34 | ||||
| 35 | /* NOTE: never use async methods on this stream, the stream is just | |||
| 36 | * a wrapper around CtkTextBuffer api so that we can use GIO Stream | |||
| 37 | * methods, but the underlying code operates on a CtkTextBuffer, so | |||
| 38 | * there is no I/O involved and should be accessed only by the main | |||
| 39 | * thread. | |||
| 40 | */ | |||
| 41 | ||||
| 42 | /* NOTE2: welcome to a really big headache. At the beginning this was | |||
| 43 | * split in several classes, one for encoding detection, another | |||
| 44 | * for UTF-8 conversion and another for validation. The reason this is | |||
| 45 | * all together is because we need specific information from all parts | |||
| 46 | * in other to be able to mark characters as invalid if there was some | |||
| 47 | * specific problem on the conversion. | |||
| 48 | */ | |||
| 49 | ||||
| 50 | /* The code comes from gedit, the class was GeditDocumentOutputStream. */ | |||
| 51 | ||||
| 52 | #if 0 | |||
| 53 | #define DEBUG(x) (x) | |||
| 54 | #else | |||
| 55 | #define DEBUG(x) | |||
| 56 | #endif | |||
| 57 | ||||
| 58 | #define MAX_UNICHAR_LEN6 6 | |||
| 59 | ||||
| 60 | struct _CtkSourceBufferOutputStreamPrivate | |||
| 61 | { | |||
| 62 | CtkSourceBuffer *source_buffer; | |||
| 63 | CtkTextIter pos; | |||
| 64 | ||||
| 65 | gchar *buffer; | |||
| 66 | gsize buflen; | |||
| 67 | ||||
| 68 | gchar *iconv_buffer; | |||
| 69 | gsize iconv_buflen; | |||
| 70 | ||||
| 71 | /* Encoding detection */ | |||
| 72 | GIConv iconv; | |||
| 73 | GCharsetConverter *charset_conv; | |||
| 74 | ||||
| 75 | GSList *encodings; | |||
| 76 | GSList *current_encoding; | |||
| 77 | ||||
| 78 | gint error_offset; | |||
| 79 | guint n_fallback_errors; | |||
| 80 | ||||
| 81 | guint is_utf8 : 1; | |||
| 82 | guint use_first : 1; | |||
| 83 | ||||
| 84 | guint is_initialized : 1; | |||
| 85 | guint is_closed : 1; | |||
| 86 | ||||
| 87 | guint remove_trailing_newline : 1; | |||
| 88 | }; | |||
| 89 | ||||
| 90 | enum | |||
| 91 | { | |||
| 92 | PROP_0, | |||
| 93 | PROP_BUFFER, | |||
| 94 | PROP_REMOVE_TRAILING_NEWLINE | |||
| 95 | }; | |||
| 96 | ||||
| 97 | G_DEFINE_TYPE_WITH_PRIVATE (CtkSourceBufferOutputStream, ctk_source_buffer_output_stream, G_TYPE_OUTPUT_STREAM)static void ctk_source_buffer_output_stream_init (CtkSourceBufferOutputStream *self); static void ctk_source_buffer_output_stream_class_init (CtkSourceBufferOutputStreamClass *klass); static GType ctk_source_buffer_output_stream_get_type_once (void); static gpointer ctk_source_buffer_output_stream_parent_class = ((void*)0); static gint CtkSourceBufferOutputStream_private_offset ; static void ctk_source_buffer_output_stream_class_intern_init (gpointer klass) { ctk_source_buffer_output_stream_parent_class = g_type_class_peek_parent (klass); if (CtkSourceBufferOutputStream_private_offset != 0) g_type_class_adjust_private_offset (klass, &CtkSourceBufferOutputStream_private_offset ); ctk_source_buffer_output_stream_class_init ((CtkSourceBufferOutputStreamClass *) klass); } __attribute__ ((__unused__)) static inline gpointer ctk_source_buffer_output_stream_get_instance_private (CtkSourceBufferOutputStream *self) { return (((gpointer) ((guint8*) (self) + (glong) (CtkSourceBufferOutputStream_private_offset )))); } GType ctk_source_buffer_output_stream_get_type (void) { static GType static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); (void) ( 0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0)) ; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id ) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (*(&static_g_define_type_id)) gapg_temp_newval; __typeof__ ((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id ); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5) ; gapg_temp_newval; })) && g_once_init_enter_pointer ( &static_g_define_type_id)); })) ) { GType g_define_type_id = ctk_source_buffer_output_stream_get_type_once (); (__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); 0 ? (void ) (*(&static_g_define_type_id) = (g_define_type_id)) : (void ) 0; g_once_init_leave_pointer ((&static_g_define_type_id ), (gpointer) (guintptr) (g_define_type_id)); })) ; } return static_g_define_type_id ; } __attribute__ ((__noinline__)) static GType ctk_source_buffer_output_stream_get_type_once (void) { GType g_define_type_id = g_type_register_static_simple ((g_output_stream_get_type ()), g_intern_static_string ("CtkSourceBufferOutputStream" ), sizeof (CtkSourceBufferOutputStreamClass), (GClassInitFunc )(void (*)(void)) ctk_source_buffer_output_stream_class_intern_init , sizeof (CtkSourceBufferOutputStream), (GInstanceInitFunc)(void (*)(void)) ctk_source_buffer_output_stream_init, (GTypeFlags ) 0); { {{ CtkSourceBufferOutputStream_private_offset = g_type_add_instance_private (g_define_type_id, sizeof (CtkSourceBufferOutputStreamPrivate )); };} } return g_define_type_id; } | |||
| 98 | ||||
| 99 | static gssize ctk_source_buffer_output_stream_write (GOutputStream *stream, | |||
| 100 | const void *buffer, | |||
| 101 | gsize count, | |||
| 102 | GCancellable *cancellable, | |||
| 103 | GError **error); | |||
| 104 | ||||
| 105 | static gboolean ctk_source_buffer_output_stream_close (GOutputStream *stream, | |||
| 106 | GCancellable *cancellable, | |||
| 107 | GError **error); | |||
| 108 | ||||
| 109 | static gboolean ctk_source_buffer_output_stream_flush (GOutputStream *stream, | |||
| 110 | GCancellable *cancellable, | |||
| 111 | GError **error); | |||
| 112 | ||||
| 113 | static void | |||
| 114 | ctk_source_buffer_output_stream_set_property (GObject *object, | |||
| 115 | guint prop_id, | |||
| 116 | const GValue *value, | |||
| 117 | GParamSpec *pspec) | |||
| 118 | { | |||
| 119 | CtkSourceBufferOutputStream *stream = CTK_SOURCE_BUFFER_OUTPUT_STREAM (object)((((CtkSourceBufferOutputStream*) (void *) ((object))))); | |||
| 120 | ||||
| 121 | switch (prop_id) | |||
| 122 | { | |||
| 123 | case PROP_BUFFER: | |||
| 124 | g_assert (stream->priv->source_buffer == NULL)do { if (__builtin_expect (__extension__ ({ int _g_boolean_var_14 = 0; if (stream->priv->source_buffer == ((void*)0)) _g_boolean_var_14 = 1; _g_boolean_var_14; }), 1)) ; else g_assertion_message_expr ("CtkSourceView", "../ctksourceview/ctksourcebufferoutputstream.c" , 124, ((const char*) (__func__)), "stream->priv->source_buffer == NULL" ); } while (0); | |||
| 125 | stream->priv->source_buffer = g_value_dup_object (value); | |||
| 126 | break; | |||
| 127 | ||||
| 128 | case PROP_REMOVE_TRAILING_NEWLINE: | |||
| 129 | stream->priv->remove_trailing_newline = g_value_get_boolean (value); | |||
| 130 | break; | |||
| 131 | ||||
| 132 | default: | |||
| 133 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec *_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id = ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'" , "../ctksourceview/ctksourcebufferoutputstream.c", 133, ("property" ), _glib__property_id, _glib__pspec->name, g_type_name ((( (((GTypeClass*) (((GTypeInstance*) (_glib__pspec))->g_class ))->g_type)))), (g_type_name ((((((GTypeClass*) (((GTypeInstance *) (_glib__object))->g_class))->g_type)))))); } while ( 0); | |||
| 134 | break; | |||
| 135 | } | |||
| 136 | } | |||
| 137 | ||||
| 138 | static void | |||
| 139 | ctk_source_buffer_output_stream_get_property (GObject *object, | |||
| 140 | guint prop_id, | |||
| 141 | GValue *value, | |||
| 142 | GParamSpec *pspec) | |||
| 143 | { | |||
| 144 | CtkSourceBufferOutputStream *stream = CTK_SOURCE_BUFFER_OUTPUT_STREAM (object)((((CtkSourceBufferOutputStream*) (void *) ((object))))); | |||
| 145 | ||||
| 146 | switch (prop_id) | |||
| 147 | { | |||
| 148 | case PROP_BUFFER: | |||
| 149 | g_value_set_object (value, stream->priv->source_buffer); | |||
| 150 | break; | |||
| 151 | ||||
| 152 | case PROP_REMOVE_TRAILING_NEWLINE: | |||
| 153 | g_value_set_boolean (value, stream->priv->remove_trailing_newline); | |||
| 154 | break; | |||
| 155 | ||||
| 156 | default: | |||
| 157 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec *_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id = ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'" , "../ctksourceview/ctksourcebufferoutputstream.c", 157, ("property" ), _glib__property_id, _glib__pspec->name, g_type_name ((( (((GTypeClass*) (((GTypeInstance*) (_glib__pspec))->g_class ))->g_type)))), (g_type_name ((((((GTypeClass*) (((GTypeInstance *) (_glib__object))->g_class))->g_type)))))); } while ( 0); | |||
| 158 | break; | |||
| 159 | } | |||
| 160 | } | |||
| 161 | ||||
| 162 | static void | |||
| 163 | ctk_source_buffer_output_stream_dispose (GObject *object) | |||
| 164 | { | |||
| 165 | CtkSourceBufferOutputStream *stream = CTK_SOURCE_BUFFER_OUTPUT_STREAM (object)((((CtkSourceBufferOutputStream*) (void *) ((object))))); | |||
| 166 | ||||
| 167 | g_clear_object (&stream->priv->source_buffer)do { _Static_assert (sizeof *((&stream->priv->source_buffer )) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (((&stream->priv->source_buffer))) _pp = ((&stream ->priv->source_buffer)); __typeof__ (*((&stream-> priv->source_buffer))) _ptr = *_pp; *_pp = ((void*)0); if ( _ptr) (g_object_unref) (_ptr); } while (0); | |||
| 168 | g_clear_object (&stream->priv->charset_conv)do { _Static_assert (sizeof *((&stream->priv->charset_conv )) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (((&stream->priv->charset_conv))) _pp = ((&stream ->priv->charset_conv)); __typeof__ (*((&stream-> priv->charset_conv))) _ptr = *_pp; *_pp = ((void*)0); if ( _ptr) (g_object_unref) (_ptr); } while (0); | |||
| 169 | ||||
| 170 | G_OBJECT_CLASS (ctk_source_buffer_output_stream_parent_class)((((GObjectClass*) (void *) ((ctk_source_buffer_output_stream_parent_class )))))->dispose (object); | |||
| 171 | } | |||
| 172 | ||||
| 173 | static void | |||
| 174 | ctk_source_buffer_output_stream_finalize (GObject *object) | |||
| 175 | { | |||
| 176 | CtkSourceBufferOutputStream *stream = CTK_SOURCE_BUFFER_OUTPUT_STREAM (object)((((CtkSourceBufferOutputStream*) (void *) ((object))))); | |||
| 177 | ||||
| 178 | g_free (stream->priv->buffer); | |||
| 179 | g_free (stream->priv->iconv_buffer); | |||
| 180 | g_slist_free (stream->priv->encodings); | |||
| 181 | ||||
| 182 | G_OBJECT_CLASS (ctk_source_buffer_output_stream_parent_class)((((GObjectClass*) (void *) ((ctk_source_buffer_output_stream_parent_class )))))->finalize (object); | |||
| 183 | } | |||
| 184 | ||||
| 185 | static void | |||
| 186 | ctk_source_buffer_output_stream_constructed (GObject *object) | |||
| 187 | { | |||
| 188 | CtkSourceBufferOutputStream *stream = CTK_SOURCE_BUFFER_OUTPUT_STREAM (object)((((CtkSourceBufferOutputStream*) (void *) ((object))))); | |||
| 189 | ||||
| 190 | if (stream->priv->source_buffer == NULL((void*)0)) | |||
| 191 | { | |||
| 192 | g_critical ("This should never happen, a problem happened constructing the Buffer Output Stream!"); | |||
| 193 | return; | |||
| 194 | } | |||
| 195 | ||||
| 196 | ctk_source_buffer_begin_not_undoable_action (stream->priv->source_buffer); | |||
| 197 | ||||
| 198 | ctk_text_buffer_set_text (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))), "", 0); | |||
| 199 | ctk_text_buffer_set_modified (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))), FALSE(0)); | |||
| 200 | ||||
| 201 | ctk_source_buffer_end_not_undoable_action (stream->priv->source_buffer); | |||
| 202 | ||||
| 203 | G_OBJECT_CLASS (ctk_source_buffer_output_stream_parent_class)((((GObjectClass*) (void *) ((ctk_source_buffer_output_stream_parent_class )))))->constructed (object); | |||
| 204 | } | |||
| 205 | ||||
| 206 | static void | |||
| 207 | ctk_source_buffer_output_stream_class_init (CtkSourceBufferOutputStreamClass *klass) | |||
| 208 | { | |||
| 209 | GObjectClass *object_class = G_OBJECT_CLASS (klass)((((GObjectClass*) (void *) ((klass))))); | |||
| 210 | GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass)((((GOutputStreamClass*) (void *) ((klass))))); | |||
| 211 | ||||
| 212 | object_class->get_property = ctk_source_buffer_output_stream_get_property; | |||
| 213 | object_class->set_property = ctk_source_buffer_output_stream_set_property; | |||
| 214 | object_class->dispose = ctk_source_buffer_output_stream_dispose; | |||
| 215 | object_class->finalize = ctk_source_buffer_output_stream_finalize; | |||
| 216 | object_class->constructed = ctk_source_buffer_output_stream_constructed; | |||
| 217 | ||||
| 218 | stream_class->write_fn = ctk_source_buffer_output_stream_write; | |||
| 219 | stream_class->close_fn = ctk_source_buffer_output_stream_close; | |||
| 220 | stream_class->flush = ctk_source_buffer_output_stream_flush; | |||
| 221 | ||||
| 222 | g_object_class_install_property (object_class, | |||
| 223 | PROP_BUFFER, | |||
| 224 | g_param_spec_object ("buffer", | |||
| 225 | "CtkSourceBuffer", | |||
| 226 | "", | |||
| 227 | CTK_SOURCE_TYPE_BUFFER(ctk_source_buffer_get_type ()), | |||
| 228 | G_PARAM_READWRITE | | |||
| 229 | G_PARAM_CONSTRUCT_ONLY | | |||
| 230 | G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB ))); | |||
| 231 | ||||
| 232 | g_object_class_install_property (object_class, | |||
| 233 | PROP_REMOVE_TRAILING_NEWLINE, | |||
| 234 | g_param_spec_boolean ("remove-trailing-newline", | |||
| 235 | "Remove trailing newline", | |||
| 236 | "", | |||
| 237 | TRUE(!(0)), | |||
| 238 | G_PARAM_READWRITE | | |||
| 239 | G_PARAM_CONSTRUCT_ONLY | | |||
| 240 | G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB ))); | |||
| 241 | } | |||
| 242 | ||||
| 243 | static void | |||
| 244 | ctk_source_buffer_output_stream_init (CtkSourceBufferOutputStream *stream) | |||
| 245 | { | |||
| 246 | stream->priv = ctk_source_buffer_output_stream_get_instance_private (stream); | |||
| 247 | ||||
| 248 | stream->priv->buffer = NULL((void*)0); | |||
| 249 | stream->priv->buflen = 0; | |||
| 250 | ||||
| 251 | stream->priv->charset_conv = NULL((void*)0); | |||
| 252 | stream->priv->encodings = NULL((void*)0); | |||
| 253 | stream->priv->current_encoding = NULL((void*)0); | |||
| 254 | ||||
| 255 | stream->priv->error_offset = -1; | |||
| 256 | ||||
| 257 | stream->priv->is_initialized = FALSE(0); | |||
| 258 | stream->priv->is_closed = FALSE(0); | |||
| 259 | stream->priv->is_utf8 = FALSE(0); | |||
| 260 | stream->priv->use_first = FALSE(0); | |||
| 261 | } | |||
| 262 | ||||
| 263 | static const CtkSourceEncoding * | |||
| 264 | get_encoding (CtkSourceBufferOutputStream *stream) | |||
| 265 | { | |||
| 266 | if (stream->priv->current_encoding == NULL((void*)0)) | |||
| 267 | { | |||
| 268 | stream->priv->current_encoding = stream->priv->encodings; | |||
| 269 | } | |||
| 270 | else | |||
| 271 | { | |||
| 272 | stream->priv->current_encoding = g_slist_next (stream->priv->current_encoding)((stream->priv->current_encoding) ? (((GSList *)(stream ->priv->current_encoding))->next) : ((void*)0)); | |||
| 273 | } | |||
| 274 | ||||
| 275 | if (stream->priv->current_encoding
| |||
| 276 | { | |||
| 277 | return stream->priv->current_encoding->data; | |||
| 278 | } | |||
| 279 | ||||
| 280 | stream->priv->use_first = TRUE(!(0)); | |||
| 281 | stream->priv->current_encoding = stream->priv->encodings; | |||
| 282 | ||||
| 283 | return stream->priv->current_encoding->data; | |||
| ||||
| 284 | } | |||
| 285 | ||||
| 286 | static gboolean | |||
| 287 | try_convert (GCharsetConverter *converter, | |||
| 288 | const void *inbuf, | |||
| 289 | gsize inbuf_size) | |||
| 290 | { | |||
| 291 | GError *err; | |||
| 292 | gsize bytes_read, nread; | |||
| 293 | gsize bytes_written, nwritten; | |||
| 294 | GConverterResult res; | |||
| 295 | gchar *out; | |||
| 296 | gboolean ret; | |||
| 297 | gsize out_size; | |||
| 298 | ||||
| 299 | if (inbuf == NULL((void*)0) || inbuf_size == 0) | |||
| 300 | { | |||
| 301 | return FALSE(0); | |||
| 302 | } | |||
| 303 | ||||
| 304 | err = NULL((void*)0); | |||
| 305 | nread = 0; | |||
| 306 | nwritten = 0; | |||
| 307 | out_size = inbuf_size * 4; | |||
| 308 | out = g_malloc (out_size); | |||
| 309 | ||||
| 310 | do | |||
| 311 | { | |||
| 312 | res = g_converter_convert (G_CONVERTER (converter)((((GConverter*) (void *) ((converter))))), | |||
| 313 | (gchar *)inbuf + nread, | |||
| 314 | inbuf_size - nread, | |||
| 315 | (gchar *)out + nwritten, | |||
| 316 | out_size - nwritten, | |||
| 317 | G_CONVERTER_INPUT_AT_END, | |||
| 318 | &bytes_read, | |||
| 319 | &bytes_written, | |||
| 320 | &err); | |||
| 321 | ||||
| 322 | nread += bytes_read; | |||
| 323 | nwritten += bytes_written; | |||
| 324 | } while (res != G_CONVERTER_FINISHED && res != G_CONVERTER_ERROR && err == NULL((void*)0)); | |||
| 325 | ||||
| 326 | if (err != NULL((void*)0)) | |||
| 327 | { | |||
| 328 | if (err->code == G_CONVERT_ERROR_PARTIAL_INPUT) | |||
| 329 | { | |||
| 330 | /* FIXME We can get partial input while guessing the | |||
| 331 | encoding because we just take some amount of text | |||
| 332 | to guess from. */ | |||
| 333 | ret = TRUE(!(0)); | |||
| 334 | } | |||
| 335 | else | |||
| 336 | { | |||
| 337 | ret = FALSE(0); | |||
| 338 | } | |||
| 339 | ||||
| 340 | g_error_free (err); | |||
| 341 | } | |||
| 342 | else | |||
| 343 | { | |||
| 344 | ret = TRUE(!(0)); | |||
| 345 | } | |||
| 346 | ||||
| 347 | /* FIXME: Check the remainder? */ | |||
| 348 | if (ret == TRUE(!(0)) && !g_utf8_validate (out, nwritten, NULL((void*)0))) | |||
| 349 | { | |||
| 350 | ret = FALSE(0); | |||
| 351 | } | |||
| 352 | ||||
| 353 | g_free (out); | |||
| 354 | ||||
| 355 | return ret; | |||
| 356 | } | |||
| 357 | ||||
| 358 | static GCharsetConverter * | |||
| 359 | guess_encoding (CtkSourceBufferOutputStream *stream, | |||
| 360 | const void *inbuf, | |||
| 361 | gsize inbuf_size) | |||
| 362 | { | |||
| 363 | GCharsetConverter *conv = NULL((void*)0); | |||
| 364 | ||||
| 365 | if (inbuf == NULL((void*)0) || inbuf_size == 0) | |||
| 366 | { | |||
| 367 | stream->priv->is_utf8 = TRUE(!(0)); | |||
| 368 | return NULL((void*)0); | |||
| 369 | } | |||
| 370 | ||||
| 371 | if (stream->priv->encodings != NULL((void*)0) && | |||
| 372 | stream->priv->encodings->next == NULL((void*)0)) | |||
| 373 | { | |||
| 374 | stream->priv->use_first = TRUE(!(0)); | |||
| 375 | } | |||
| 376 | ||||
| 377 | /* We just check the first block */ | |||
| 378 | while (TRUE(!(0))) | |||
| 379 | { | |||
| 380 | const CtkSourceEncoding *enc; | |||
| 381 | ||||
| 382 | g_clear_object (&conv)do { _Static_assert (sizeof *((&conv)) == sizeof (gpointer ), "Expression evaluates to false"); __typeof__ (((&conv) )) _pp = ((&conv)); __typeof__ (*((&conv))) _ptr = *_pp ; *_pp = ((void*)0); if (_ptr) (g_object_unref) (_ptr); } while (0); | |||
| 383 | ||||
| 384 | /* We get an encoding from the list */ | |||
| 385 | enc = get_encoding (stream); | |||
| 386 | ||||
| 387 | /* if it is NULL we didn't guess anything */ | |||
| 388 | if (enc == NULL((void*)0)) | |||
| 389 | { | |||
| 390 | break; | |||
| 391 | } | |||
| 392 | ||||
| 393 | DEBUG ({ | |||
| 394 | g_print ("trying charset: %s\n", | |||
| 395 | ctk_source_encoding_get_charset (stream->priv->current_encoding->data)); | |||
| 396 | }); | |||
| 397 | ||||
| 398 | if (enc == ctk_source_encoding_get_utf8 ()) | |||
| 399 | { | |||
| 400 | gsize remainder; | |||
| 401 | const gchar *end; | |||
| 402 | ||||
| 403 | if (g_utf8_validate (inbuf, inbuf_size, &end) || | |||
| 404 | stream->priv->use_first) | |||
| 405 | { | |||
| 406 | stream->priv->is_utf8 = TRUE(!(0)); | |||
| 407 | break; | |||
| 408 | } | |||
| 409 | ||||
| 410 | /* Check if the end is less than one char */ | |||
| 411 | remainder = inbuf_size - (end - (gchar *)inbuf); | |||
| 412 | if (remainder < 6) | |||
| 413 | { | |||
| 414 | stream->priv->is_utf8 = TRUE(!(0)); | |||
| 415 | break; | |||
| 416 | } | |||
| 417 | ||||
| 418 | continue; | |||
| 419 | } | |||
| 420 | ||||
| 421 | conv = g_charset_converter_new ("UTF-8", | |||
| 422 | ctk_source_encoding_get_charset (enc), | |||
| 423 | NULL((void*)0)); | |||
| 424 | ||||
| 425 | /* If we tried all encodings we use the first one */ | |||
| 426 | if (stream->priv->use_first) | |||
| 427 | { | |||
| 428 | break; | |||
| 429 | } | |||
| 430 | ||||
| 431 | /* Try to convert */ | |||
| 432 | if (try_convert (conv, inbuf, inbuf_size)) | |||
| 433 | { | |||
| 434 | break; | |||
| 435 | } | |||
| 436 | } | |||
| 437 | ||||
| 438 | if (conv != NULL((void*)0)) | |||
| 439 | { | |||
| 440 | g_converter_reset (G_CONVERTER (conv)((((GConverter*) (void *) ((conv)))))); | |||
| 441 | } | |||
| 442 | ||||
| 443 | return conv; | |||
| 444 | } | |||
| 445 | ||||
| 446 | static CtkSourceNewlineType | |||
| 447 | get_newline_type (CtkTextIter *end) | |||
| 448 | { | |||
| 449 | CtkSourceNewlineType res; | |||
| 450 | CtkTextIter copy; | |||
| 451 | gunichar c; | |||
| 452 | ||||
| 453 | copy = *end; | |||
| 454 | c = ctk_text_iter_get_char (©); | |||
| 455 | ||||
| 456 | if (g_unichar_break_type (c) == G_UNICODE_BREAK_CARRIAGE_RETURN) | |||
| 457 | { | |||
| 458 | if (ctk_text_iter_forward_char (©) && | |||
| 459 | g_unichar_break_type (ctk_text_iter_get_char (©)) == G_UNICODE_BREAK_LINE_FEED) | |||
| 460 | { | |||
| 461 | res = CTK_SOURCE_NEWLINE_TYPE_CR_LF; | |||
| 462 | } | |||
| 463 | else | |||
| 464 | { | |||
| 465 | res = CTK_SOURCE_NEWLINE_TYPE_CR; | |||
| 466 | } | |||
| 467 | } | |||
| 468 | else | |||
| 469 | { | |||
| 470 | res = CTK_SOURCE_NEWLINE_TYPE_LF; | |||
| 471 | } | |||
| 472 | ||||
| 473 | return res; | |||
| 474 | } | |||
| 475 | ||||
| 476 | CtkSourceBufferOutputStream * | |||
| 477 | ctk_source_buffer_output_stream_new (CtkSourceBuffer *buffer, | |||
| 478 | GSList *candidate_encodings, | |||
| 479 | gboolean remove_trailing_newline) | |||
| 480 | { | |||
| 481 | CtkSourceBufferOutputStream *stream; | |||
| 482 | ||||
| 483 | stream = g_object_new (CTK_SOURCE_TYPE_BUFFER_OUTPUT_STREAM(ctk_source_buffer_output_stream_get_type ()), | |||
| 484 | "buffer", buffer, | |||
| 485 | "remove-trailing-newline", remove_trailing_newline, | |||
| 486 | NULL((void*)0)); | |||
| 487 | ||||
| 488 | stream->priv->encodings = g_slist_copy (candidate_encodings); | |||
| 489 | ||||
| 490 | return stream; | |||
| 491 | } | |||
| 492 | ||||
| 493 | CtkSourceNewlineType | |||
| 494 | ctk_source_buffer_output_stream_detect_newline_type (CtkSourceBufferOutputStream *stream) | |||
| 495 | { | |||
| 496 | CtkSourceNewlineType type; | |||
| 497 | CtkTextIter iter; | |||
| 498 | ||||
| 499 | g_return_val_if_fail (CTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream),do { if ((__builtin_expect (__extension__ ({ int _g_boolean_var_15 = 0; if ((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance *) ((stream)); GType __t = ((ctk_source_buffer_output_stream_get_type ())); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; }))))) _g_boolean_var_15 = 1; _g_boolean_var_15; }), 1) )) { } else { g_return_if_fail_warning ("CtkSourceView", ((const char*) (__func__)), "CTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream)" ); return (CTK_SOURCE_NEWLINE_TYPE_LF); } } while (0) | |||
| 500 | CTK_SOURCE_NEWLINE_TYPE_DEFAULT)do { if ((__builtin_expect (__extension__ ({ int _g_boolean_var_15 = 0; if ((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance *) ((stream)); GType __t = ((ctk_source_buffer_output_stream_get_type ())); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; }))))) _g_boolean_var_15 = 1; _g_boolean_var_15; }), 1) )) { } else { g_return_if_fail_warning ("CtkSourceView", ((const char*) (__func__)), "CTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream)" ); return (CTK_SOURCE_NEWLINE_TYPE_LF); } } while (0); | |||
| 501 | ||||
| 502 | if (stream->priv->source_buffer == NULL((void*)0)) | |||
| 503 | { | |||
| 504 | return CTK_SOURCE_NEWLINE_TYPE_DEFAULTCTK_SOURCE_NEWLINE_TYPE_LF; | |||
| 505 | } | |||
| 506 | ||||
| 507 | type = CTK_SOURCE_NEWLINE_TYPE_DEFAULTCTK_SOURCE_NEWLINE_TYPE_LF; | |||
| 508 | ||||
| 509 | ctk_text_buffer_get_start_iter (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))), | |||
| 510 | &iter); | |||
| 511 | ||||
| 512 | if (ctk_text_iter_ends_line (&iter) || ctk_text_iter_forward_to_line_end (&iter)) | |||
| 513 | { | |||
| 514 | type = get_newline_type (&iter); | |||
| 515 | } | |||
| 516 | ||||
| 517 | return type; | |||
| 518 | } | |||
| 519 | ||||
| 520 | const CtkSourceEncoding * | |||
| 521 | ctk_source_buffer_output_stream_get_guessed (CtkSourceBufferOutputStream *stream) | |||
| 522 | { | |||
| 523 | g_return_val_if_fail (CTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream), NULL)do { if ((__builtin_expect (__extension__ ({ int _g_boolean_var_16 = 0; if ((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance *) ((stream)); GType __t = ((ctk_source_buffer_output_stream_get_type ())); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; }))))) _g_boolean_var_16 = 1; _g_boolean_var_16; }), 1) )) { } else { g_return_if_fail_warning ("CtkSourceView", ((const char*) (__func__)), "CTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream)" ); return (((void*)0)); } } while (0); | |||
| 524 | ||||
| 525 | if (stream->priv->current_encoding != NULL((void*)0)) | |||
| 526 | { | |||
| 527 | return stream->priv->current_encoding->data; | |||
| 528 | } | |||
| 529 | else if (stream->priv->is_utf8 || !stream->priv->is_initialized) | |||
| 530 | { | |||
| 531 | /* If it is not initialized we assume that we are trying to | |||
| 532 | * convert the empty string. | |||
| 533 | */ | |||
| 534 | return ctk_source_encoding_get_utf8 (); | |||
| 535 | } | |||
| 536 | ||||
| 537 | return NULL((void*)0); | |||
| 538 | } | |||
| 539 | ||||
| 540 | guint | |||
| 541 | ctk_source_buffer_output_stream_get_num_fallbacks (CtkSourceBufferOutputStream *stream) | |||
| 542 | { | |||
| 543 | g_return_val_if_fail (CTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream), 0)do { if ((__builtin_expect (__extension__ ({ int _g_boolean_var_17 = 0; if ((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance *) ((stream)); GType __t = ((ctk_source_buffer_output_stream_get_type ())); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; }))))) _g_boolean_var_17 = 1; _g_boolean_var_17; }), 1) )) { } else { g_return_if_fail_warning ("CtkSourceView", ((const char*) (__func__)), "CTK_SOURCE_IS_BUFFER_OUTPUT_STREAM (stream)" ); return (0); } } while (0); | |||
| 544 | ||||
| 545 | return stream->priv->n_fallback_errors; | |||
| 546 | } | |||
| 547 | ||||
| 548 | static void | |||
| 549 | apply_error_tag (CtkSourceBufferOutputStream *stream) | |||
| 550 | { | |||
| 551 | CtkTextIter start; | |||
| 552 | ||||
| 553 | if (stream->priv->error_offset == -1 || | |||
| 554 | stream->priv->source_buffer == NULL((void*)0)) | |||
| 555 | { | |||
| 556 | return; | |||
| 557 | } | |||
| 558 | ||||
| 559 | ctk_text_buffer_get_iter_at_offset (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))), | |||
| 560 | &start, stream->priv->error_offset); | |||
| 561 | ||||
| 562 | _ctk_source_buffer_set_as_invalid_character (stream->priv->source_buffer, | |||
| 563 | &start, | |||
| 564 | &stream->priv->pos); | |||
| 565 | ||||
| 566 | stream->priv->error_offset = -1; | |||
| 567 | } | |||
| 568 | ||||
| 569 | static void | |||
| 570 | insert_fallback (CtkSourceBufferOutputStream *stream, | |||
| 571 | const gchar *buffer) | |||
| 572 | { | |||
| 573 | guint8 out[4]; | |||
| 574 | guint8 v; | |||
| 575 | const gchar hex[] = "0123456789ABCDEF"; | |||
| 576 | ||||
| 577 | if (stream->priv->source_buffer == NULL((void*)0)) | |||
| 578 | { | |||
| 579 | return; | |||
| 580 | } | |||
| 581 | ||||
| 582 | /* If we are here it is because we are pointing to an invalid char so we | |||
| 583 | * substitute it by an hex value. | |||
| 584 | */ | |||
| 585 | v = *(guint8 *)buffer; | |||
| 586 | out[0] = '\\'; | |||
| 587 | out[1] = hex[(v & 0xf0) >> 4]; | |||
| 588 | out[2] = hex[(v & 0x0f) >> 0]; | |||
| 589 | out[3] = '\0'; | |||
| 590 | ||||
| 591 | ctk_text_buffer_insert (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))), | |||
| 592 | &stream->priv->pos, (const gchar *)out, 3); | |||
| 593 | ||||
| 594 | ++stream->priv->n_fallback_errors; | |||
| 595 | } | |||
| 596 | ||||
| 597 | static void | |||
| 598 | validate_and_insert (CtkSourceBufferOutputStream *stream, | |||
| 599 | gchar *buffer, | |||
| 600 | gsize count, | |||
| 601 | gboolean owned) | |||
| 602 | { | |||
| 603 | CtkTextBuffer *text_buffer; | |||
| 604 | CtkTextIter *iter; | |||
| 605 | gsize len; | |||
| 606 | gchar *free_text = NULL((void*)0); | |||
| 607 | ||||
| 608 | if (stream->priv->source_buffer == NULL((void*)0)) | |||
| 609 | { | |||
| 610 | return; | |||
| 611 | } | |||
| 612 | ||||
| 613 | text_buffer = CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))); | |||
| 614 | iter = &stream->priv->pos; | |||
| 615 | len = count; | |||
| 616 | ||||
| 617 | while (len != 0) | |||
| 618 | { | |||
| 619 | const gchar *end; | |||
| 620 | gboolean valid; | |||
| 621 | gsize nvalid; | |||
| 622 | ||||
| 623 | /* validate */ | |||
| 624 | valid = g_utf8_validate (buffer, len, &end); | |||
| 625 | nvalid = end - buffer; | |||
| 626 | ||||
| 627 | /* Note: this is a workaround for a 'bug' in CtkTextBuffer where | |||
| 628 | inserting first a \r and then in a second insert, a \n, | |||
| 629 | will result in two lines being added instead of a single | |||
| 630 | one */ | |||
| 631 | ||||
| 632 | if (valid) | |||
| 633 | { | |||
| 634 | gchar *ptr; | |||
| 635 | ||||
| 636 | ptr = g_utf8_find_prev_char (buffer, buffer + len); | |||
| 637 | ||||
| 638 | if (ptr && *ptr == '\r' && ptr - buffer == (glong)len - 1) | |||
| 639 | { | |||
| 640 | stream->priv->buffer = g_new (gchar, 2)(gchar *) (__extension__ ({ gsize __n = (gsize) (2); gsize __s = sizeof (gchar); gpointer __p; if (__s == 1) __p = g_malloc (__n); else if (__builtin_constant_p (__n) && (__s == 0 || __n <= (9223372036854775807L *2UL+1UL) / __s)) __p = g_malloc (__n * __s); else __p = g_malloc_n (__n, __s); __p; })); | |||
| 641 | stream->priv->buffer[0] = '\r'; | |||
| 642 | stream->priv->buffer[1] = '\0'; | |||
| 643 | stream->priv->buflen = 1; | |||
| 644 | ||||
| 645 | /* Decrease also the len so in the check | |||
| 646 | nvalid == len we get out of this method */ | |||
| 647 | --nvalid; | |||
| 648 | --len; | |||
| 649 | } | |||
| 650 | } | |||
| 651 | ||||
| 652 | /* if we've got any valid char we must tag the invalid chars */ | |||
| 653 | if (nvalid > 0) | |||
| 654 | { | |||
| 655 | gchar orig_non_null = '\0'; | |||
| 656 | ||||
| 657 | apply_error_tag (stream); | |||
| 658 | ||||
| 659 | if ((nvalid != len || !owned) && buffer[nvalid] != '\0') | |||
| 660 | { | |||
| 661 | /* make sure the buffer is always properly null | |||
| 662 | * terminated. This is needed, at least for now, | |||
| 663 | * to avoid issues with pygobject marshalling of | |||
| 664 | * the insert-text signal of ctktextbuffer | |||
| 665 | * | |||
| 666 | * https://bugzilla.gnome.org/show_bug.cgi?id=726689 | |||
| 667 | */ | |||
| 668 | if (!owned) | |||
| 669 | { | |||
| 670 | /* forced to make a copy */ | |||
| 671 | free_text = g_new (gchar, len + 1)(gchar *) (__extension__ ({ gsize __n = (gsize) (len + 1); gsize __s = sizeof (gchar); gpointer __p; if (__s == 1) __p = g_malloc (__n); else if (__builtin_constant_p (__n) && (__s == 0 || __n <= (9223372036854775807L *2UL+1UL) / __s)) __p = g_malloc (__n * __s); else __p = g_malloc_n (__n, __s); __p; })); | |||
| 672 | memcpy (free_text, buffer, len); | |||
| 673 | free_text[len] = '\0'; | |||
| 674 | ||||
| 675 | buffer = free_text; | |||
| 676 | owned = TRUE(!(0)); | |||
| 677 | } | |||
| 678 | ||||
| 679 | orig_non_null = buffer[nvalid]; | |||
| 680 | buffer[nvalid] = '\0'; | |||
| 681 | } | |||
| 682 | ||||
| 683 | ctk_text_buffer_insert (text_buffer, iter, buffer, nvalid); | |||
| 684 | ||||
| 685 | if (orig_non_null != '\0') | |||
| 686 | { | |||
| 687 | /* restore null terminated replaced byte */ | |||
| 688 | buffer[nvalid] = orig_non_null; | |||
| 689 | } | |||
| 690 | } | |||
| 691 | ||||
| 692 | /* If we inserted all return */ | |||
| 693 | if (nvalid == len) | |||
| 694 | { | |||
| 695 | break; | |||
| 696 | } | |||
| 697 | ||||
| 698 | buffer += nvalid; | |||
| 699 | len = len - nvalid; | |||
| 700 | ||||
| 701 | if ((len < MAX_UNICHAR_LEN6) && | |||
| 702 | (g_utf8_get_char_validated (buffer, len) == (gunichar)-2)) | |||
| 703 | { | |||
| 704 | stream->priv->buffer = g_strndup (end, len); | |||
| 705 | stream->priv->buflen = len; | |||
| 706 | ||||
| 707 | break; | |||
| 708 | } | |||
| 709 | ||||
| 710 | /* we need the start of the chunk of invalid chars */ | |||
| 711 | if (stream->priv->error_offset == -1) | |||
| 712 | { | |||
| 713 | stream->priv->error_offset = ctk_text_iter_get_offset (&stream->priv->pos); | |||
| 714 | } | |||
| 715 | ||||
| 716 | insert_fallback (stream, buffer); | |||
| 717 | ++buffer; | |||
| 718 | --len; | |||
| 719 | } | |||
| 720 | ||||
| 721 | g_free (free_text); | |||
| 722 | } | |||
| 723 | ||||
| 724 | static void | |||
| 725 | remove_trailing_newline (CtkSourceBufferOutputStream *stream) | |||
| 726 | { | |||
| 727 | CtkTextIter end; | |||
| 728 | CtkTextIter start; | |||
| 729 | ||||
| 730 | if (stream->priv->source_buffer == NULL((void*)0)) | |||
| 731 | { | |||
| 732 | return; | |||
| 733 | } | |||
| 734 | ||||
| 735 | ctk_text_buffer_get_end_iter (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))), &end); | |||
| 736 | start = end; | |||
| 737 | ||||
| 738 | ctk_text_iter_set_line_offset (&start, 0); | |||
| 739 | ||||
| 740 | if (ctk_text_iter_ends_line (&start) && | |||
| 741 | ctk_text_iter_backward_line (&start)) | |||
| 742 | { | |||
| 743 | if (!ctk_text_iter_ends_line (&start)) | |||
| 744 | { | |||
| 745 | ctk_text_iter_forward_to_line_end (&start); | |||
| 746 | } | |||
| 747 | ||||
| 748 | ctk_text_buffer_delete (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))), | |||
| 749 | &start, | |||
| 750 | &end); | |||
| 751 | } | |||
| 752 | } | |||
| 753 | ||||
| 754 | static void | |||
| 755 | end_append_text_to_document (CtkSourceBufferOutputStream *stream) | |||
| 756 | { | |||
| 757 | if (stream->priv->source_buffer == NULL((void*)0)) | |||
| 758 | { | |||
| 759 | return; | |||
| 760 | } | |||
| 761 | ||||
| 762 | if (stream->priv->remove_trailing_newline) | |||
| 763 | { | |||
| 764 | remove_trailing_newline (stream); | |||
| 765 | } | |||
| 766 | ||||
| 767 | ctk_text_buffer_set_modified (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer ))))), | |||
| 768 | FALSE(0)); | |||
| 769 | ||||
| 770 | ctk_text_buffer_end_user_action (CTK_TEXT_BUFFER (stream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((stream->priv->source_buffer )))))); | |||
| 771 | ctk_source_buffer_end_not_undoable_action (stream->priv->source_buffer); | |||
| 772 | } | |||
| 773 | ||||
| 774 | static gboolean | |||
| 775 | convert_text (CtkSourceBufferOutputStream *stream, | |||
| 776 | const gchar *inbuf, | |||
| 777 | gsize inbuf_len, | |||
| 778 | gchar **outbuf, | |||
| 779 | gsize *outbuf_len, | |||
| 780 | GError **error) | |||
| 781 | { | |||
| 782 | gchar *out, *dest; | |||
| 783 | gsize in_left, out_left, outbuf_size, res; | |||
| 784 | gint errsv; | |||
| 785 | gboolean done, have_error; | |||
| 786 | ||||
| 787 | in_left = inbuf_len; | |||
| 788 | /* set an arbitrary length if inbuf_len is 0, this is needed to flush | |||
| 789 | the iconv data */ | |||
| 790 | outbuf_size = (inbuf_len > 0) ? inbuf_len : 100; | |||
| 791 | ||||
| 792 | out_left = outbuf_size; | |||
| 793 | ||||
| 794 | /* keep room for null termination */ | |||
| 795 | out = dest = g_malloc (sizeof (gchar) * (outbuf_size + 1)); | |||
| 796 | ||||
| 797 | done = FALSE(0); | |||
| 798 | have_error = FALSE(0); | |||
| 799 | ||||
| 800 | while (!done && !have_error) | |||
| 801 | { | |||
| 802 | /* If we reached here is because we need to convert the text, | |||
| 803 | so we convert it using iconv. | |||
| 804 | See that if inbuf is NULL the data will be flushed */ | |||
| 805 | res = g_iconv (stream->priv->iconv, | |||
| 806 | (gchar **)&inbuf, &in_left, | |||
| 807 | &out, &out_left); | |||
| 808 | ||||
| 809 | /* something went wrong */ | |||
| 810 | if (res == (gsize)-1) | |||
| 811 | { | |||
| 812 | errsv = errno(*__errno_location ()); | |||
| 813 | ||||
| 814 | switch (errsv) | |||
| 815 | { | |||
| 816 | case EINVAL22: | |||
| 817 | /* Incomplete text, do not report an error */ | |||
| 818 | stream->priv->iconv_buffer = g_strndup (inbuf, in_left); | |||
| 819 | stream->priv->iconv_buflen = in_left; | |||
| 820 | done = TRUE(!(0)); | |||
| 821 | break; | |||
| 822 | ||||
| 823 | case E2BIG7: | |||
| 824 | { | |||
| 825 | /* allocate more space */ | |||
| 826 | gsize used = out - dest; | |||
| 827 | ||||
| 828 | outbuf_size *= 2; | |||
| 829 | ||||
| 830 | /* make sure to allocate room for | |||
| 831 | terminating null byte */ | |||
| 832 | dest = g_realloc (dest, outbuf_size + 1); | |||
| 833 | ||||
| 834 | out = dest + used; | |||
| 835 | out_left = outbuf_size - used; | |||
| 836 | } | |||
| 837 | break; | |||
| 838 | ||||
| 839 | case EILSEQ84: | |||
| 840 | g_set_error_literal (error, G_CONVERT_ERRORg_convert_error_quark(), | |||
| 841 | G_CONVERT_ERROR_ILLEGAL_SEQUENCE, | |||
| 842 | _("Invalid byte sequence in conversion input")((char *) g_dgettext ("ctksourceview-4", "Invalid byte sequence in conversion input" ))); | |||
| 843 | have_error = TRUE(!(0)); | |||
| 844 | break; | |||
| 845 | ||||
| 846 | default: | |||
| 847 | g_set_error (error, G_CONVERT_ERRORg_convert_error_quark(), G_CONVERT_ERROR_FAILED, | |||
| 848 | _("Error during conversion: %s")((char *) g_dgettext ("ctksourceview-4", "Error during conversion: %s" )), | |||
| 849 | g_strerror (errsv)); | |||
| 850 | have_error = TRUE(!(0)); | |||
| 851 | break; | |||
| 852 | } | |||
| 853 | } | |||
| 854 | else | |||
| 855 | { | |||
| 856 | done = TRUE(!(0)); | |||
| 857 | } | |||
| 858 | } | |||
| 859 | ||||
| 860 | if (have_error) | |||
| 861 | { | |||
| 862 | g_free (dest); | |||
| 863 | *outbuf = NULL((void*)0); | |||
| 864 | *outbuf_len = 0; | |||
| 865 | ||||
| 866 | return FALSE(0); | |||
| 867 | } | |||
| 868 | ||||
| 869 | *outbuf_len = out - dest; | |||
| 870 | dest[*outbuf_len] = '\0'; | |||
| 871 | ||||
| 872 | *outbuf = dest; | |||
| 873 | return TRUE(!(0)); | |||
| 874 | } | |||
| 875 | ||||
| 876 | static gssize | |||
| 877 | ctk_source_buffer_output_stream_write (GOutputStream *stream, | |||
| 878 | const void *buffer, | |||
| 879 | gsize count, | |||
| 880 | GCancellable *cancellable, | |||
| 881 | GError **error) | |||
| 882 | { | |||
| 883 | CtkSourceBufferOutputStream *ostream; | |||
| 884 | gchar *text; | |||
| 885 | gsize len; | |||
| 886 | gboolean freetext = FALSE(0); | |||
| 887 | ||||
| 888 | ostream = CTK_SOURCE_BUFFER_OUTPUT_STREAM (stream)((((CtkSourceBufferOutputStream*) (void *) ((stream))))); | |||
| 889 | ||||
| 890 | if (g_cancellable_set_error_if_cancelled (cancellable, error) || | |||
| ||||
| 891 | ostream->priv->source_buffer == NULL((void*)0)) | |||
| 892 | { | |||
| 893 | return -1; | |||
| 894 | } | |||
| 895 | ||||
| 896 | if (!ostream->priv->is_initialized) | |||
| 897 | { | |||
| 898 | ostream->priv->charset_conv = guess_encoding (ostream, buffer, count); | |||
| 899 | ||||
| 900 | /* If we still have the previous case is that we didn't guess | |||
| 901 | anything */ | |||
| 902 | if (ostream->priv->charset_conv == NULL((void*)0) && | |||
| 903 | !ostream->priv->is_utf8) | |||
| 904 | { | |||
| 905 | g_set_error_literal (error, CTK_SOURCE_FILE_LOADER_ERRORctk_source_file_loader_error_quark (), | |||
| 906 | CTK_SOURCE_FILE_LOADER_ERROR_ENCODING_AUTO_DETECTION_FAILED, | |||
| 907 | "It is not possible to detect the encoding automatically"); | |||
| 908 | ||||
| 909 | return -1; | |||
| 910 | } | |||
| 911 | ||||
| 912 | /* Do not initialize iconv if we are not going to convert anything */ | |||
| 913 | if (!ostream->priv->is_utf8) | |||
| 914 | { | |||
| 915 | gchar *from_charset; | |||
| 916 | ||||
| 917 | /* Initialize iconv */ | |||
| 918 | g_object_get (G_OBJECT (ostream->priv->charset_conv)((((GObject*) (void *) ((ostream->priv->charset_conv))) )), | |||
| 919 | "from-charset", &from_charset, | |||
| 920 | NULL((void*)0)); | |||
| 921 | ||||
| 922 | ostream->priv->iconv = g_iconv_open ("UTF-8", from_charset); | |||
| 923 | ||||
| 924 | if (ostream->priv->iconv == (GIConv)-1) | |||
| 925 | { | |||
| 926 | if (errno(*__errno_location ()) == EINVAL22) | |||
| 927 | { | |||
| 928 | g_set_error (error, G_IO_ERRORg_io_error_quark(), G_IO_ERROR_NOT_SUPPORTED, | |||
| 929 | _("Conversion from character set “%s” to “UTF-8” is not supported")((char *) g_dgettext ("ctksourceview-4", "Conversion from character set “%s” to “UTF-8” is not supported" )), | |||
| 930 | from_charset); | |||
| 931 | } | |||
| 932 | else | |||
| 933 | { | |||
| 934 | g_set_error (error, G_IO_ERRORg_io_error_quark(), G_IO_ERROR_FAILED, | |||
| 935 | _("Could not open converter from “%s” to “UTF-8”")((char *) g_dgettext ("ctksourceview-4", "Could not open converter from “%s” to “UTF-8”" )), | |||
| 936 | from_charset); | |||
| 937 | } | |||
| 938 | ||||
| 939 | g_free (from_charset); | |||
| 940 | g_clear_object (&ostream->priv->charset_conv)do { _Static_assert (sizeof *((&ostream->priv->charset_conv )) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (((&ostream->priv->charset_conv))) _pp = ((&ostream ->priv->charset_conv)); __typeof__ (*((&ostream-> priv->charset_conv))) _ptr = *_pp; *_pp = ((void*)0); if ( _ptr) (g_object_unref) (_ptr); } while (0); | |||
| 941 | ||||
| 942 | return -1; | |||
| 943 | } | |||
| 944 | ||||
| 945 | g_free (from_charset); | |||
| 946 | } | |||
| 947 | ||||
| 948 | /* Begin not undoable action. Begin also a normal user action, | |||
| 949 | * since we load the file chunk by chunk and it should be seen | |||
| 950 | * as only one action, for the features that rely on the user | |||
| 951 | * action. | |||
| 952 | */ | |||
| 953 | ctk_source_buffer_begin_not_undoable_action (ostream->priv->source_buffer); | |||
| 954 | ctk_text_buffer_begin_user_action (CTK_TEXT_BUFFER (ostream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((ostream->priv->source_buffer )))))); | |||
| 955 | ||||
| 956 | ctk_text_buffer_get_start_iter (CTK_TEXT_BUFFER (ostream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((ostream->priv->source_buffer ))))), | |||
| 957 | &ostream->priv->pos); | |||
| 958 | ||||
| 959 | ostream->priv->is_initialized = TRUE(!(0)); | |||
| 960 | } | |||
| 961 | ||||
| 962 | if (ostream->priv->buflen > 0) | |||
| 963 | { | |||
| 964 | len = ostream->priv->buflen + count; | |||
| 965 | text = g_malloc (len + 1); | |||
| 966 | ||||
| 967 | memcpy (text, ostream->priv->buffer, ostream->priv->buflen); | |||
| 968 | memcpy (text + ostream->priv->buflen, buffer, count); | |||
| 969 | ||||
| 970 | text[len] = '\0'; | |||
| 971 | ||||
| 972 | g_free (ostream->priv->buffer); | |||
| 973 | ||||
| 974 | ostream->priv->buffer = NULL((void*)0); | |||
| 975 | ostream->priv->buflen = 0; | |||
| 976 | ||||
| 977 | freetext = TRUE(!(0)); | |||
| 978 | } | |||
| 979 | else | |||
| 980 | { | |||
| 981 | text = (gchar *) buffer; | |||
| 982 | len = count; | |||
| 983 | } | |||
| 984 | ||||
| 985 | if (!ostream->priv->is_utf8) | |||
| 986 | { | |||
| 987 | gchar *outbuf; | |||
| 988 | gsize outbuf_len; | |||
| 989 | ||||
| 990 | /* check if iconv was correctly initializated, this shouldn't | |||
| 991 | happen but better be safe */ | |||
| 992 | if (ostream->priv->iconv == NULL((void*)0)) | |||
| 993 | { | |||
| 994 | g_set_error_literal (error, G_IO_ERRORg_io_error_quark(), G_IO_ERROR_NOT_INITIALIZED, | |||
| 995 | _("Invalid object, not initialized")((char *) g_dgettext ("ctksourceview-4", "Invalid object, not initialized" ))); | |||
| 996 | ||||
| 997 | if (freetext) | |||
| 998 | { | |||
| 999 | g_free (text); | |||
| 1000 | } | |||
| 1001 | ||||
| 1002 | return -1; | |||
| 1003 | } | |||
| 1004 | ||||
| 1005 | /* manage the previous conversion buffer */ | |||
| 1006 | if (ostream->priv->iconv_buflen > 0) | |||
| 1007 | { | |||
| 1008 | gchar *text2; | |||
| 1009 | gsize len2; | |||
| 1010 | ||||
| 1011 | len2 = len + ostream->priv->iconv_buflen; | |||
| 1012 | text2 = g_malloc (len2 + 1); | |||
| 1013 | ||||
| 1014 | memcpy (text2, ostream->priv->iconv_buffer, ostream->priv->iconv_buflen); | |||
| 1015 | memcpy (text2 + ostream->priv->iconv_buflen, text, len); | |||
| 1016 | ||||
| 1017 | text2[len2] = '\0'; | |||
| 1018 | ||||
| 1019 | if (freetext) | |||
| 1020 | { | |||
| 1021 | g_free (text); | |||
| 1022 | } | |||
| 1023 | ||||
| 1024 | text = text2; | |||
| 1025 | len = len2; | |||
| 1026 | ||||
| 1027 | g_free (ostream->priv->iconv_buffer); | |||
| 1028 | ||||
| 1029 | ostream->priv->iconv_buffer = NULL((void*)0); | |||
| 1030 | ostream->priv->iconv_buflen = 0; | |||
| 1031 | ||||
| 1032 | freetext = TRUE(!(0)); | |||
| 1033 | } | |||
| 1034 | ||||
| 1035 | if (!convert_text (ostream, text, len, &outbuf, &outbuf_len, error)) | |||
| 1036 | { | |||
| 1037 | if (freetext) | |||
| 1038 | { | |||
| 1039 | g_free (text); | |||
| 1040 | } | |||
| 1041 | ||||
| 1042 | return -1; | |||
| 1043 | } | |||
| 1044 | ||||
| 1045 | if (freetext) | |||
| 1046 | { | |||
| 1047 | g_free (text); | |||
| 1048 | } | |||
| 1049 | ||||
| 1050 | /* set the converted text as the text to validate */ | |||
| 1051 | text = outbuf; | |||
| 1052 | len = outbuf_len; | |||
| 1053 | } | |||
| 1054 | ||||
| 1055 | validate_and_insert (ostream, text, len, freetext); | |||
| 1056 | ||||
| 1057 | if (freetext) | |||
| 1058 | { | |||
| 1059 | g_free (text); | |||
| 1060 | } | |||
| 1061 | ||||
| 1062 | return count; | |||
| 1063 | } | |||
| 1064 | ||||
| 1065 | static gboolean | |||
| 1066 | ctk_source_buffer_output_stream_flush (GOutputStream *stream, | |||
| 1067 | GCancellable *cancellable, | |||
| 1068 | GError **error) | |||
| 1069 | { | |||
| 1070 | CtkSourceBufferOutputStream *ostream; | |||
| 1071 | ||||
| 1072 | ostream = CTK_SOURCE_BUFFER_OUTPUT_STREAM (stream)((((CtkSourceBufferOutputStream*) (void *) ((stream))))); | |||
| 1073 | ||||
| 1074 | if (ostream->priv->is_closed || | |||
| 1075 | ostream->priv->source_buffer == NULL((void*)0)) | |||
| 1076 | { | |||
| 1077 | return TRUE(!(0)); | |||
| 1078 | } | |||
| 1079 | ||||
| 1080 | /* if we have converted something flush residual data, validate and insert */ | |||
| 1081 | if (ostream->priv->iconv != NULL((void*)0)) | |||
| 1082 | { | |||
| 1083 | gchar *outbuf; | |||
| 1084 | gsize outbuf_len; | |||
| 1085 | ||||
| 1086 | if (convert_text (ostream, NULL((void*)0), 0, &outbuf, &outbuf_len, error)) | |||
| 1087 | { | |||
| 1088 | validate_and_insert (ostream, outbuf, outbuf_len, TRUE(!(0))); | |||
| 1089 | g_free (outbuf); | |||
| 1090 | } | |||
| 1091 | else | |||
| 1092 | { | |||
| 1093 | return FALSE(0); | |||
| 1094 | } | |||
| 1095 | } | |||
| 1096 | ||||
| 1097 | if (ostream->priv->buflen > 0 && *ostream->priv->buffer != '\r') | |||
| 1098 | { | |||
| 1099 | /* If we reached here is because the last insertion was a half | |||
| 1100 | correct char, which has to be inserted as fallback */ | |||
| 1101 | gchar *text; | |||
| 1102 | ||||
| 1103 | if (ostream->priv->error_offset == -1) | |||
| 1104 | { | |||
| 1105 | ostream->priv->error_offset = ctk_text_iter_get_offset (&ostream->priv->pos); | |||
| 1106 | } | |||
| 1107 | ||||
| 1108 | text = ostream->priv->buffer; | |||
| 1109 | while (ostream->priv->buflen != 0) | |||
| 1110 | { | |||
| 1111 | insert_fallback (ostream, text); | |||
| 1112 | ++text; | |||
| 1113 | --ostream->priv->buflen; | |||
| 1114 | } | |||
| 1115 | ||||
| 1116 | g_free (ostream->priv->buffer); | |||
| 1117 | ostream->priv->buffer = NULL((void*)0); | |||
| 1118 | } | |||
| 1119 | else if (ostream->priv->buflen == 1 && *ostream->priv->buffer == '\r') | |||
| 1120 | { | |||
| 1121 | /* The previous chars can be invalid */ | |||
| 1122 | apply_error_tag (ostream); | |||
| 1123 | ||||
| 1124 | /* See special case above, flush this */ | |||
| 1125 | ctk_text_buffer_insert (CTK_TEXT_BUFFER (ostream->priv->source_buffer)((((CtkTextBuffer*) (void *) ((ostream->priv->source_buffer ))))), | |||
| 1126 | &ostream->priv->pos, | |||
| 1127 | "\r", | |||
| 1128 | 1); | |||
| 1129 | ||||
| 1130 | g_free (ostream->priv->buffer); | |||
| 1131 | ostream->priv->buffer = NULL((void*)0); | |||
| 1132 | ostream->priv->buflen = 0; | |||
| 1133 | } | |||
| 1134 | ||||
| 1135 | if (ostream->priv->iconv_buflen > 0 ) | |||
| 1136 | { | |||
| 1137 | /* If we reached here is because the last insertion was a half | |||
| 1138 | correct char, which has to be inserted as fallback */ | |||
| 1139 | gchar *text; | |||
| 1140 | ||||
| 1141 | if (ostream->priv->error_offset == -1) | |||
| 1142 | { | |||
| 1143 | ostream->priv->error_offset = ctk_text_iter_get_offset (&ostream->priv->pos); | |||
| 1144 | } | |||
| 1145 | ||||
| 1146 | text = ostream->priv->iconv_buffer; | |||
| 1147 | while (ostream->priv->iconv_buflen != 0) | |||
| 1148 | { | |||
| 1149 | insert_fallback (ostream, text); | |||
| 1150 | ++text; | |||
| 1151 | --ostream->priv->iconv_buflen; | |||
| 1152 | } | |||
| 1153 | ||||
| 1154 | g_free (ostream->priv->iconv_buffer); | |||
| 1155 | ostream->priv->iconv_buffer = NULL((void*)0); | |||
| 1156 | } | |||
| 1157 | ||||
| 1158 | apply_error_tag (ostream); | |||
| 1159 | ||||
| 1160 | return TRUE(!(0)); | |||
| 1161 | } | |||
| 1162 | ||||
| 1163 | static gboolean | |||
| 1164 | ctk_source_buffer_output_stream_close (GOutputStream *stream, | |||
| 1165 | GCancellable *cancellable, | |||
| 1166 | GError **error) | |||
| 1167 | { | |||
| 1168 | CtkSourceBufferOutputStream *ostream = CTK_SOURCE_BUFFER_OUTPUT_STREAM (stream)((((CtkSourceBufferOutputStream*) (void *) ((stream))))); | |||
| 1169 | ||||
| 1170 | if (!ostream->priv->is_closed && ostream->priv->is_initialized) | |||
| 1171 | { | |||
| 1172 | end_append_text_to_document (ostream); | |||
| 1173 | ||||
| 1174 | if (ostream->priv->iconv != NULL((void*)0)) | |||
| 1175 | { | |||
| 1176 | g_iconv_close (ostream->priv->iconv); | |||
| 1177 | } | |||
| 1178 | ||||
| 1179 | ostream->priv->is_closed = TRUE(!(0)); | |||
| 1180 | } | |||
| 1181 | ||||
| 1182 | if (ostream->priv->buflen > 0 || ostream->priv->iconv_buflen > 0) | |||
| 1183 | { | |||
| 1184 | g_set_error (error, | |||
| 1185 | G_IO_ERRORg_io_error_quark(), | |||
| 1186 | G_IO_ERROR_INVALID_DATA, | |||
| 1187 | _("Incomplete UTF-8 sequence in input")((char *) g_dgettext ("ctksourceview-4", "Incomplete UTF-8 sequence in input" ))); | |||
| 1188 | ||||
| 1189 | return FALSE(0); | |||
| 1190 | } | |||
| 1191 | ||||
| 1192 | return TRUE(!(0)); | |||
| 1193 | } |