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 gsize 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 (&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 ((&static_g_define_type_id ), (gsize) (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"); union { char *in; gpointer *out; } _pp; gpointer _p; GDestroyNotify _destroy = (GDestroyNotify) (g_object_unref); _pp.in = (char *) ((&stream->priv->source_buffer)); _p = *_pp.out ; if (_p) { *_pp.out = ((void*)0); _destroy (_p); } } while ( 0); | |||
168 | g_clear_object (&stream->priv->charset_conv)do { _Static_assert (sizeof *((&stream->priv->charset_conv )) == sizeof (gpointer), "Expression evaluates to false"); union { char *in; gpointer *out; } _pp; gpointer _p; GDestroyNotify _destroy = (GDestroyNotify) (g_object_unref); _pp.in = (char *) ((&stream->priv->charset_conv)); _p = *_pp.out; if (_p) { *_pp.out = ((void*)0); _destroy (_p); } } 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"); union { char *in; gpointer *out; } _pp; gpointer _p; GDestroyNotify _destroy = (GDestroyNotify ) (g_object_unref); _pp.in = (char *) ((&conv)); _p = *_pp .out; if (_p) { *_pp.out = ((void*)0); _destroy (_p); } } 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"); union { char *in; gpointer *out; } _pp; gpointer _p; GDestroyNotify _destroy = (GDestroyNotify) (g_object_unref); _pp.in = (char *) ((&ostream->priv->charset_conv)); _p = *_pp.out ; if (_p) { *_pp.out = ((void*)0); _destroy (_p); } } 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 | } |