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') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
37 | typedef 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 | ||||
50 | static gchar * | |||
51 | serialize_value (GValue *value) | |||
52 | { | |||
53 | G_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 | } | |||
77 | G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop | |||
78 | ||||
79 | return NULL((void*)0); | |||
80 | } | |||
81 | ||||
82 | static gboolean | |||
83 | deserialize_value (const gchar *str, | |||
84 | GValue *value) | |||
85 | { | |||
86 | G_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 | } | |||
197 | G_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 */ | |||
203 | static gboolean | |||
204 | is_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 | ||||
282 | static void | |||
283 | serialize_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 | ||||
352 | static void | |||
353 | serialize_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 | ||||
361 | static void | |||
362 | find_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 | ||||
399 | static void | |||
400 | serialize_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 | ||||
415 | static void | |||
416 | serialize_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)); | |||
424 | ||||
425 | iter = context->start; | |||
426 | tag_list = NULL((void*)0); | |||
427 | active_tags = NULL((void*)0); | |||
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) | |||
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)) | |||
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)); | |||
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) | |||
| ||||
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 | ||||
571 | G_GNUC_BEGIN_IGNORE_DEPRECATIONSclang diagnostic push
clang diagnostic ignored "-Wdeprecated-declarations" | |||
572 | static void | |||
573 | serialize_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 | } | |||
593 | G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop | |||
594 | ||||
595 | guint8 * | |||
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); | |||
| ||||
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 | ||||
642 | typedef 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 | ||||
654 | typedef struct | |||
655 | { | |||
656 | gchar *text; | |||
657 | GdkPixbuf *pixbuf; | |||
658 | GSList *tags; | |||
659 | } TextSpan; | |||
660 | ||||
661 | typedef struct | |||
662 | { | |||
663 | CtkTextTag *tag; | |||
664 | gint prio; | |||
665 | } TextTagPrio; | |||
666 | ||||
667 | typedef 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 | ||||
706 | static void | |||
707 | set_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 | ||||
714 | static void | |||
715 | set_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 | ||||
739 | static void | |||
740 | push_state (ParseInfo *info, | |||
741 | ParseState state) | |||
742 | { | |||
743 | info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state)((gpointer) (glong) (state))); | |||
744 | } | |||
745 | ||||
746 | static void | |||
747 | pop_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 | ||||
754 | static ParseState | |||
755 | peek_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 | ||||
765 | static gboolean | |||
766 | check_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 | ||||
862 | typedef struct | |||
863 | { | |||
864 | const char *name; | |||
865 | const char **retloc; | |||
866 | } LocateAttr; | |||
867 | ||||
868 | static gboolean | |||
869 | locate_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 | ||||
973 | static gboolean | |||
974 | check_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 | ||||
993 | static CtkTextTag * | |||
994 | tag_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 | ||||
1053 | typedef struct | |||
1054 | { | |||
1055 | const gchar *id; | |||
1056 | gint length; | |||
1057 | const gchar *start; | |||
1058 | } Header; | |||
1059 | ||||
1060 | G_GNUC_BEGIN_IGNORE_DEPRECATIONSclang diagnostic push
clang diagnostic ignored "-Wdeprecated-declarations" | |||
1061 | static GdkPixbuf * | |||
1062 | get_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 | } | |||
1083 | G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop | |||
1084 | ||||
1085 | static void | |||
1086 | parse_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 | ||||
1152 | static void | |||
1153 | parse_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 | ||||
1229 | static gchar * | |||
1230 | get_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 | ||||
1259 | static void | |||
1260 | parse_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 | ||||
1333 | static void | |||
1334 | start_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 | ||||
1427 | static gint | |||
1428 | sort_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 | ||||
1439 | static void | |||
1440 | end_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 | ||||
1544 | static gboolean | |||
1545 | all_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 | ||||
1565 | static void | |||
1566 | text_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 | ||||
1602 | static void | |||
1603 | parse_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 | ||||
1626 | static void | |||
1627 | text_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 | ||||
1634 | static void | |||
1635 | parse_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 | ||||
1672 | static void | |||
1673 | insert_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 | ||||
1722 | static int | |||
1723 | read_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 | ||||
1736 | static gboolean | |||
1737 | header_is (Header *header, | |||
1738 | const gchar *id) | |||
1739 | { | |||
1740 | return (strncmp (header->id, id, strlen (id)) == 0); | |||
1741 | } | |||
1742 | ||||
1743 | static GList * | |||
1744 | read_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 | ||||
1799 | static gboolean | |||
1800 | deserialize_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 | ||||
1847 | gboolean | |||
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 | } |