Bug Summary

File:ctk/ctktextbufferserialize.c
Warning:line 453, column 22
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'active_tags')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ctktextbufferserialize.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/rootdir/ctk -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAVE_CONFIG_H -I . -I .. -D G_LOG_DOMAIN="Ctk" -D G_LOG_USE_STRUCTURED=1 -D CTK_VERSION="3.25.5" -D CTK_BINARY_VERSION="3.0.0" -D CTK_COMPILATION -D CTK_PRINT_BACKEND_ENABLE_UNSUPPORTED -D CTK_LIBDIR="/usr/lib" -D CTK_LOCALEDIR="/usr/share/locale" -D CTK_DATADIR="/usr/share" -D CTK_DATA_PREFIX="/usr" -D CTK_SYSCONFDIR="/usr/etc" -D CTK_HOST="x86_64-pc-linux-gnu" -D CTK_PRINT_BACKENDS="file,cups" -D X11_DATA_PREFIX="/usr" -D ISO_CODES_PREFIX="" -I .. -I ../ctk -I .. -I ../cdk -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D G_ENABLE_DEBUG -D G_ENABLE_CONSISTENCY_CHECKS -D GLIB_MIN_REQUIRED_VERSION=GLIB_VERSION_2_66 -D GLIB_MAX_ALLOWED_VERSION=GLIB_VERSION_2_66 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/atk-1.0 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gio-unix-2.0 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/pango-1.0 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -D PIC -internal-isystem /usr/lib/llvm-16/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir=/rootdir/ctk -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2024-09-19-170037-43636-1 -x c ctktextbufferserialize.c
1/* ctktextbufferserialize.c
2 *
3 * Copyright (C) 2001 Havoc Pennington
4 * Copyright (C) 2004 Nokia Corporation
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/* FIXME: We should use other error codes for the
21 * parts that deal with the format errors
22 */
23
24#include "config.h"
25
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <errno(*__errno_location ()).h>
30
31#include "gdk-pixbuf/gdk-pixdata.h"
32#include "ctktextbufferserialize.h"
33#include "ctktexttagprivate.h"
34#include "ctkintl.h"
35
36
37typedef struct
38{
39 GString *tag_table_str;
40 GString *text_str;
41 GHashTable *tags;
42 CtkTextIter start, end;
43
44 gint n_pixbufs;
45 GList *pixbufs;
46 gint tag_id;
47 GHashTable *tag_id_tags;
48} SerializationContext;
49
50static gchar *
51serialize_value (GValue *value)
52{
53G_GNUC_BEGIN_IGNORE_DEPRECATIONSclang diagnostic push clang diagnostic ignored "-Wdeprecated-declarations"
54 if (g_value_type_transformable (value->g_type, G_TYPE_STRING((GType) ((16) << (2)))))
55 {
56 GValue text_value = G_VALUE_INIT{ 0, { { 0 } } };
57 gchar *tmp;
58
59 g_value_init (&text_value, G_TYPE_STRING((GType) ((16) << (2))));
60 g_value_transform (value, &text_value);
61
62 tmp = g_markup_escape_text (g_value_get_string (&text_value), -1);
63 g_value_unset (&text_value);
64
65 return tmp;
66 }
67 else if (value->g_type == CDK_TYPE_COLOR(cdk_color_get_type ()))
68 {
69 CdkColor *color = g_value_get_boxed (value);
70
71 return g_strdup_printf ("%x:%x:%x", color->red, color->green, color->blue);
72 }
73 else
74 {
75 g_warning ("Type %s is not serializable", g_type_name (value->g_type));
76 }
77G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop
78
79 return NULL((void*)0);
80}
81
82static gboolean
83deserialize_value (const gchar *str,
84 GValue *value)
85{
86G_GNUC_BEGIN_IGNORE_DEPRECATIONSclang diagnostic push clang diagnostic ignored "-Wdeprecated-declarations"
87 if (g_value_type_transformable (G_TYPE_STRING((GType) ((16) << (2))), value->g_type))
88 {
89 GValue text_value = G_VALUE_INIT{ 0, { { 0 } } };
90 gboolean retval;
91
92 g_value_init (&text_value, G_TYPE_STRING((GType) ((16) << (2))));
93 g_value_set_static_string (&text_value, str);
94
95 retval = g_value_transform (&text_value, value);
96 g_value_unset (&text_value);
97
98 return retval;
99 }
100 else if (value->g_type == G_TYPE_BOOLEAN((GType) ((5) << (2))))
101 {
102 gboolean v;
103
104 v = strcmp (str, "TRUE") == 0;
105
106 g_value_set_boolean (value, v);
107
108 return TRUE(!(0));
109 }
110 else if (value->g_type == G_TYPE_INT((GType) ((6) << (2))))
111 {
112 gchar *tmp;
113 int v;
114
115 errno(*__errno_location ()) = 0;
116 v = g_ascii_strtoll (str, &tmp, 10);
117
118 if (errno(*__errno_location ()) || tmp == NULL((void*)0) || tmp == str)
119 return FALSE(0);
120
121 g_value_set_int (value, v);
122
123 return TRUE(!(0));
124 }
125 else if (value->g_type == G_TYPE_DOUBLE((GType) ((15) << (2))))
126 {
127 gchar *tmp;
128 gdouble v;
129
130 v = g_ascii_strtod (str, &tmp);
131
132 if (tmp == NULL((void*)0) || tmp == str)
133 return FALSE(0);
134
135 g_value_set_double (value, v);
136
137 return TRUE(!(0));
138 }
139 else if (value->g_type == CDK_TYPE_COLOR(cdk_color_get_type ()))
140 {
141 CdkColor color;
142 const gchar *old;
143 gchar *tmp;
144
145 old = str;
146 tmp = NULL((void*)0);
147 errno(*__errno_location ()) = 0;
148 color.red = g_ascii_strtoll (old, &tmp, 16);
149
150 if (errno(*__errno_location ()) || tmp == old)
151 return FALSE(0);
152
153 old = tmp;
154 if (*old++ != ':')
155 return FALSE(0);
156
157 tmp = NULL((void*)0);
158 errno(*__errno_location ()) = 0;
159 color.green = g_ascii_strtoll (old, &tmp, 16);
160 if (errno(*__errno_location ()) || tmp == old)
161 return FALSE(0);
162
163 old = tmp;
164 if (*old++ != ':')
165 return FALSE(0);
166
167 tmp = NULL((void*)0);
168 errno(*__errno_location ()) = 0;
169 color.blue = g_ascii_strtoll (old, &tmp, 16);
170
171 if (errno(*__errno_location ()) || tmp == old || *tmp != '\0')
172 return FALSE(0);
173
174 g_value_set_boxed (value, &color);
175
176 return TRUE(!(0));
177 }
178 else if (G_VALUE_HOLDS_ENUM (value)(((__extension__ ({ const GValue *__val = (const GValue*) ((value
)); GType __t = (((GType) ((12) << (2)))); gboolean __r
; if (!__val) __r = (0); else if (__val->g_type == __t) __r
= (!(0)); else __r = g_type_check_value_holds (__val, __t); __r
; }))))
)
179 {
180 GEnumClass *class = G_ENUM_CLASS (g_type_class_peek (value->g_type))((((GEnumClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((g_type_class_peek (value->g_type))), (((GType) ((12) <<
(2))))))))
;
181 GEnumValue *enum_value;
182
183 enum_value = g_enum_get_value_by_name (class, str);
184
185 if (enum_value)
186 {
187 g_value_set_enum (value, enum_value->value);
188 return TRUE(!(0));
189 }
190
191 return FALSE(0);
192 }
193 else
194 {
195 g_warning ("Type %s can not be deserialized", g_type_name (value->g_type));
196 }
197G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop
198
199 return FALSE(0);
200}
201
202/* Checks if a param is set, or if it's the default value */
203static gboolean
204is_param_set (GObject *object,
205 GParamSpec *pspec,
206 GValue *value)
207{
208 /* We need to special case some attributes here */
209 if (strcmp (pspec->name, "background-cdk") == 0)
210 {
211 gboolean is_set;
212
213 g_object_get (object, "background-set", &is_set, NULL((void*)0));
214
215 if (is_set)
216 {
217 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)(((((GParamSpec*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((pspec)), (((GType) ((19) << (2))))))))->value_type
)
);
218
219 g_object_get_property (object, pspec->name, value);
220
221 return TRUE(!(0));
222 }
223
224 return FALSE(0);
225 }
226 else if (strcmp (pspec->name, "foreground-cdk") == 0)
227 {
228 gboolean is_set;
229
230 g_object_get (object, "foreground-set", &is_set, NULL((void*)0));
231
232 if (is_set)
233 {
234 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)(((((GParamSpec*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((pspec)), (((GType) ((19) << (2))))))))->value_type
)
);
235
236 g_object_get_property (object, pspec->name, value);
237
238 return TRUE(!(0));
239 }
240
241 return FALSE(0);
242 }
243 else
244 {
245 gboolean is_set;
246 gchar *is_set_name;
247
248 is_set_name = g_strdup_printf ("%s-set", pspec->name);
249
250 if (g_object_class_find_property (G_OBJECT_GET_CLASS (object)((((GObjectClass*) (((GTypeInstance*) ((object)))->g_class
))))
, is_set_name) == NULL((void*)0))
251 {
252 g_free (is_set_name);
253 return FALSE(0);
254 }
255 else
256 {
257 g_object_get (object, is_set_name, &is_set, NULL((void*)0));
258
259 if (!is_set)
260 {
261 g_free (is_set_name);
262 return FALSE(0);
263 }
264
265 g_free (is_set_name);
266
267 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)(((((GParamSpec*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((pspec)), (((GType) ((19) << (2))))))))->value_type
)
);
268
269 g_object_get_property (object, pspec->name, value);
270
271 if (g_param_value_defaults (pspec, value))
272 {
273 g_value_unset (value);
274
275 return FALSE(0);
276 }
277 }
278 return TRUE(!(0));
279 }
280}
281
282static void
283serialize_tag (gpointer key G_GNUC_UNUSED__attribute__ ((__unused__)),
284 gpointer data,
285 gpointer user_data)
286{
287 SerializationContext *context = user_data;
288 CtkTextTag *tag = data;
289 gchar *tag_name;
290 gint tag_id;
291 GParamSpec **pspecs;
292 guint n_pspecs;
293 int i;
294
295 g_string_append (context->tag_table_str, " <tag ")(__builtin_constant_p (" <tag ") ? __extension__ ({ const
char * const __val = (" <tag "); g_string_append_len_inline
(context->tag_table_str, __val, (__val != ((void*)0)) ? (
gssize) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->tag_table_str, " <tag ", (gssize) -1))
;
296
297 /* Handle anonymous tags */
298 if (tag->priv->name)
299 {
300 tag_name = g_markup_escape_text (tag->priv->name, -1);
301 g_string_append_printf (context->tag_table_str, "name=\"%s\"", tag_name);
302 g_free (tag_name);
303 }
304 else
305 {
306 tag_id = GPOINTER_TO_INT (g_hash_table_lookup (context->tag_id_tags, tag))((gint) (glong) (g_hash_table_lookup (context->tag_id_tags
, tag)))
;
307
308 g_string_append_printf (context->tag_table_str, "id=\"%d\"", tag_id);
309 }
310
311 g_string_append_printf (context->tag_table_str, " priority=\"%d\">\n", tag->priv->priority);
312
313 /* Serialize properties */
314 pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (tag)((((GObjectClass*) (((GTypeInstance*) ((tag)))->g_class)))
)
, &n_pspecs);
315
316 for (i = 0; i < n_pspecs; i++)
317 {
318 GValue value = G_VALUE_INIT{ 0, { { 0 } } };
319 gchar *tmp, *tmp2;
320
321 if (!(pspecs[i]->flags & G_PARAM_READABLE) ||
322 !(pspecs[i]->flags & G_PARAM_WRITABLE))
323 continue;
324
325 if (!is_param_set (G_OBJECT (tag)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((tag)), (((GType) ((20) << (2))))))))
, pspecs[i], &value))
326 continue;
327
328 /* Now serialize the attr */
329 tmp2 = serialize_value (&value);
330
331 if (tmp2)
332 {
333 tmp = g_markup_escape_text (pspecs[i]->name, -1);
334 g_string_append_printf (context->tag_table_str, " <attr name=\"%s\" ", tmp);
335 g_free (tmp);
336
337 tmp = g_markup_escape_text (g_type_name (pspecs[i]->value_type), -1);
338 g_string_append_printf (context->tag_table_str, "type=\"%s\" value=\"%s\" />\n", tmp, tmp2);
339
340 g_free (tmp);
341 g_free (tmp2);
342 }
343
344 g_value_unset (&value);
345 }
346
347 g_free (pspecs);
348
349 g_string_append (context->tag_table_str, " </tag>\n")(__builtin_constant_p (" </tag>\n") ? __extension__ ({
const char * const __val = (" </tag>\n"); g_string_append_len_inline
(context->tag_table_str, __val, (__val != ((void*)0)) ? (
gssize) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->tag_table_str, " </tag>\n", (gssize) -1)
)
;
350}
351
352static void
353serialize_tags (SerializationContext *context)
354{
355 g_string_append (context->tag_table_str, " <text_view_markup>\n")(__builtin_constant_p (" <text_view_markup>\n") ? __extension__
({ const char * const __val = (" <text_view_markup>\n"
); g_string_append_len_inline (context->tag_table_str, __val
, (__val != ((void*)0)) ? (gssize) strlen (((__val) + !(__val
))) : (gssize) -1); }) : g_string_append_len_inline (context->
tag_table_str, " <text_view_markup>\n", (gssize) -1))
;
356 g_string_append (context->tag_table_str, " <tags>\n")(__builtin_constant_p (" <tags>\n") ? __extension__ ({ const
char * const __val = (" <tags>\n"); g_string_append_len_inline
(context->tag_table_str, __val, (__val != ((void*)0)) ? (
gssize) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->tag_table_str, " <tags>\n", (gssize) -1))
;
357 g_hash_table_foreach (context->tags, serialize_tag, context);
358 g_string_append (context->tag_table_str, " </tags>\n")(__builtin_constant_p (" </tags>\n") ? __extension__ ({
const char * const __val = (" </tags>\n"); g_string_append_len_inline
(context->tag_table_str, __val, (__val != ((void*)0)) ? (
gssize) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->tag_table_str, " </tags>\n", (gssize) -1)
)
;
359}
360
361static void
362find_list_delta (GSList *old_list,
363 GSList *new_list,
364 GList **added,
365 GList **removed)
366{
367 GSList *tmp;
368 GList *tmp_added, *tmp_removed;
369
370 tmp_added = NULL((void*)0);
371 tmp_removed = NULL((void*)0);
372
373 /* Find added tags */
374 tmp = new_list;
375 while (tmp)
376 {
377 if (!g_slist_find (old_list, tmp->data))
378 tmp_added = g_list_prepend (tmp_added, tmp->data);
379
380 tmp = tmp->next;
381 }
382
383 *added = tmp_added;
384
385 /* Find removed tags */
386 tmp = old_list;
387 while (tmp)
388 {
389 if (!g_slist_find (new_list, tmp->data))
390 tmp_removed = g_list_prepend (tmp_removed, tmp->data);
391
392 tmp = tmp->next;
393 }
394
395 /* We reverse the list here to match the xml semantics */
396 *removed = g_list_reverse (tmp_removed);
397}
398
399static void
400serialize_section_header (GString *str,
401 const gchar *name,
402 gint length)
403{
404 g_return_if_fail (strlen (name) == 26)do { if ((strlen (name) == 26)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "strlen (name) == 26"); return
; } } while (0)
;
405
406 g_string_append (str, name)(__builtin_constant_p (name) ? __extension__ ({ const char * const
__val = (name); g_string_append_len_inline (str, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (str, name, (gssize) -
1))
;
407
408 g_string_append_c (str, length >> 24)g_string_append_c_inline (str, length >> 24);
409
410 g_string_append_c (str, (length >> 16) & 0xff)g_string_append_c_inline (str, (length >> 16) & 0xff
)
;
411 g_string_append_c (str, (length >> 8) & 0xff)g_string_append_c_inline (str, (length >> 8) & 0xff
)
;
412 g_string_append_c (str, length & 0xff)g_string_append_c_inline (str, length & 0xff);
413}
414
415static void
416serialize_text (CtkTextBuffer *buffer G_GNUC_UNUSED__attribute__ ((__unused__)),
417 SerializationContext *context)
418{
419 CtkTextIter iter, old_iter;
420 GSList *tag_list, *new_tag_list;
421 GSList *active_tags;
422
423 g_string_append (context->text_str, "<text>")(__builtin_constant_p ("<text>") ? __extension__ ({ const
char * const __val = ("<text>"); g_string_append_len_inline
(context->text_str, __val, (__val != ((void*)0)) ? (gssize
) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->text_str, "<text>", (gssize) -1))
;
2
'?' condition is true
3
'?' condition is true
424
425 iter = context->start;
426 tag_list = NULL((void*)0);
427 active_tags = NULL((void*)0);
4
Null pointer value stored to 'active_tags'
428
429 do
430 {
431 GList *added, *removed;
432 GList *tmp;
433 gchar *tmp_text, *escaped_text;
434
435 new_tag_list = ctk_text_iter_get_tags (&iter);
436 find_list_delta (tag_list, new_tag_list, &added, &removed);
437
438 /* Handle removed tags */
439 for (tmp = removed; tmp; tmp = tmp->next)
5
Loop condition is true. Entering loop body
440 {
441 CtkTextTag *tag = tmp->data;
442
443 /* Only close the tag if we didn't close it before (by using
444 * the stack logic in the while() loop below)
445 */
446 if (g_slist_find (active_tags, tag))
6
Assuming the condition is true
7
Taking true branch
447 {
448 g_string_append (context->text_str, "</apply_tag>")(__builtin_constant_p ("</apply_tag>") ? __extension__ (
{ const char * const __val = ("</apply_tag>"); g_string_append_len_inline
(context->text_str, __val, (__val != ((void*)0)) ? (gssize
) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->text_str, "</apply_tag>", (gssize) -1))
;
8
'?' condition is true
9
'?' condition is true
449
450 /* Drop all tags that were opened after this one (which are
451 * above this on in the stack)
452 */
453 while (active_tags->data != tag)
10
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'active_tags')
454 {
455 added = g_list_prepend (added, active_tags->data);
456 active_tags = g_slist_remove (active_tags, active_tags->data);
457 g_string_append_printf (context->text_str, "</apply_tag>");
458 }
459
460 active_tags = g_slist_remove (active_tags, active_tags->data);
461 }
462 }
463
464 /* Handle added tags */
465 for (tmp = added; tmp; tmp = tmp->next)
466 {
467 CtkTextTag *tag = tmp->data;
468 gchar *tag_name;
469
470 /* Add it to the tag hash table */
471 g_hash_table_insert (context->tags, tag, tag);
472
473 if (tag->priv->name)
474 {
475 tag_name = g_markup_escape_text (tag->priv->name, -1);
476
477 g_string_append_printf (context->text_str, "<apply_tag name=\"%s\">", tag_name);
478 g_free (tag_name);
479 }
480 else
481 {
482 gpointer tag_id;
483
484 /* We've got an anonymous tag, find out if it's been
485 used before */
486 if (!g_hash_table_lookup_extended (context->tag_id_tags, tag, NULL((void*)0), &tag_id))
487 {
488 tag_id = GINT_TO_POINTER (context->tag_id++)((gpointer) (glong) (context->tag_id++));
489
490 g_hash_table_insert (context->tag_id_tags, tag, tag_id);
491 }
492
493 g_string_append_printf (context->text_str, "<apply_tag id=\"%d\">", GPOINTER_TO_INT (tag_id)((gint) (glong) (tag_id)));
494 }
495
496 active_tags = g_slist_prepend (active_tags, tag);
497 }
498
499 g_slist_free (tag_list);
500 tag_list = new_tag_list;
501
502 g_list_free (added);
503 g_list_free (removed);
504
505 old_iter = iter;
506
507 /* Now try to go to either the next tag toggle, or if a pixbuf appears */
508 while (TRUE(!(0)))
509 {
510 gunichar ch = ctk_text_iter_get_char (&iter);
511
512 if (ch == 0xFFFC)
513 {
514 GdkPixbuf *pixbuf = ctk_text_iter_get_pixbuf (&iter);
515
516 if (pixbuf)
517 {
518 /* Append the text before the pixbuf */
519 tmp_text = ctk_text_iter_get_slice (&old_iter, &iter);
520 escaped_text = g_markup_escape_text (tmp_text, -1);
521 g_free (tmp_text);
522
523 /* Forward so we don't get the 0xfffc char */
524 ctk_text_iter_forward_char (&iter);
525 old_iter = iter;
526
527 g_string_append (context->text_str, escaped_text)(__builtin_constant_p (escaped_text) ? __extension__ ({ const
char * const __val = (escaped_text); g_string_append_len_inline
(context->text_str, __val, (__val != ((void*)0)) ? (gssize
) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->text_str, escaped_text, (gssize) -1))
;
528 g_free (escaped_text);
529
530 g_string_append_printf (context->text_str, "<pixbuf index=\"%d\" />", context->n_pixbufs);
531
532 context->n_pixbufs++;
533 context->pixbufs = g_list_prepend (context->pixbufs, pixbuf);
534 }
535 }
536 else if (ch == 0)
537 {
538 break;
539 }
540 else
541 ctk_text_iter_forward_char (&iter);
542
543 if (ctk_text_iter_toggles_tag (&iter, NULL((void*)0)))
544 break;
545 }
546
547 /* We might have moved too far */
548 if (ctk_text_iter_compare (&iter, &context->end) > 0)
549 iter = context->end;
550
551 /* Append the text */
552 tmp_text = ctk_text_iter_get_slice (&old_iter, &iter);
553 escaped_text = g_markup_escape_text (tmp_text, -1);
554 g_free (tmp_text);
555
556 g_string_append (context->text_str, escaped_text)(__builtin_constant_p (escaped_text) ? __extension__ ({ const
char * const __val = (escaped_text); g_string_append_len_inline
(context->text_str, __val, (__val != ((void*)0)) ? (gssize
) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->text_str, escaped_text, (gssize) -1))
;
557 g_free (escaped_text);
558 }
559 while (!ctk_text_iter_equal (&iter, &context->end));
560
561 g_slist_free (tag_list);
562
563 /* Close any open tags */
564 for (tag_list = active_tags; tag_list; tag_list = tag_list->next)
565 g_string_append (context->text_str, "</apply_tag>")(__builtin_constant_p ("</apply_tag>") ? __extension__ (
{ const char * const __val = ("</apply_tag>"); g_string_append_len_inline
(context->text_str, __val, (__val != ((void*)0)) ? (gssize
) strlen (((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(context->text_str, "</apply_tag>", (gssize) -1))
;
566
567 g_slist_free (active_tags);
568 g_string_append (context->text_str, "</text>\n</text_view_markup>\n")(__builtin_constant_p ("</text>\n</text_view_markup>\n"
) ? __extension__ ({ const char * const __val = ("</text>\n</text_view_markup>\n"
); g_string_append_len_inline (context->text_str, __val, (
__val != ((void*)0)) ? (gssize) strlen (((__val) + !(__val)))
: (gssize) -1); }) : g_string_append_len_inline (context->
text_str, "</text>\n</text_view_markup>\n", (gssize
) -1))
;
569}
570
571G_GNUC_BEGIN_IGNORE_DEPRECATIONSclang diagnostic push clang diagnostic ignored "-Wdeprecated-declarations"
572static void
573serialize_pixbufs (SerializationContext *context,
574 GString *text)
575{
576 GList *list;
577
578 for (list = context->pixbufs; list != NULL((void*)0); list = list->next)
579 {
580 GdkPixbuf *pixbuf = list->data;
581 GdkPixdata pixdata;
582 guint8 *tmp;
583 guint len;
584
585 gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE(0));
586 tmp = gdk_pixdata_serialize (&pixdata, &len);
587
588 serialize_section_header (text, "CTKTEXTBUFFERPIXBDATA-0001", len);
589 g_string_append_len (text, (gchar *) tmp, len)g_string_append_len_inline (text, (gchar *) tmp, len);
590 g_free (tmp);
591 }
592}
593G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop
594
595guint8 *
596_ctk_text_buffer_serialize_rich_text (CtkTextBuffer *register_buffer G_GNUC_UNUSED__attribute__ ((__unused__)),
597 CtkTextBuffer *content_buffer,
598 const CtkTextIter *start,
599 const CtkTextIter *end,
600 gsize *length,
601 gpointer user_data G_GNUC_UNUSED__attribute__ ((__unused__)))
602{
603 SerializationContext context;
604 GString *text;
605
606 context.tags = g_hash_table_new (NULL((void*)0), NULL((void*)0));
607 context.text_str = g_string_new (NULL((void*)0));
608 context.tag_table_str = g_string_new (NULL((void*)0));
609 context.start = *start;
610 context.end = *end;
611 context.n_pixbufs = 0;
612 context.pixbufs = NULL((void*)0);
613 context.tag_id = 0;
614 context.tag_id_tags = g_hash_table_new (NULL((void*)0), NULL((void*)0));
615
616 /* We need to serialize the text before the tag table so we know
617 what tags are used */
618 serialize_text (content_buffer, &context);
1
Calling 'serialize_text'
619 serialize_tags (&context);
620
621 text = g_string_new (NULL((void*)0));
622 serialize_section_header (text, "CTKTEXTBUFFERCONTENTS-0001",
623 context.tag_table_str->len + context.text_str->len);
624
625 g_string_append_len (text, context.tag_table_str->str, context.tag_table_str->len)g_string_append_len_inline (text, context.tag_table_str->str
, context.tag_table_str->len)
;
626 g_string_append_len (text, context.text_str->str, context.text_str->len)g_string_append_len_inline (text, context.text_str->str, context
.text_str->len)
;
627
628 context.pixbufs = g_list_reverse (context.pixbufs);
629 serialize_pixbufs (&context, text);
630
631 g_hash_table_destroy (context.tags);
632 g_list_free (context.pixbufs);
633 g_string_free (context.text_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) (
(context.text_str), ((!(0)))) : g_string_free_and_steal (context
.text_str)) : (g_string_free) ((context.text_str), ((!(0)))))
;
634 g_string_free (context.tag_table_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) (
(context.tag_table_str), ((!(0)))) : g_string_free_and_steal (
context.tag_table_str)) : (g_string_free) ((context.tag_table_str
), ((!(0)))))
;
635 g_hash_table_destroy (context.tag_id_tags);
636
637 *length = text->len;
638
639 return (guint8 *) g_string_free (text, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((text
), ((0))) : g_string_free_and_steal (text)) : (g_string_free)
((text), ((0))))
;
640}
641
642typedef enum
643{
644 STATE_START,
645 STATE_TEXT_VIEW_MARKUP,
646 STATE_TAGS,
647 STATE_TAG,
648 STATE_ATTR,
649 STATE_TEXT,
650 STATE_APPLY_TAG,
651 STATE_PIXBUF
652} ParseState;
653
654typedef struct
655{
656 gchar *text;
657 GdkPixbuf *pixbuf;
658 GSList *tags;
659} TextSpan;
660
661typedef struct
662{
663 CtkTextTag *tag;
664 gint prio;
665} TextTagPrio;
666
667typedef struct
668{
669 GSList *states;
670
671 GList *headers;
672
673 CtkTextBuffer *buffer;
674
675 /* Tags that are defined in <tag> elements */
676 GHashTable *defined_tags;
677
678 /* Tags that are anonymous */
679 GHashTable *anonymous_tags;
680
681 /* Tag name substitutions */
682 GHashTable *substitutions;
683
684 /* Current tag */
685 CtkTextTag *current_tag;
686
687 /* Priority of current tag */
688 gint current_tag_prio;
689
690 /* Id of current tag */
691 gint current_tag_id;
692
693 /* Tags and their priorities */
694 GList *tag_priorities;
695
696 GSList *tag_stack;
697
698 GList *spans;
699
700 gboolean create_tags;
701
702 gboolean parsed_text;
703 gboolean parsed_tags;
704} ParseInfo;
705
706static void
707set_error (GError **err,
708 GMarkupParseContext *context,
709 int error_domain,
710 int error_code,
711 const char *format,
712 ...) G_GNUC_PRINTF(5, 6)__attribute__((__format__ (__printf__, 5, 6)));
713
714static void
715set_error (GError **err,
716 GMarkupParseContext *context,
717 int error_domain,
718 int error_code,
719 const char *format,
720 ...)
721{
722 int line, ch;
723 va_list args;
724 char *str;
725
726 g_markup_parse_context_get_position (context, &line, &ch);
727
728 va_start (args, format)__builtin_va_start(args, format);
729 str = g_strdup_vprintf (format, args);
730 va_end (args)__builtin_va_end(args);
731
732 g_set_error (err, error_domain, error_code,
733 ("Line %d character %d: %s"),
734 line, ch, str);
735
736 g_free (str);
737}
738
739static void
740push_state (ParseInfo *info,
741 ParseState state)
742{
743 info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state)((gpointer) (glong) (state)));
744}
745
746static void
747pop_state (ParseInfo *info)
748{
749 g_return_if_fail (info->states != NULL)do { if ((info->states != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info->states != NULL"
); return; } } while (0)
;
750
751 info->states = g_slist_remove (info->states, info->states->data);
752}
753
754static ParseState
755peek_state (ParseInfo *info)
756{
757 g_return_val_if_fail (info->states != NULL, STATE_START)do { if ((info->states != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info->states != NULL"
); return (STATE_START); } } while (0)
;
758
759 return GPOINTER_TO_INT (info->states->data)((gint) (glong) (info->states->data));
760}
761
762#define ELEMENT_IS(name)(strcmp (element_name, (name)) == 0) (strcmp (element_name, (name)) == 0)
763
764
765static gboolean
766check_id_or_name (GMarkupParseContext *context,
767 const gchar *element_name,
768 const gchar **attribute_names,
769 const gchar **attribute_values,
770 gint *id,
771 const gchar **name,
772 GError **error)
773{
774 gboolean has_id = FALSE(0);
775 gboolean has_name = FALSE(0);
776 int i;
777
778 *id = 0;
779 *name = NULL((void*)0);
780
781 for (i = 0; attribute_names[i] != NULL((void*)0); i++)
782 {
783 if (strcmp (attribute_names[i], "name") == 0)
784 {
785 *name = attribute_values[i];
786
787 if (has_id)
788 {
789 set_error (error, context,
790 G_MARKUP_ERRORg_markup_error_quark (),
791 G_MARKUP_ERROR_PARSE,
792 _("Both \"id\" and \"name\" were found on the <%s> element")((char *) g_dgettext ("ctk30", "Both \"id\" and \"name\" were found on the <%s> element"
))
,
793 element_name);
794 return FALSE(0);
795 }
796
797 if (has_name)
798 {
799 set_error (error, context,
800 G_MARKUP_ERRORg_markup_error_quark (),
801 G_MARKUP_ERROR_PARSE,
802 _("The attribute \"%s\" was found twice on the <%s> element")((char *) g_dgettext ("ctk30", "The attribute \"%s\" was found twice on the <%s> element"
))
,
803 "name", element_name);
804 return FALSE(0);
805 }
806
807 has_name = TRUE(!(0));
808 }
809 else if (strcmp (attribute_names[i], "id") == 0)
810 {
811 gchar *tmp;
812
813 if (has_name)
814 {
815 set_error (error, context,
816 G_MARKUP_ERRORg_markup_error_quark (),
817 G_MARKUP_ERROR_PARSE,
818 _("Both \"id\" and \"name\" were found on the <%s> element")((char *) g_dgettext ("ctk30", "Both \"id\" and \"name\" were found on the <%s> element"
))
,
819 element_name);
820 return FALSE(0);
821 }
822
823 if (has_id)
824 {
825 set_error (error, context,
826 G_MARKUP_ERRORg_markup_error_quark (),
827 G_MARKUP_ERROR_PARSE,
828 _("The attribute \"%s\" was found twice on the <%s> element")((char *) g_dgettext ("ctk30", "The attribute \"%s\" was found twice on the <%s> element"
))
,
829 "id", element_name);
830 return FALSE(0);
831 }
832
833 has_id = TRUE(!(0));
834
835 /* Try parsing the integer */
836 tmp = NULL((void*)0);
837 errno(*__errno_location ()) = 0;
838 *id = g_ascii_strtoll (attribute_values[i], &tmp, 10);
839
840 if (errno(*__errno_location ()) || tmp == attribute_values[i])
841 {
842 set_error (error, context,
843 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
844 _("<%s> element has invalid ID \"%s\"")((char *) g_dgettext ("ctk30", "<%s> element has invalid ID \"%s\""
))
,
845 element_name, attribute_values[i]);
846 return FALSE(0);
847 }
848 }
849 }
850
851 if (!has_id && !has_name)
852 {
853 set_error (error, context,
854 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
855 _("<%s> element has neither a \"name\" nor an \"id\" attribute")((char *) g_dgettext ("ctk30", "<%s> element has neither a \"name\" nor an \"id\" attribute"
))
, element_name);
856 return FALSE(0);
857 }
858
859 return TRUE(!(0));
860}
861
862typedef struct
863{
864 const char *name;
865 const char **retloc;
866} LocateAttr;
867
868static gboolean
869locate_attributes (GMarkupParseContext *context,
870 const char *element_name,
871 const char **attribute_names,
872 const char **attribute_values,
873 gboolean allow_unknown_attrs,
874 GError **error,
875 const char *first_attribute_name,
876 const char **first_attribute_retloc,
877 ...)
878{
879 va_list args;
880 const char *name;
881 const char **retloc;
882 int n_attrs;
883#define MAX_ATTRS24 24
884 LocateAttr attrs[MAX_ATTRS24];
885 gboolean retval;
886 int i;
887
888 g_return_val_if_fail (first_attribute_name != NULL, FALSE)do { if ((first_attribute_name != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "first_attribute_name != NULL"
); return ((0)); } } while (0)
;
889 g_return_val_if_fail (first_attribute_retloc != NULL, FALSE)do { if ((first_attribute_retloc != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "first_attribute_retloc != NULL"
); return ((0)); } } while (0)
;
890
891 retval = TRUE(!(0));
892
893 n_attrs = 1;
894 attrs[0].name = first_attribute_name;
895 attrs[0].retloc = first_attribute_retloc;
896 *first_attribute_retloc = NULL((void*)0);
897
898 va_start (args, first_attribute_retloc)__builtin_va_start(args, first_attribute_retloc);
899
900 name = va_arg (args, const char*)__builtin_va_arg(args, const char*);
901 retloc = va_arg (args, const char**)__builtin_va_arg(args, const char**);
902
903 while (name != NULL((void*)0))
904 {
905 g_return_val_if_fail (retloc != NULL, FALSE)do { if ((retloc != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "retloc != NULL"); return
((0)); } } while (0)
;
906
907 g_assert (n_attrs < MAX_ATTRS)do { if (n_attrs < 24) ; else g_assertion_message_expr ("Ctk"
, "ctktextbufferserialize.c", 907, ((const char*) (__func__))
, "n_attrs < MAX_ATTRS"); } while (0)
;
908
909 attrs[n_attrs].name = name;
910 attrs[n_attrs].retloc = retloc;
911 n_attrs += 1;
912 *retloc = NULL((void*)0);
913
914 name = va_arg (args, const char*)__builtin_va_arg(args, const char*);
915 retloc = va_arg (args, const char**)__builtin_va_arg(args, const char**);
916 }
917
918 va_end (args)__builtin_va_end(args);
919
920 if (!retval)
921 return retval;
922
923 i = 0;
924 while (attribute_names[i])
925 {
926 int j;
927 gboolean found;
928
929 found = FALSE(0);
930 j = 0;
931 while (j < n_attrs)
932 {
933 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
934 {
935 retloc = attrs[j].retloc;
936
937 if (*retloc != NULL((void*)0))
938 {
939 set_error (error, context,
940 G_MARKUP_ERRORg_markup_error_quark (),
941 G_MARKUP_ERROR_PARSE,
942 _("Attribute \"%s\" repeated twice on the same <%s> element")((char *) g_dgettext ("ctk30", "Attribute \"%s\" repeated twice on the same <%s> element"
))
,
943 attrs[j].name, element_name);
944 retval = FALSE(0);
945 goto out;
946 }
947
948 *retloc = attribute_values[i];
949 found = TRUE(!(0));
950 }
951
952 ++j;
953 }
954
955 if (!found && !allow_unknown_attrs)
956 {
957 set_error (error, context,
958 G_MARKUP_ERRORg_markup_error_quark (),
959 G_MARKUP_ERROR_PARSE,
960 _("Attribute \"%s\" is invalid on <%s> element in this context")((char *) g_dgettext ("ctk30", "Attribute \"%s\" is invalid on <%s> element in this context"
))
,
961 attribute_names[i], element_name);
962 retval = FALSE(0);
963 goto out;
964 }
965
966 ++i;
967 }
968
969 out:
970 return retval;
971}
972
973static gboolean
974check_no_attributes (GMarkupParseContext *context,
975 const char *element_name,
976 const char **attribute_names,
977 const char **attribute_values G_GNUC_UNUSED__attribute__ ((__unused__)),
978 GError **error)
979{
980 if (attribute_names[0] != NULL((void*)0))
981 {
982 set_error (error, context,
983 G_MARKUP_ERRORg_markup_error_quark (),
984 G_MARKUP_ERROR_PARSE,
985 _("Attribute \"%s\" is invalid on <%s> element in this context")((char *) g_dgettext ("ctk30", "Attribute \"%s\" is invalid on <%s> element in this context"
))
,
986 attribute_names[0], element_name);
987 return FALSE(0);
988 }
989
990 return TRUE(!(0));
991}
992
993static CtkTextTag *
994tag_exists (GMarkupParseContext *context,
995 const gchar *name,
996 gint id,
997 ParseInfo *info,
998 GError **error)
999{
1000 CtkTextTagTable *tag_table;
1001 const gchar *real_name;
1002
1003 tag_table = ctk_text_buffer_get_tag_table (info->buffer);
1004
1005 if (info->create_tags)
1006 {
1007 /* If we have an anonymous tag, just return it directly */
1008 if (!name)
1009 return g_hash_table_lookup (info->anonymous_tags,
1010 GINT_TO_POINTER (id)((gpointer) (glong) (id)));
1011
1012 /* First, try the substitutions */
1013 real_name = g_hash_table_lookup (info->substitutions, name);
1014
1015 if (real_name)
1016 return ctk_text_tag_table_lookup (tag_table, real_name);
1017
1018 /* Next, try the list of defined tags */
1019 if (g_hash_table_lookup (info->defined_tags, name) != NULL((void*)0))
1020 return ctk_text_tag_table_lookup (tag_table, name);
1021
1022 set_error (error, context,
1023 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1024 _("Tag \"%s\" has not been defined.")((char *) g_dgettext ("ctk30", "Tag \"%s\" has not been defined."
))
, name);
1025
1026 return NULL((void*)0);
1027 }
1028 else
1029 {
1030 CtkTextTag *tag;
1031
1032 if (!name)
1033 {
1034 set_error (error, context,
1035 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1036 _("Anonymous tag found and tags can not be created.")((char *) g_dgettext ("ctk30", "Anonymous tag found and tags can not be created."
))
);
1037 return NULL((void*)0);
1038 }
1039
1040 tag = ctk_text_tag_table_lookup (tag_table, name);
1041
1042 if (tag)
1043 return tag;
1044
1045 set_error (error, context,
1046 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1047 _("Tag \"%s\" does not exist in buffer and tags can not be created.")((char *) g_dgettext ("ctk30", "Tag \"%s\" does not exist in buffer and tags can not be created."
))
, name);
1048
1049 return NULL((void*)0);
1050 }
1051}
1052
1053typedef struct
1054{
1055 const gchar *id;
1056 gint length;
1057 const gchar *start;
1058} Header;
1059
1060G_GNUC_BEGIN_IGNORE_DEPRECATIONSclang diagnostic push clang diagnostic ignored "-Wdeprecated-declarations"
1061static GdkPixbuf *
1062get_pixbuf_from_headers (GList *headers,
1063 int id,
1064 GError **error)
1065{
1066 Header *header;
1067 GdkPixdata pixdata;
1068 GdkPixbuf *pixbuf;
1069
1070 header = g_list_nth_data (headers, id);
1071
1072 if (!header)
1073 return NULL((void*)0);
1074
1075 if (!gdk_pixdata_deserialize (&pixdata, header->length,
1076 (const guint8 *) header->start, error))
1077 return NULL((void*)0);
1078
1079 pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE(!(0)), error);
1080
1081 return pixbuf;
1082}
1083G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop
1084
1085static void
1086parse_apply_tag_element (GMarkupParseContext *context,
1087 const gchar *element_name,
1088 const gchar **attribute_names,
1089 const gchar **attribute_values,
1090 ParseInfo *info,
1091 GError **error)
1092{
1093 const gchar *name, *priority;
1094 gint id;
1095 CtkTextTag *tag;
1096
1097 g_assert (peek_state (info) == STATE_TEXT ||do { if (peek_state (info) == STATE_TEXT || peek_state (info)
== STATE_APPLY_TAG) ; else g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1098, ((const char*) (__func__)), "peek_state (info) == STATE_TEXT || peek_state (info) == STATE_APPLY_TAG"
); } while (0)
1098 peek_state (info) == STATE_APPLY_TAG)do { if (peek_state (info) == STATE_TEXT || peek_state (info)
== STATE_APPLY_TAG) ; else g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1098, ((const char*) (__func__)), "peek_state (info) == STATE_TEXT || peek_state (info) == STATE_APPLY_TAG"
); } while (0)
;
1099
1100 if (ELEMENT_IS ("apply_tag")(strcmp (element_name, ("apply_tag")) == 0))
1101 {
1102 if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE(!(0)), error,
1103 "priority", &priority, NULL((void*)0)))
1104 return;
1105
1106 if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1107 &id, &name, error))
1108 return;
1109
1110
1111 tag = tag_exists (context, name, id, info, error);
1112
1113 if (!tag)
1114 return;
1115
1116 info->tag_stack = g_slist_prepend (info->tag_stack, tag);
1117
1118 push_state (info, STATE_APPLY_TAG);
1119 }
1120 else if (ELEMENT_IS ("pixbuf")(strcmp (element_name, ("pixbuf")) == 0))
1121 {
1122 int int_id;
1123 GdkPixbuf *pixbuf;
1124 TextSpan *span;
1125 const gchar *pixbuf_id;
1126
1127 if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE(0), error,
1128 "index", &pixbuf_id, NULL((void*)0)))
1129 return;
1130
1131 int_id = atoi (pixbuf_id);
1132 pixbuf = get_pixbuf_from_headers (info->headers, int_id, error);
1133
1134 span = g_slice_new0 (TextSpan)((TextSpan*) g_slice_alloc0 (sizeof (TextSpan)));
1135 span->pixbuf = pixbuf;
1136 span->tags = NULL((void*)0);
1137
1138 info->spans = g_list_prepend (info->spans, span);
1139
1140 if (!pixbuf)
1141 return;
1142
1143 push_state (info, STATE_PIXBUF);
1144 }
1145 else
1146 set_error (error, context,
1147 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1148 _("Element <%s> is not allowed below <%s>")((char *) g_dgettext ("ctk30", "Element <%s> is not allowed below <%s>"
))
,
1149 element_name, peek_state(info) == STATE_TEXT ? "text" : "apply_tag");
1150}
1151
1152static void
1153parse_attr_element (GMarkupParseContext *context,
1154 const gchar *element_name,
1155 const gchar **attribute_names,
1156 const gchar **attribute_values,
1157 ParseInfo *info,
1158 GError **error)
1159{
1160 const gchar *name, *type, *value;
1161 GType gtype;
1162 GValue gvalue = G_VALUE_INIT{ 0, { { 0 } } };
1163 GParamSpec *pspec;
1164
1165 g_assert (peek_state (info) == STATE_TAG)do { if (peek_state (info) == STATE_TAG) ; else g_assertion_message_expr
("Ctk", "ctktextbufferserialize.c", 1165, ((const char*) (__func__
)), "peek_state (info) == STATE_TAG"); } while (0)
;
1166
1167 if (ELEMENT_IS ("attr")(strcmp (element_name, ("attr")) == 0))
1168 {
1169 if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE(0), error,
1170 "name", &name, "type", &type, "value", &value, NULL((void*)0)))
1171 return;
1172
1173 gtype = g_type_from_name (type);
1174
1175 if (gtype == G_TYPE_INVALID((GType) ((0) << (2))))
1176 {
1177 set_error (error, context,
1178 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1179 _("\"%s\" is not a valid attribute type")((char *) g_dgettext ("ctk30", "\"%s\" is not a valid attribute type"
))
, type);
1180 return;
1181 }
1182
1183 if (!(pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info->current_tag)((((GObjectClass*) (((GTypeInstance*) ((info->current_tag)
))->g_class))))
, name)))
1184 {
1185 set_error (error, context,
1186 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1187 _("\"%s\" is not a valid attribute name")((char *) g_dgettext ("ctk30", "\"%s\" is not a valid attribute name"
))
, name);
1188 return;
1189 }
1190
1191 g_value_init (&gvalue, gtype);
1192
1193 if (!deserialize_value (value, &gvalue))
1194 {
1195 set_error (error, context,
1196 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1197 _("\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\"")((char *) g_dgettext ("ctk30", "\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\""
))
,
1198 value, type, name);
1199 return;
1200 }
1201
1202 if (g_param_value_validate (pspec, &gvalue))
1203 {
1204 set_error (error, context,
1205 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1206 _("\"%s\" is not a valid value for attribute \"%s\"")((char *) g_dgettext ("ctk30", "\"%s\" is not a valid value for attribute \"%s\""
))
,
1207 value, name);
1208 g_value_unset (&gvalue);
1209 return;
1210 }
1211
1212 g_object_set_property (G_OBJECT (info->current_tag)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((info->current_tag)), (((GType) ((20) << (2)))))
)))
,
1213 name, &gvalue);
1214
1215 g_value_unset (&gvalue);
1216
1217 push_state (info, STATE_ATTR);
1218 }
1219 else
1220 {
1221 set_error (error, context,
1222 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1223 _("Element <%s> is not allowed below <%s>")((char *) g_dgettext ("ctk30", "Element <%s> is not allowed below <%s>"
))
,
1224 element_name, "tag");
1225 }
1226}
1227
1228
1229static gchar *
1230get_tag_name (ParseInfo *info,
1231 const gchar *tag_name)
1232{
1233 CtkTextTagTable *tag_table;
1234 gchar *name;
1235 gint i;
1236
1237 name = g_strdup (tag_name)g_strdup_inline (tag_name);
1238
1239 if (!info->create_tags)
1240 return name;
1241
1242 i = 0;
1243 tag_table = ctk_text_buffer_get_tag_table (info->buffer);
1244
1245 while (ctk_text_tag_table_lookup (tag_table, name) != NULL((void*)0))
1246 {
1247 g_free (name);
1248 name = g_strdup_printf ("%s-%d", tag_name, ++i);
1249 }
1250
1251 if (i != 0)
1252 {
1253 g_hash_table_insert (info->substitutions, g_strdup (tag_name)g_strdup_inline (tag_name), g_strdup (name)g_strdup_inline (name));
1254 }
1255
1256 return name;
1257}
1258
1259static void
1260parse_tag_element (GMarkupParseContext *context,
1261 const gchar *element_name,
1262 const gchar **attribute_names,
1263 const gchar **attribute_values,
1264 ParseInfo *info,
1265 GError **error)
1266{
1267 const gchar *name, *priority;
1268 gchar *tag_name;
1269 gint id;
1270 gint prio;
1271 gchar *tmp;
1272
1273 g_assert (peek_state (info) == STATE_TAGS)do { if (peek_state (info) == STATE_TAGS) ; else g_assertion_message_expr
("Ctk", "ctktextbufferserialize.c", 1273, ((const char*) (__func__
)), "peek_state (info) == STATE_TAGS"); } while (0)
;
1274
1275 if (ELEMENT_IS ("tag")(strcmp (element_name, ("tag")) == 0))
1276 {
1277 if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE(!(0)), error,
1278 "priority", &priority, NULL((void*)0)))
1279 return;
1280
1281 if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1282 &id, &name, error))
1283 return;
1284
1285 if (name)
1286 {
1287 if (g_hash_table_lookup (info->defined_tags, name) != NULL((void*)0))
1288 {
1289 set_error (error, context,
1290 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1291 _("Tag \"%s\" already defined")((char *) g_dgettext ("ctk30", "Tag \"%s\" already defined")), name);
1292 return;
1293 }
1294 }
1295
1296 tmp = NULL((void*)0);
1297 errno(*__errno_location ()) = 0;
1298 prio = g_ascii_strtoll (priority, &tmp, 10);
1299
1300 if (errno(*__errno_location ()) || tmp == priority)
1301 {
1302 set_error (error, context,
1303 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1304 _("Tag \"%s\" has invalid priority \"%s\"")((char *) g_dgettext ("ctk30", "Tag \"%s\" has invalid priority \"%s\""
))
, name, priority);
1305 return;
1306 }
1307
1308 if (name)
1309 {
1310 tag_name = get_tag_name (info, name);
1311 info->current_tag = ctk_text_tag_new (tag_name);
1312 g_free (tag_name);
1313 }
1314 else
1315 {
1316 info->current_tag = ctk_text_tag_new (NULL((void*)0));
1317 info->current_tag_id = id;
1318 }
1319
1320 info->current_tag_prio = prio;
1321
1322 push_state (info, STATE_TAG);
1323 }
1324 else
1325 {
1326 set_error (error, context,
1327 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1328 _("Element <%s> is not allowed below <%s>")((char *) g_dgettext ("ctk30", "Element <%s> is not allowed below <%s>"
))
,
1329 element_name, "tags");
1330 }
1331}
1332
1333static void
1334start_element_handler (GMarkupParseContext *context,
1335 const gchar *element_name,
1336 const gchar **attribute_names,
1337 const gchar **attribute_values,
1338 gpointer user_data,
1339 GError **error)
1340{
1341 ParseInfo *info = user_data;
1342
1343 switch (peek_state (info))
1344 {
1345 case STATE_START:
1346 if (ELEMENT_IS ("text_view_markup")(strcmp (element_name, ("text_view_markup")) == 0))
1347 {
1348 if (!check_no_attributes (context, element_name,
1349 attribute_names, attribute_values, error))
1350 return;
1351
1352 push_state (info, STATE_TEXT_VIEW_MARKUP);
1353 break;
1354 }
1355 else
1356 set_error (error, context, G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1357 _("Outermost element in text must be <text_view_markup> not <%s>")((char *) g_dgettext ("ctk30", "Outermost element in text must be <text_view_markup> not <%s>"
))
,
1358 element_name);
1359 break;
1360 case STATE_TEXT_VIEW_MARKUP:
1361 if (ELEMENT_IS ("tags")(strcmp (element_name, ("tags")) == 0))
1362 {
1363 if (info->parsed_tags)
1364 {
1365 set_error (error, context, G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1366 _("A <%s> element has already been specified")((char *) g_dgettext ("ctk30", "A <%s> element has already been specified"
))
, "tags");
1367 return;
1368 }
1369
1370 if (!check_no_attributes (context, element_name,
1371 attribute_names, attribute_values, error))
1372 return;
1373
1374 push_state (info, STATE_TAGS);
1375 break;
1376 }
1377 else if (ELEMENT_IS ("text")(strcmp (element_name, ("text")) == 0))
1378 {
1379 if (info->parsed_text)
1380 {
1381 set_error (error, context, G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1382 _("A <%s> element has already been specified")((char *) g_dgettext ("ctk30", "A <%s> element has already been specified"
))
, "text");
1383 return;
1384 }
1385 else if (!info->parsed_tags)
1386 {
1387 set_error (error, context, G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1388 _("A <text> element can't occur before a <tags> element")((char *) g_dgettext ("ctk30", "A <text> element can't occur before a <tags> element"
))
);
1389 return;
1390 }
1391
1392 if (!check_no_attributes (context, element_name,
1393 attribute_names, attribute_values, error))
1394 return;
1395
1396 push_state (info, STATE_TEXT);
1397 break;
1398 }
1399 else
1400 set_error (error, context,
1401 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1402 _("Element <%s> is not allowed below <%s>")((char *) g_dgettext ("ctk30", "Element <%s> is not allowed below <%s>"
))
,
1403 element_name, "text_view_markup");
1404 break;
1405 case STATE_TAGS:
1406 parse_tag_element (context, element_name,
1407 attribute_names, attribute_values,
1408 info, error);
1409 break;
1410 case STATE_TAG:
1411 parse_attr_element (context, element_name,
1412 attribute_names, attribute_values,
1413 info, error);
1414 break;
1415 case STATE_TEXT:
1416 case STATE_APPLY_TAG:
1417 parse_apply_tag_element (context, element_name,
1418 attribute_names, attribute_values,
1419 info, error);
1420 break;
1421 default:
1422 g_assert_not_reached ()do { g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1422, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1423 break;
1424 }
1425}
1426
1427static gint
1428sort_tag_prio (TextTagPrio *a,
1429 TextTagPrio *b)
1430{
1431 if (a->prio < b->prio)
1432 return -1;
1433 else if (a->prio > b->prio)
1434 return 1;
1435 else
1436 return 0;
1437}
1438
1439static void
1440end_element_handler (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
1441 const gchar *element_name G_GNUC_UNUSED__attribute__ ((__unused__)),
1442 gpointer user_data,
1443 GError **error G_GNUC_UNUSED__attribute__ ((__unused__)))
1444{
1445 ParseInfo *info = user_data;
1446 gchar *tmp;
1447 GList *list;
1448
1449 switch (peek_state (info))
1450 {
1451 case STATE_TAGS:
1452 pop_state (info);
1453 g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP)do { if (peek_state (info) == STATE_TEXT_VIEW_MARKUP) ; else g_assertion_message_expr
("Ctk", "ctktextbufferserialize.c", 1453, ((const char*) (__func__
)), "peek_state (info) == STATE_TEXT_VIEW_MARKUP"); } while (
0)
;
1454
1455 info->parsed_tags = TRUE(!(0));
1456
1457 /* Sort list and add the tags */
1458 info->tag_priorities = g_list_sort (info->tag_priorities,
1459 (GCompareFunc)sort_tag_prio);
1460 list = info->tag_priorities;
1461 while (list)
1462 {
1463 TextTagPrio *prio = list->data;
1464
1465 if (info->create_tags)
1466 ctk_text_tag_table_add (ctk_text_buffer_get_tag_table (info->buffer),
1467 prio->tag);
1468
1469 g_object_unref (prio->tag);
1470 prio->tag = NULL((void*)0);
1471
1472 list = list->next;
1473 }
1474
1475 break;
1476 case STATE_TAG:
1477 pop_state (info);
1478 g_assert (peek_state (info) == STATE_TAGS)do { if (peek_state (info) == STATE_TAGS) ; else g_assertion_message_expr
("Ctk", "ctktextbufferserialize.c", 1478, ((const char*) (__func__
)), "peek_state (info) == STATE_TAGS"); } while (0)
;
1479
1480 if (info->current_tag->priv->name)
1481 {
1482 /* Add tag to defined tags hash */
1483 tmp = g_strdup (info->current_tag->priv->name)g_strdup_inline (info->current_tag->priv->name);
1484 g_hash_table_insert (info->defined_tags,
1485 tmp, tmp);
1486 }
1487 else
1488 {
1489 g_hash_table_insert (info->anonymous_tags,
1490 GINT_TO_POINTER (info->current_tag_id)((gpointer) (glong) (info->current_tag_id)),
1491 info->current_tag);
1492 }
1493
1494 if (info->create_tags)
1495 {
1496 TextTagPrio *prio;
1497
1498 /* add the tag to the list */
1499 prio = g_slice_new0 (TextTagPrio)((TextTagPrio*) g_slice_alloc0 (sizeof (TextTagPrio)));
1500 prio->prio = info->current_tag_prio;
1501 prio->tag = info->current_tag;
1502
1503 info->tag_priorities = g_list_prepend (info->tag_priorities, prio);
1504 }
1505
1506 info->current_tag = NULL((void*)0);
1507 break;
1508 case STATE_ATTR:
1509 pop_state (info);
1510 g_assert (peek_state (info) == STATE_TAG)do { if (peek_state (info) == STATE_TAG) ; else g_assertion_message_expr
("Ctk", "ctktextbufferserialize.c", 1510, ((const char*) (__func__
)), "peek_state (info) == STATE_TAG"); } while (0)
;
1511 break;
1512 case STATE_APPLY_TAG:
1513 pop_state (info);
1514 g_assert (peek_state (info) == STATE_APPLY_TAG ||do { if (peek_state (info) == STATE_APPLY_TAG || peek_state (
info) == STATE_TEXT) ; else g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1515, ((const char*) (__func__)), "peek_state (info) == STATE_APPLY_TAG || peek_state (info) == STATE_TEXT"
); } while (0)
1515 peek_state (info) == STATE_TEXT)do { if (peek_state (info) == STATE_APPLY_TAG || peek_state (
info) == STATE_TEXT) ; else g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1515, ((const char*) (__func__)), "peek_state (info) == STATE_APPLY_TAG || peek_state (info) == STATE_TEXT"
); } while (0)
;
1516
1517 /* Pop tag */
1518 info->tag_stack = g_slist_delete_link (info->tag_stack,
1519 info->tag_stack);
1520
1521 break;
1522 case STATE_TEXT:
1523 pop_state (info);
1524 g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP)do { if (peek_state (info) == STATE_TEXT_VIEW_MARKUP) ; else g_assertion_message_expr
("Ctk", "ctktextbufferserialize.c", 1524, ((const char*) (__func__
)), "peek_state (info) == STATE_TEXT_VIEW_MARKUP"); } while (
0)
;
1525
1526 info->spans = g_list_reverse (info->spans);
1527 info->parsed_text = TRUE(!(0));
1528 break;
1529 case STATE_TEXT_VIEW_MARKUP:
1530 pop_state (info);
1531 g_assert (peek_state (info) == STATE_START)do { if (peek_state (info) == STATE_START) ; else g_assertion_message_expr
("Ctk", "ctktextbufferserialize.c", 1531, ((const char*) (__func__
)), "peek_state (info) == STATE_START"); } while (0)
;
1532 break;
1533 case STATE_PIXBUF:
1534 pop_state (info);
1535 g_assert (peek_state (info) == STATE_APPLY_TAG ||do { if (peek_state (info) == STATE_APPLY_TAG || peek_state (
info) == STATE_TEXT) ; else g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1536, ((const char*) (__func__)), "peek_state (info) == STATE_APPLY_TAG || peek_state (info) == STATE_TEXT"
); } while (0)
1536 peek_state (info) == STATE_TEXT)do { if (peek_state (info) == STATE_APPLY_TAG || peek_state (
info) == STATE_TEXT) ; else g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1536, ((const char*) (__func__)), "peek_state (info) == STATE_APPLY_TAG || peek_state (info) == STATE_TEXT"
); } while (0)
;
1537 break;
1538 default:
1539 g_assert_not_reached ()do { g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1539, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1540 break;
1541 }
1542}
1543
1544static gboolean
1545all_whitespace (const char *text,
1546 int text_len)
1547{
1548 const char *p;
1549 const char *end;
1550
1551 p = text;
1552 end = text + text_len;
1553
1554 while (p != end)
1555 {
1556 if (!g_ascii_isspace (*p)((g_ascii_table[(guchar) (*p)] & G_ASCII_SPACE) != 0))
1557 return FALSE(0);
1558
1559 p = g_utf8_next_char (p)((p) + g_utf8_skip[*(const guchar *)(p)]);
1560 }
1561
1562 return TRUE(!(0));
1563}
1564
1565static void
1566text_handler (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
1567 const gchar *text,
1568 gsize text_len,
1569 gpointer user_data,
1570 GError **error G_GNUC_UNUSED__attribute__ ((__unused__)))
1571{
1572 ParseInfo *info = user_data;
1573 TextSpan *span;
1574
1575 if (all_whitespace (text, text_len) &&
1576 peek_state (info) != STATE_TEXT &&
1577 peek_state (info) != STATE_APPLY_TAG)
1578 return;
1579
1580 switch (peek_state (info))
1581 {
1582 case STATE_START:
1583 g_assert_not_reached ()do { g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1583, ((const char*) (__func__)), ((void*)0)); } while (0)
; /* gmarkup shouldn't do this */
1584 break;
1585 case STATE_TEXT:
1586 case STATE_APPLY_TAG:
1587 if (text_len == 0)
1588 return;
1589
1590 span = g_slice_new0 (TextSpan)((TextSpan*) g_slice_alloc0 (sizeof (TextSpan)));
1591 span->text = g_strndup (text, text_len);
1592 span->tags = g_slist_copy (info->tag_stack);
1593
1594 info->spans = g_list_prepend (info->spans, span);
1595 break;
1596 default:
1597 g_assert_not_reached ()do { g_assertion_message_expr ("Ctk", "ctktextbufferserialize.c"
, 1597, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1598 break;
1599 }
1600}
1601
1602static void
1603parse_info_init (ParseInfo *info,
1604 CtkTextBuffer *buffer,
1605 gboolean create_tags,
1606 GList *headers)
1607{
1608 info->states = g_slist_prepend (NULL((void*)0), GINT_TO_POINTER (STATE_START)((gpointer) (glong) (STATE_START)));
1609
1610 info->create_tags = create_tags;
1611 info->headers = headers;
1612 info->defined_tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL((void*)0));
1613 info->substitutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1614 info->anonymous_tags = g_hash_table_new_full (NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0));
1615 info->tag_stack = NULL((void*)0);
1616 info->spans = NULL((void*)0);
1617 info->parsed_text = FALSE(0);
1618 info->parsed_tags = FALSE(0);
1619 info->current_tag = NULL((void*)0);
1620 info->current_tag_prio = -1;
1621 info->tag_priorities = NULL((void*)0);
1622
1623 info->buffer = buffer;
1624}
1625
1626static void
1627text_span_free (TextSpan *span)
1628{
1629 g_free (span->text);
1630 g_slist_free (span->tags);
1631 g_slice_free (TextSpan, span)do { if (1) g_slice_free1 (sizeof (TextSpan), (span)); else (
void) ((TextSpan*) 0 == (span)); } while (0)
;
1632}
1633
1634static void
1635parse_info_free (ParseInfo *info)
1636{
1637 GList *list;
1638
1639 g_slist_free (info->tag_stack);
1640 g_slist_free (info->states);
1641
1642 g_hash_table_destroy (info->substitutions);
1643 g_hash_table_destroy (info->defined_tags);
1644
1645 if (info->current_tag)
1646 g_object_unref (info->current_tag);
1647
1648 list = info->spans;
1649 while (list)
1650 {
1651 text_span_free (list->data);
1652
1653 list = list->next;
1654 }
1655 g_list_free (info->spans);
1656
1657 list = info->tag_priorities;
1658 while (list)
1659 {
1660 TextTagPrio *prio = list->data;
1661
1662 if (prio->tag)
1663 g_object_unref (prio->tag);
1664 g_slice_free (TextTagPrio, prio)do { if (1) g_slice_free1 (sizeof (TextTagPrio), (prio)); else
(void) ((TextTagPrio*) 0 == (prio)); } while (0)
;
1665
1666 list = list->next;
1667 }
1668 g_list_free (info->tag_priorities);
1669
1670}
1671
1672static void
1673insert_text (ParseInfo *info,
1674 CtkTextIter *iter)
1675{
1676 CtkTextIter start_iter;
1677 CtkTextMark *mark;
1678 GList *tmp;
1679 GSList *tags;
1680
1681 start_iter = *iter;
1682
1683 mark = ctk_text_buffer_create_mark (info->buffer, "deserialize_insert_point",
1684 &start_iter, TRUE(!(0)));
1685
1686 tmp = info->spans;
1687 while (tmp)
1688 {
1689 TextSpan *span = tmp->data;
1690
1691 if (span->text)
1692 ctk_text_buffer_insert (info->buffer, iter, span->text, -1);
1693 else
1694 {
1695 ctk_text_buffer_insert_pixbuf (info->buffer, iter, span->pixbuf);
1696 g_object_unref (span->pixbuf);
1697 }
1698 ctk_text_buffer_get_iter_at_mark (info->buffer, &start_iter, mark);
1699
1700 /* Apply tags */
1701 tags = span->tags;
1702 while (tags)
1703 {
1704 CtkTextTag *tag = tags->data;
1705
1706 ctk_text_buffer_apply_tag (info->buffer, tag,
1707 &start_iter, iter);
1708
1709 tags = tags->next;
1710 }
1711
1712 ctk_text_buffer_move_mark (info->buffer, mark, iter);
1713
1714 tmp = tmp->next;
1715 }
1716
1717 ctk_text_buffer_delete_mark (info->buffer, mark);
1718}
1719
1720
1721
1722static int
1723read_int (const guchar *start)
1724{
1725 int result;
1726
1727 result =
1728 start[0] << 24 |
1729 start[1] << 16 |
1730 start[2] << 8 |
1731 start[3];
1732
1733 return result;
1734}
1735
1736static gboolean
1737header_is (Header *header,
1738 const gchar *id)
1739{
1740 return (strncmp (header->id, id, strlen (id)) == 0);
1741}
1742
1743static GList *
1744read_headers (const gchar *start,
1745 gint len,
1746 GError **error)
1747{
1748 int i = 0;
1749 int section_len;
1750 Header *header;
1751 GList *headers = NULL((void*)0);
1752 GList *l;
1753
1754 while (i < len)
1755 {
1756 if (i + 30 >= len)
1757 goto error;
1758
1759 if (strncmp (start + i, "CTKTEXTBUFFERCONTENTS-0001", 26) == 0 ||
1760 strncmp (start + i, "CTKTEXTBUFFERPIXBDATA-0001", 26) == 0)
1761 {
1762 section_len = read_int ((const guchar *) start + i + 26);
1763
1764 if (i + 30 + section_len > len)
1765 goto error;
1766
1767 header = g_slice_new0 (Header)((Header*) g_slice_alloc0 (sizeof (Header)));
1768 header->id = start + i;
1769 header->length = section_len;
1770 header->start = start + i + 30;
1771
1772 i += 30 + section_len;
1773
1774 headers = g_list_prepend (headers, header);
1775 }
1776 else
1777 break;
1778 }
1779
1780 return g_list_reverse (headers);
1781
1782 error:
1783 for (l = headers; l != NULL((void*)0); l = l->next)
1784 {
1785 header = l->data;
1786 g_slice_free (Header, header)do { if (1) g_slice_free1 (sizeof (Header), (header)); else (
void) ((Header*) 0 == (header)); } while (0)
;
1787 }
1788
1789 g_list_free (headers);
1790
1791 g_set_error_literal (error,
1792 G_MARKUP_ERRORg_markup_error_quark (),
1793 G_MARKUP_ERROR_PARSE,
1794 _("Serialized data is malformed")((char *) g_dgettext ("ctk30", "Serialized data is malformed"
))
);
1795
1796 return NULL((void*)0);
1797}
1798
1799static gboolean
1800deserialize_text (CtkTextBuffer *buffer,
1801 CtkTextIter *iter,
1802 const gchar *text,
1803 gint len,
1804 gboolean create_tags,
1805 GError **error,
1806 GList *headers)
1807{
1808 GMarkupParseContext *context;
1809 ParseInfo info;
1810 gboolean retval = FALSE(0);
1811
1812 static const GMarkupParser rich_text_parser = {
1813 start_element_handler,
1814 end_element_handler,
1815 text_handler,
1816 NULL((void*)0),
1817 NULL((void*)0)
1818 };
1819
1820 parse_info_init (&info, buffer, create_tags, headers);
1821
1822 context = g_markup_parse_context_new (&rich_text_parser,
1823 0, &info, NULL((void*)0));
1824
1825 if (!g_markup_parse_context_parse (context,
1826 text,
1827 len,
1828 error))
1829 goto out;
1830
1831 if (!g_markup_parse_context_end_parse (context, error))
1832 goto out;
1833
1834 retval = TRUE(!(0));
1835
1836 /* Now insert the text */
1837 insert_text (&info, iter);
1838
1839 out:
1840 parse_info_free (&info);
1841
1842 g_markup_parse_context_free (context);
1843
1844 return retval;
1845}
1846
1847gboolean
1848_ctk_text_buffer_deserialize_rich_text (CtkTextBuffer *register_buffer G_GNUC_UNUSED__attribute__ ((__unused__)),
1849 CtkTextBuffer *content_buffer,
1850 CtkTextIter *iter,
1851 const guint8 *text,
1852 gsize length,
1853 gboolean create_tags,
1854 gpointer user_data G_GNUC_UNUSED__attribute__ ((__unused__)),
1855 GError **error)
1856{
1857 GList *headers;
1858 GList *l;
1859 Header *header;
1860 gboolean retval;
1861
1862 headers = read_headers ((gchar *) text, length, error);
1863
1864 if (!headers)
1865 return FALSE(0);
1866
1867 header = headers->data;
1868 if (!header_is (header, "CTKTEXTBUFFERCONTENTS-0001"))
1869 {
1870 g_set_error_literal (error,
1871 G_MARKUP_ERRORg_markup_error_quark (),
1872 G_MARKUP_ERROR_PARSE,
1873 _("Serialized data is malformed. First section isn't CTKTEXTBUFFERCONTENTS-0001")((char *) g_dgettext ("ctk30", "Serialized data is malformed. First section isn't CTKTEXTBUFFERCONTENTS-0001"
))
);
1874
1875 retval = FALSE(0);
1876 goto out;
1877 }
1878
1879 retval = deserialize_text (content_buffer, iter,
1880 header->start, header->length,
1881 create_tags, error, headers->next);
1882
1883 out:
1884 for (l = headers; l != NULL((void*)0); l = l->next)
1885 {
1886 header = l->data;
1887 g_slice_free (Header, header)do { if (1) g_slice_free1 (sizeof (Header), (header)); else (
void) ((Header*) 0 == (header)); } while (0)
;
1888 }
1889
1890 g_list_free (headers);
1891
1892 return retval;
1893}