1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * gsf-libxml.c :
4 *
5 * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2.1 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <gsf-config.h>
23 #include <gsf/gsf-libxml.h>
24 #include <gsf/gsf.h>
25
26 #include <glib/gi18n-lib.h>
27
28 #include <math.h>
29 #include <string.h>
30
31 #undef DEBUG_PUSH_POP
32
33 static GObjectClass *parent_class;
34
35 static gint
glade_enum_from_string(GType type,const char * string)36 glade_enum_from_string (GType type, const char *string)
37 {
38 GEnumClass *eclass;
39 GEnumValue *ev;
40 gchar *endptr;
41 gint ret = 0;
42
43 ret = strtoul(string, &endptr, 0);
44 if (endptr != string) /* parsed a number */
45 return ret;
46
47 eclass = g_type_class_ref (type);
48 ev = g_enum_get_value_by_name (eclass, string);
49 if (!ev) ev = g_enum_get_value_by_nick (eclass, string);
50 if (ev) ret = ev->value;
51
52 g_type_class_unref (eclass);
53
54 return ret;
55 }
56
57 static char const *
glade_string_from_enum(GType type,gint value)58 glade_string_from_enum (GType type, gint value)
59 {
60 GEnumClass *eclass;
61 GEnumValue *ev;
62
63 eclass = g_type_class_ref (type);
64
65 ev = g_enum_get_value (eclass, value);
66
67 g_type_class_unref (eclass);
68
69 return ev ? ev->value_name : "0";
70 }
71
72 static guint
glade_flags_from_string(GType type,const char * string)73 glade_flags_from_string (GType type, const char *string)
74 {
75 GFlagsClass *fclass;
76 gchar *endptr, *prevptr;
77 guint i, j, ret = 0;
78 char *flagstr;
79
80 ret = strtoul(string, &endptr, 0);
81 if (endptr != string) /* parsed a number */
82 return ret;
83
84 fclass = g_type_class_ref(type);
85
86
87 flagstr = g_strdup (string);
88 for (ret = i = j = 0; ; i++) {
89 gboolean eos;
90
91 eos = flagstr [i] == '\0';
92
93 if (eos || flagstr [i] == '|') {
94 GFlagsValue *fv;
95 const char *flag;
96 gunichar ch;
97
98 flag = &flagstr [j];
99 endptr = &flagstr [i];
100
101 if (!eos) {
102 flagstr [i++] = '\0';
103 j = i;
104 }
105
106 /* trim spaces */
107 for (;;)
108 {
109 ch = g_utf8_get_char (flag);
110 if (!g_unichar_isspace (ch))
111 break;
112 flag = g_utf8_next_char (flag);
113 }
114
115 while (endptr > flag)
116 {
117 prevptr = g_utf8_prev_char (endptr);
118 ch = g_utf8_get_char (prevptr);
119 if (!g_unichar_isspace (ch))
120 break;
121 endptr = prevptr;
122 }
123
124 if (endptr > flag)
125 {
126 *endptr = '\0';
127 fv = g_flags_get_value_by_name (fclass, flag);
128
129 if (!fv)
130 fv = g_flags_get_value_by_nick (fclass, flag);
131
132 if (fv)
133 ret |= fv->value;
134 else
135 g_warning ("Unknown flag: '%s'", flag);
136 }
137
138 if (eos)
139 break;
140 }
141 }
142
143 g_free (flagstr);
144
145 g_type_class_unref(fclass);
146
147 return ret;
148 }
149 static gchar *
glade_string_from_flags(GType type,guint flags)150 glade_string_from_flags (GType type, guint flags)
151 {
152 GFlagsClass *flags_class;
153 GString *string;
154 char *ret;
155
156 flags_class = g_type_class_ref (type);
157
158 string = g_string_new ("");
159
160 if (flags_class->n_values)
161 {
162 GFlagsValue *fval;
163
164 for (fval = flags_class->values; fval->value_name; fval++)
165 {
166 /* We have to be careful as some flags include 0 values, e.g.
167 BonoboDockItemBehavior uses 0 for BONOBO_DOCK_ITEM_BEH_NORMAL.
168 If a 0 value is available, we only output it if the entire
169 flags value is 0, otherwise we check if the bit-flag is set. */
170 if ((fval->value == 0 && flags == 0)
171 || (fval->value && (fval->value & flags) == fval->value))
172 {
173 if (string->len)
174 g_string_append_c (string, '|');
175 g_string_append (string, fval->value_name);
176 }
177 }
178 }
179
180 ret = string->str;
181 g_string_free (string, FALSE);
182
183 g_type_class_unref (flags_class);
184
185 return ret;
186 }
187
188 /**
189 * gsf_xml_gvalue_from_str:
190 * @res: Result value
191 * @t: Type of data
192 * @str: Value string
193 *
194 * Try to parse @str as a value of type @t into @res.
195 *
196 * Returns: True when parsing of @str as a value of type @t was succesfull;
197 * false otherwise.
198 */
199 gboolean
gsf_xml_gvalue_from_str(GValue * res,GType t,char const * str)200 gsf_xml_gvalue_from_str (GValue *res, GType t, char const *str)
201 {
202 g_return_val_if_fail (res != NULL, FALSE);
203 g_return_val_if_fail (str != NULL, FALSE);
204
205 g_value_init (res, t);
206
207 /* If the passed-in type is derived from G_TYPE_ENUM
208 * or G_TYPE_FLAGS, we cannot switch on its type
209 * because we don't know its GType at compile time.
210 * We just pretend to have a G_TYPE_ENUM/G_TYPE_FLAGS.
211 */
212 if (G_TYPE_FUNDAMENTAL (t) == G_TYPE_ENUM ||
213 G_TYPE_FUNDAMENTAL (t) == G_TYPE_FLAGS) {
214 t = G_TYPE_FUNDAMENTAL (t);
215 }
216
217 switch (t) {
218 case G_TYPE_CHAR:
219 g_value_set_schar (res, (signed char)(str[0]));
220 break;
221 case G_TYPE_UCHAR:
222 g_value_set_uchar (res, (guchar)str[0]);
223 break;
224 case G_TYPE_BOOLEAN:
225 g_value_set_boolean (res,
226 g_ascii_tolower (str[0]) == 't' ||
227 g_ascii_tolower (str[0]) == 'y' ||
228 strtol (str, NULL, 0));
229 break;
230 case G_TYPE_INT:
231 g_value_set_int (res, strtol (str, NULL, 0));
232 break;
233 case G_TYPE_UINT:
234 g_value_set_uint (res, strtoul (str, NULL, 0));
235 break;
236 case G_TYPE_LONG:
237 g_value_set_long (res, strtol (str, NULL, 0));
238 break;
239 case G_TYPE_ULONG:
240 g_value_set_ulong (res, strtoul (str, NULL, 0));
241 break;
242 case G_TYPE_ENUM:
243 g_value_set_enum (res, glade_enum_from_string (G_VALUE_TYPE (res), str));
244 break;
245 case G_TYPE_FLAGS:
246 g_value_set_flags (res, glade_flags_from_string (G_VALUE_TYPE (res), str));
247 break;
248 case G_TYPE_FLOAT:
249 g_value_set_float (res, g_strtod (str, NULL));
250 break;
251 case G_TYPE_DOUBLE:
252 g_value_set_double (res, g_strtod (str, NULL));
253 break;
254 case G_TYPE_STRING:
255 g_value_set_string (res, str);
256 break;
257
258 default:
259 if (GSF_TIMESTAMP_TYPE == t) {
260 GsfTimestamp *ts = gsf_timestamp_new ();
261 gboolean ok = gsf_timestamp_load_from_string (ts, str);
262 if (ok)
263 gsf_timestamp_to_value (ts, res);
264 gsf_timestamp_free (ts);
265 if (ok)
266 break;
267 } else g_warning ("gsf_xml_gvalue_from_str(): Don't know how to handle type '%s'", g_type_name (t));
268
269 return FALSE;
270 }
271 return TRUE;
272 }
273
274 /* Note: libxml erroneously declares the length argument as int. */
275 static int
gsf_libxml_read(void * context,guint8 * buffer,int len)276 gsf_libxml_read (void *context, guint8 *buffer, int len)
277 {
278 gsf_off_t remaining = gsf_input_remaining ((GsfInput *)context);
279 guint8* res;
280
281 if (len > remaining)
282 len = remaining;
283 res = (guint8 *) gsf_input_read ((GsfInput *)context,
284 (size_t)len, buffer);
285 if (res == NULL && len > 0) /* Not an error if len == 0 */
286 return -1;
287 return len;
288 }
289
290 static int
gsf_libxml_write(void * context,char const * buffer,int len)291 gsf_libxml_write (void *context, char const *buffer, int len)
292 {
293 if (!gsf_output_write ((GsfOutput *)context, (size_t)len, buffer))
294 return -1;
295 return len;
296 }
297
298 static int
gsf_libxml_close(void * context)299 gsf_libxml_close (void *context)
300 {
301 g_object_unref (context);
302 return TRUE;
303 }
304
305 static xmlParserCtxtPtr
gsf_xml_parser_context_full(GsfInput * input,xmlSAXHandlerPtr sax,gpointer user)306 gsf_xml_parser_context_full (GsfInput *input, xmlSAXHandlerPtr sax, gpointer user)
307 {
308 GsfInput *gzip;
309 xmlParserCtxtPtr res;
310
311 g_return_val_if_fail (GSF_IS_INPUT (input), NULL);
312
313 gzip = gsf_input_gzip_new (input, NULL);
314 if (gzip != NULL)
315 input = gzip;
316 else
317 g_object_ref (input);
318
319 res = xmlCreateIOParserCtxt (
320 sax, user,
321 (xmlInputReadCallback) gsf_libxml_read,
322 (xmlInputCloseCallback) gsf_libxml_close,
323 input, XML_CHAR_ENCODING_NONE);
324
325 if (res) {
326 res->replaceEntities = TRUE;
327 res->options |= XML_PARSE_HUGE;
328 } else
329 g_object_unref (input);
330
331 return res;
332 }
333
334 /**
335 * gsf_xml_parser_context: (skip)
336 * @input: #GsfInput
337 *
338 * Create a libxml2 pull style parser context wrapper around gsf input @input.
339 * This signature will probably change to supply a SAX structure.
340 *
341 * <note>This adds a reference to @input.</note>
342 * <note>A simple wrapper around a cleaner implementation that will fold in
343 * when we add other api changes. Its not worth bumping just for this.</note>
344 *
345 * NOTE: skipped since xmlParserCtxt is not exported to introspection.
346 *
347 * Returns: (nullable): A parser context
348 **/
349 xmlParserCtxtPtr
gsf_xml_parser_context(GsfInput * input)350 gsf_xml_parser_context (GsfInput *input)
351 {
352 return gsf_xml_parser_context_full (input, NULL, NULL);
353 }
354
355 /**
356 * gsf_xml_output_buffer_new: (skip)
357 * @output: #GsfOutput
358 * @handler: (allow-none) (scope call):
359 *
360 * <note>This adds a reference to @output.</note>
361 * <note>This is <emphasis>not</emphasis> releated to #GsfXMLOut.</note>
362 * Hmm... @handler.
363 **/
364 static xmlOutputBufferPtr
gsf_xml_output_buffer_new(GsfOutput * output,xmlCharEncodingHandlerPtr handler)365 gsf_xml_output_buffer_new (GsfOutput *output,
366 xmlCharEncodingHandlerPtr handler)
367 {
368 xmlOutputBufferPtr res = xmlAllocOutputBuffer (handler);
369 if (res != NULL) {
370 g_object_ref (output);
371 res->context = (void *)output;
372 res->writecallback = gsf_libxml_write;
373 res->closecallback = gsf_libxml_close;
374 }
375
376 return res;
377 }
378
379 /**
380 * gsf_xmlDocFormatDump:
381 * @output: #GsfOutput
382 * @cur: #xmlDocPtr
383 * @encoding: (allow-none): The encoding to use.
384 * @format: %TRUE to reformat the output.
385 *
386 * Dumps the document @cur into @output.
387 *
388 * Returns: status from xmlSaveFormatFileTo.
389 **/
390 int
gsf_xmlDocFormatDump(GsfOutput * output,xmlDocPtr cur,char const * encoding,gboolean format)391 gsf_xmlDocFormatDump (GsfOutput *output, xmlDocPtr cur, char const *encoding,
392 gboolean format)
393 {
394 xmlOutputBufferPtr buf;
395 xmlCharEncodingHandlerPtr handler = NULL;
396
397 if (cur == NULL) {
398 #ifdef DEBUG_TREE
399 xmlGenericError(xmlGenericErrorContext,
400 "xmlDocDump : document == NULL\n");
401 #endif
402 return(-1);
403 }
404
405 if (encoding != NULL) {
406 xmlCharEncoding enc;
407
408 enc = xmlParseCharEncoding(encoding);
409
410 if (cur->charset != XML_CHAR_ENCODING_UTF8) {
411 xmlGenericError(xmlGenericErrorContext,
412 "xmlDocDump: document not in UTF8\n");
413 return(-1);
414 }
415 if (enc != XML_CHAR_ENCODING_UTF8) {
416 handler = xmlFindCharEncodingHandler(encoding);
417 if (handler == NULL) {
418 xmlFree((char *) cur->encoding);
419 cur->encoding = NULL;
420 }
421 }
422 }
423 buf = gsf_xml_output_buffer_new (output, handler);
424 return xmlSaveFormatFileTo (buf, cur, encoding, format);
425 }
426
427 /**************************************************************************/
428 /* We parse and do some limited validation of the XML file, if this passes,
429 * then we return TRUE
430 */
431 typedef struct {
432 GsfXMLProbeFunc func;
433 gboolean success;
434 } GsfXMLProbeState;
435
436 static void
gsf_xml_probe_error(GsfXMLProbeState * state,char const * msg,...)437 gsf_xml_probe_error (GsfXMLProbeState *state, char const *msg, ...)
438 {
439 (void)msg;
440 state->func = NULL;
441 state->success = FALSE;
442 }
443 static void
gsf_xml_probe_element(GsfXMLProbeState * state,const xmlChar * name,const xmlChar * prefix,const xmlChar * URI,int nb_namespaces,const xmlChar ** namespaces,int nb_attributes,int nb_defaulted,const xmlChar ** attributes)444 gsf_xml_probe_element (GsfXMLProbeState *state,
445 const xmlChar *name,
446 const xmlChar *prefix,
447 const xmlChar *URI,
448 int nb_namespaces,
449 const xmlChar **namespaces,
450 int nb_attributes,
451 int nb_defaulted,
452 const xmlChar **attributes)
453 {
454 state->success =
455 state->func &&
456 state->func (name, prefix, URI, nb_namespaces, namespaces,
457 nb_attributes, nb_defaulted, attributes);
458 state->func = NULL;
459 }
460
461 /**
462 * gsf_xml_probe:
463 * @input: #GsfInput
464 * @func: (scope call): #GsfXMLProbeFunc
465 *
466 * Returns: TRUE on success.
467 */
468 gboolean
gsf_xml_probe(GsfInput * input,GsfXMLProbeFunc func)469 gsf_xml_probe (GsfInput *input, GsfXMLProbeFunc func)
470 {
471 static xmlSAXHandler gsf_xml_prober = {
472 NULL, /* internalSubset */
473 NULL, /* isStandalone */
474 NULL, /* hasInternalSubset */
475 NULL, /* hasExternalSubset */
476 NULL, /* resolveEntity */
477 NULL, /* getEntity */
478 NULL, /* entityDecl */
479 NULL, /* notationDecl */
480 NULL, /* attributeDecl */
481 NULL, /* elementDecl */
482 NULL, /* unparsedEntityDecl */
483 NULL, /* setDocumentLocator */
484 NULL, /* startDocument */
485 NULL, /* endDocument */
486 NULL, /* startElement */
487 NULL, /* endElement */
488 NULL, /* reference */
489 NULL, /* characters */
490 NULL, /* ignorableWhitespace */
491 NULL, /* processingInstruction */
492 NULL, /* comment */
493 NULL, /* xmlParserWarning */
494 (errorSAXFunc) &gsf_xml_probe_error, /* error */
495 (errorSAXFunc) &gsf_xml_probe_error, /* fatalError */
496 NULL, /* getParameterEntity */
497 NULL, /* cdataBlock; */
498 NULL, /* externalSubset; */
499 XML_SAX2_MAGIC,
500 NULL,
501 (startElementNsSAX2Func) &gsf_xml_probe_element, /* startElementNs */
502 NULL, /* endElementNs */
503 NULL /* xmlStructuredErrorFunc */
504 };
505 GsfXMLProbeState probe_state = { func, FALSE };
506 xmlParserCtxt *parse_context;
507 char const *buf;
508
509 if (gsf_input_seek (input, 0, G_SEEK_SET))
510 return FALSE;
511
512 g_object_ref (input);
513 input = gsf_input_uncompress (input);
514 gsf_input_seek (input, 0, G_SEEK_SET);
515
516 buf = gsf_input_read (input, 4, NULL);
517 if (NULL != buf ) {
518 parse_context = xmlCreatePushParserCtxt (&gsf_xml_prober, &probe_state,
519 (char *)buf, 4, gsf_input_name (input));
520 if (NULL != parse_context) {
521 while (NULL != probe_state.func &&
522 NULL != (buf = gsf_input_read (input, 1, NULL)))
523 xmlParseChunk (parse_context, (char *)buf, 1, 0);
524 }
525 xmlFreeParserCtxt (parse_context);
526 }
527 g_object_unref (input);
528
529 return probe_state.success;
530 }
531
532 /***************************************************************************/
533
534 /**
535 * GsfXMLInNS:
536 * @uri: URI
537 **/
538
539 /**
540 * GsfXMLIn:
541 * @user_state: user data
542 * @content: the current node content
543 * @doc: Current document being parsed #GsfXMLInDoc
544 * @node: current node (not on the stack)
545 **/
546
547 /**
548 * GsfXMLInNode:
549 * @id: identifier unique in the entire tree
550 * @ns_id: namespace identifier
551 * @name: node name
552 * @parent_id: parent node identifier
553 * @start: callback for the node opening
554 * @end: callback for node end
555 * @has_content: whether the node has content
556 * @check_children_for_ns: whether to check namespace for children
557 * @share_children_with_parent: whether to share children with parent.
558 **/
559
560 typedef struct {
561 GsfXMLInNode pub;
562 /* internal state */
563 GSList *groups;
564 GSList *extensions;
565 } GsfXMLInNodeInternal;
566 struct _GsfXMLInDoc {
567 GsfXMLInNodeInternal const *root_node;
568 GHashTable *symbols; /* GsfXMLInNodeInternal hashed by id */
569 GsfXMLInNS const*ns;
570 GsfXMLInUnknownFunc unknown_handler;
571 unsigned ref_count;
572 };
573 typedef struct {
574 GsfXMLIn pub;
575 GsfInput *input; /* TODO : Move to pub for 1.16.0 */
576
577 int default_ns_id; /* <0 => no default ns */
578 GSList *ns_stack;
579 GHashTable *ns_prefixes;
580 GPtrArray *ns_by_id;
581 GHashTable *ns_unknowns;
582 GSList *contents_stack;
583 gboolean initialized;
584 gint unknown_depth; /* handle recursive unknown tags */
585 gboolean from_unknown_handler;
586 gboolean debug_parsing;
587 gboolean silent_unknowns;
588
589 GSList *extension_stack; /* stack of GsfXMLInExtension */
590 } GsfXMLInInternal;
591 typedef struct {
592 char *tag;
593 unsigned taglen;
594 unsigned ref_count;
595 } GsfXMLInNSInstance;
596 typedef struct {
597 int ns_id;
598 GSList *elem;
599 } GsfXMLInNodeGroup;
600 typedef struct {
601 GsfXMLInExtDtor dtor;
602 gpointer state;
603 GsfXMLInDoc const *doc;
604 gboolean from_unknown;
605 } GsfXMLInExtension;
606
607 static char const *
node_name(GsfXMLInNode const * node)608 node_name (GsfXMLInNode const *node)
609 {
610 return (node->name != NULL) ? node->name : "{catch all)}";
611 }
612
613 static void
push_child(GsfXMLInInternal * state,GsfXMLInNode const * node,int default_ns_id,xmlChar const ** attrs,GsfXMLInExtension * ext)614 push_child (GsfXMLInInternal *state, GsfXMLInNode const *node, int default_ns_id,
615 xmlChar const **attrs, GsfXMLInExtension *ext)
616 {
617 if (state->debug_parsing)
618 g_printerr ("push: %-*s%s\n",
619 (int)g_slist_length (state->pub.node_stack), "",
620 node->name);
621
622 if (node->has_content == GSF_XML_CONTENT) {
623 if (state->pub.content->len) {
624 state->contents_stack = g_slist_prepend
625 (state->contents_stack, state->pub.content);
626 state->pub.content = g_string_sized_new (128);
627 } else {
628 state->contents_stack = g_slist_prepend
629 (state->contents_stack, NULL);
630 }
631 }
632 state->pub.node_stack = g_slist_prepend (state->pub.node_stack,
633 (gpointer)state->pub.node);
634 state->ns_stack = g_slist_prepend (state->ns_stack,
635 GINT_TO_POINTER (state->default_ns_id));
636 state->pub.node = node;
637 state->default_ns_id = default_ns_id;
638
639 state->extension_stack = g_slist_prepend (state->extension_stack, ext);
640 if (NULL != ext) {
641 GsfXMLInDoc const *old_doc = state->pub.doc;
642 state->pub.doc = ext->doc;
643 ext->doc = old_doc;
644 if (NULL != ext->state) {
645 gpointer old_state = state->pub.user_state;
646 state->pub.user_state = ext->state;
647 ext->state = old_state;
648 }
649 }
650 if (NULL != node->start)
651 node->start (&state->pub, attrs);
652 }
653
654 static gboolean
lookup_child(GsfXMLInInternal * state,int default_ns_id,GSList * groups,xmlChar const * name,xmlChar const ** attrs,GsfXMLInExtension * ext)655 lookup_child (GsfXMLInInternal *state, int default_ns_id,
656 GSList *groups, xmlChar const *name,
657 xmlChar const **attrs, GsfXMLInExtension *ext)
658 {
659 GsfXMLInNodeGroup *group;
660 GsfXMLInNode *node;
661 GsfXMLInNSInstance *inst;
662 GSList *elem, *ptr;
663 char const *tmp;
664
665 for (ptr = groups ; ptr != NULL ; ptr = ptr->next) {
666 group = ptr->data;
667 /* Is the node explicitly namespaced ? */
668 if (group->ns_id >= 0 &&
669 group->ns_id < (int)state->ns_by_id->len &&
670 NULL != (inst = g_ptr_array_index (state->ns_by_id, group->ns_id)) &&
671 0 == strncmp (name, inst->tag, inst->taglen))
672 tmp = name + inst->taglen;
673 else if (group->ns_id < 0 || /* target is unqualified */
674 group->ns_id == default_ns_id) { /* target is in default ns */
675 #if 0
676 g_return_val_if_fail ((int)state->ns_by_id->len > group->ns_id, FALSE);
677 inst = g_ptr_array_index (state->ns_by_id, group->ns_id);
678 g_warning ("accepted ns = '%s' looking for '%s'", inst->tag, name);
679 #endif
680 tmp = name;
681 } else
682 continue;
683
684 for (elem = group->elem ; elem != NULL ; elem = elem->next) {
685 node = elem->data;
686 if (node->name == NULL || !strcmp (tmp, node->name)) {
687 push_child (state, node, default_ns_id, attrs, ext);
688 return TRUE;
689 }
690 }
691 }
692 return FALSE;
693 }
694
695 static void
gsf_xml_in_dump_state(GsfXMLInInternal * state)696 gsf_xml_in_dump_state (GsfXMLInInternal *state)
697 {
698 GSList *ptr = state->pub.node_stack = g_slist_reverse (state->pub.node_stack);
699 if (ptr != NULL) /* skip toplevel catch all */
700 ptr = ptr->next;
701 for (;ptr != NULL && ptr != NULL; ptr = ptr->next) {
702 GsfXMLInNodeInternal const *node = ptr->data;
703 if (node != NULL)
704 g_printerr ("%s -> ", node_name (&node->pub));
705 }
706 if (state->pub.node != NULL)
707 g_printerr ("%s\n", node_name (state->pub.node));
708 state->pub.node_stack = g_slist_reverse (state->pub.node_stack);
709 }
710
711 static void
gsf_xml_in_start_element(GsfXMLInInternal * state,xmlChar const * name,xmlChar const ** attrs)712 gsf_xml_in_start_element (GsfXMLInInternal *state, xmlChar const *name, xmlChar const **attrs)
713 {
714 GsfXMLInNSInstance *inst;
715 GsfXMLInNS const *ns;
716 int default_ns_id = state->default_ns_id;
717 GsfXMLInNodeInternal const *node;
718 xmlChar const **ns_ptr;
719 GSList *ptr;
720 char const *tmp;
721 int i;
722 gboolean complain = !state->silent_unknowns;
723
724 /* Scan for namespace declarations. Yes it is ugly to have the api
725 * flag that its children can declare namespaces. However, given that a
726 * we need to know which namespace we are in before we can recognize
727 * the current node there is no choice.
728 * eg <gnm:Workbook xmlns:gnm="www.gnumeric.org"/> we can not know
729 * that we are in node 'Workbook' without recognizing ns=gnm, which we
730 * would not do unless we checked for a namespace */
731 ns = state->pub.doc->ns;
732 if (ns != NULL && state->pub.node->check_children_for_ns) {
733 for (ns_ptr = attrs; ns_ptr != NULL && ns_ptr[0] && ns_ptr[1] ; ns_ptr += 2) {
734 if (strncmp (*ns_ptr, "xmlns", 5))
735 continue;
736 if (ns_ptr[0][5] != '\0' && ns_ptr[0][5] != ':')
737 continue;
738 for (i = 0; (tmp = ns[i].uri) != NULL ; i++) {
739 if (strcmp (tmp, ns_ptr[1]))
740 continue;
741
742 if (ns_ptr[0][5] == '\0') {
743 default_ns_id = ns[i].ns_id;
744 break;
745 }
746
747 inst = g_hash_table_lookup (state->ns_prefixes, ns_ptr[0] + 6);
748 if (inst == NULL) {
749 inst = g_new0 (GsfXMLInNSInstance, 1);
750 inst->tag = g_strconcat (ns_ptr[0] + 6, ":", NULL);
751 inst->taglen = strlen (inst->tag);
752 inst->ref_count = 1;
753 g_hash_table_insert (state->ns_prefixes, g_strdup (ns_ptr[0] + 6), inst);
754
755 if (ns[i].ns_id >= state->ns_by_id->len)
756 g_ptr_array_set_size (state->ns_by_id, ns[i].ns_id+1);
757 if (g_ptr_array_index (state->ns_by_id, ns[i].ns_id)) {
758 g_warning ("Damn. Someone just declared the same namespace '%s' with a different prefix '%s'",
759 ns[i].uri, inst->tag);
760 } else
761 g_ptr_array_index (state->ns_by_id, ns[i].ns_id) = inst;
762 } else
763 inst->ref_count++;
764 break;
765 }
766
767 if (NULL == tmp) {
768 char *s = g_strdup (ns_ptr[0] + 6);
769 g_hash_table_replace (state->ns_unknowns, s, s);
770 if (gsf_debug_flag ("xml-ns"))
771 g_warning ("Unknown namespace uri = '%s'", ns_ptr[1]);
772 }
773 }
774 }
775
776 node = (GsfXMLInNodeInternal const *) state->pub.node;
777
778 if (state->unknown_depth == 0) {
779 if (lookup_child (state, default_ns_id, node->groups, name, attrs, NULL))
780 return;
781
782 /*
783 * useful for <Data><b><i><u></u></i></b></Data> where all of
784 * the markup can nest
785 */
786 ptr = state->pub.node_stack;
787 for (; ptr != NULL && node->pub.share_children_with_parent; ptr = ptr->next) {
788 node = ptr->data;
789 if (lookup_child (state, default_ns_id, node->groups, name, attrs, NULL))
790 return;
791 }
792
793 /* Check for extensions */
794 for (ptr = node->extensions; ptr != NULL ; ptr = ptr->next) {
795 GsfXMLInExtension *ext = ptr->data;
796 if (lookup_child (state, default_ns_id,
797 ext->doc->root_node->groups, name, attrs, ext))
798 return;
799 }
800 }
801
802 if (state->pub.doc->unknown_handler != NULL) {
803 gboolean handled;
804 state->from_unknown_handler = TRUE;
805 handled = (state->pub.doc->unknown_handler) (&state->pub, name, attrs);
806 state->from_unknown_handler = FALSE;
807 if (handled)
808 return;
809 }
810
811 if (state->unknown_depth++ > 0)
812 return;
813
814 tmp = strchr (name, ':');
815 if (tmp) {
816 char *nsstr = g_strndup (name, tmp - (const char *)name);
817 if (g_hash_table_lookup (state->ns_unknowns, nsstr))
818 complain = FALSE;
819 g_free (nsstr);
820 }
821
822 if (!complain)
823 return;
824
825 g_printerr ("Unexpected element '%s' in state : \n\t", name);
826 gsf_xml_in_dump_state (state);
827 }
828
829 static void
gsf_xml_in_ext_free(GsfXMLInInternal * state,GsfXMLInExtension * ext)830 gsf_xml_in_ext_free (GsfXMLInInternal *state, GsfXMLInExtension *ext)
831 {
832 if (ext->dtor)
833 (ext->dtor) (&state->pub, ext->state);
834 g_free (ext);
835 }
836
837 static void
gsf_xml_in_end_element(GsfXMLInInternal * state,G_GNUC_UNUSED xmlChar const * name)838 gsf_xml_in_end_element (GsfXMLInInternal *state,
839 G_GNUC_UNUSED xmlChar const *name)
840 {
841 GsfXMLInNodeInternal *node;
842 GsfXMLInExtension *ext;
843 GSList *ptr;
844
845 if (state->unknown_depth > 0) {
846 state->unknown_depth--;
847 return;
848 }
849
850 g_return_if_fail (state->pub.node != NULL);
851 g_return_if_fail (state->pub.node_stack != NULL);
852 g_return_if_fail (state->ns_stack != NULL);
853
854 node = (GsfXMLInNodeInternal *) state->pub.node;
855 if (node->pub.end)
856 node->pub.end (&state->pub, NULL);
857
858 if (node->pub.has_content == GSF_XML_CONTENT) {
859 GString *top;
860
861 g_return_if_fail (state->contents_stack != NULL);
862 top = state->contents_stack->data;
863 state->contents_stack = g_slist_remove
864 (state->contents_stack, top);
865
866 if (top) {
867 g_string_free (state->pub.content, TRUE);
868 state->pub.content = top;
869 } else {
870 g_string_truncate (state->pub.content, 0);
871 }
872 }
873
874 /* Free any potential extensions associated with the current node */
875 for (ptr = node->extensions; ptr != NULL ; ptr = ptr->next)
876 gsf_xml_in_ext_free (state, ptr->data);
877 g_slist_free (node->extensions);
878 node->extensions = NULL;
879
880 if (state->debug_parsing)
881 g_printerr (" pop: %-*s%s\n",
882 (int)g_slist_length (state->pub.node_stack) - 1, "",
883 node->pub.name);
884
885 /* pop the state stack */
886 ext = state->extension_stack->data;
887 state->extension_stack = g_slist_delete_link (state->extension_stack, state->extension_stack);
888 state->pub.node = state->pub.node_stack->data;
889 state->pub.node_stack = g_slist_delete_link (state->pub.node_stack, state->pub.node_stack);
890 state->default_ns_id = GPOINTER_TO_INT (state->ns_stack->data);
891 state->ns_stack = g_slist_delete_link (state->ns_stack, state->ns_stack);
892
893 if (NULL != ext) {
894 GsfXMLInDoc const *ext_doc = state->pub.doc;
895 state->pub.doc = ext->doc;
896 ext->doc = ext_doc;
897 if (NULL != ext->state) {
898 gpointer ext_state = state->pub.user_state;
899 state->pub.user_state = ext->state;
900 ext->state = ext_state;
901 }
902 if (ext->from_unknown)
903 gsf_xml_in_ext_free (state, ext);
904 }
905 }
906
907 static void
gsf_xml_in_characters(GsfXMLInInternal * state,xmlChar const * chars,int len)908 gsf_xml_in_characters (GsfXMLInInternal *state, xmlChar const *chars, int len)
909 {
910 if (!state->initialized)
911 return;
912
913 if (state->pub.node->has_content != GSF_XML_NO_CONTENT)
914 g_string_append_len (state->pub.content, chars, len);
915 }
916
917 static xmlEntityPtr
gsf_xml_in_get_entity(G_GNUC_UNUSED GsfXMLInInternal * state,xmlChar const * name)918 gsf_xml_in_get_entity (G_GNUC_UNUSED GsfXMLInInternal *state, xmlChar const *name)
919 {
920 return xmlGetPredefinedEntity (name);
921 }
922
923 static void
gsf_free_xmlinnsinstance(GsfXMLInNSInstance * inst)924 gsf_free_xmlinnsinstance (GsfXMLInNSInstance *inst)
925 {
926 g_free (inst->tag);
927 g_free (inst);
928 }
929
930 static void
gsf_xml_in_start_document(GsfXMLInInternal * state)931 gsf_xml_in_start_document (GsfXMLInInternal *state)
932 {
933 /*
934 * This function will not be called when parsing an empty
935 * document.
936 */
937 state->initialized = TRUE;
938 state->pub.node = &state->pub.doc->root_node->pub;
939 state->unknown_depth = 0;
940 state->pub.node_stack = NULL;
941 state->extension_stack = NULL;
942 state->ns_stack = NULL;
943 state->default_ns_id = -1;
944 state->ns_by_id = g_ptr_array_new ();
945 state->ns_prefixes = g_hash_table_new_full (
946 g_str_hash, g_str_equal,
947 g_free, (GDestroyNotify) gsf_free_xmlinnsinstance);
948 state->ns_unknowns = g_hash_table_new_full (
949 g_str_hash, g_str_equal,
950 g_free, NULL);
951 state->contents_stack = NULL;
952 state->from_unknown_handler = FALSE;
953 state->debug_parsing = gsf_debug_flag ("xml-parsing");
954 state->silent_unknowns = FALSE;
955 }
956
957 static void
gsf_xml_in_end_document(GsfXMLInInternal * state)958 gsf_xml_in_end_document (GsfXMLInInternal *state)
959 {
960 g_string_free (state->pub.content, TRUE);
961 state->pub.content = NULL;
962
963 if (state->initialized) {
964 g_ptr_array_free (state->ns_by_id, TRUE);
965 state->ns_by_id = NULL;
966
967 g_hash_table_destroy (state->ns_prefixes);
968 state->ns_prefixes = NULL;
969
970 g_hash_table_destroy (state->ns_unknowns);
971 state->ns_unknowns = NULL;
972
973 g_slist_free (state->extension_stack);
974 state->extension_stack = NULL;
975
976 g_slist_free (state->pub.node_stack);
977 state->pub.node_stack = NULL;
978
979 g_slist_free (state->ns_stack);
980 state->ns_stack = NULL;
981
982 state->initialized = FALSE;
983
984 if (state->pub.node != &state->pub.doc->root_node->pub) {
985 g_warning ("Document likely damaged.");
986 }
987 if (state->unknown_depth > 0) {
988 g_warning ("Document likely damaged.");
989 }
990 }
991 }
992
993 static void
994 gsf_xml_in_warning (G_GNUC_UNUSED GsfXMLInInternal *state, char const *msg, ...) G_GNUC_PRINTF (2, 3);
995 static void
gsf_xml_in_warning(G_GNUC_UNUSED GsfXMLInInternal * state,char const * msg,...)996 gsf_xml_in_warning (G_GNUC_UNUSED GsfXMLInInternal *state, char const *msg, ...)
997 {
998 va_list args;
999
1000 va_start (args, msg);
1001 g_logv ("XML", G_LOG_LEVEL_WARNING, msg, args);
1002 va_end (args);
1003 }
1004
1005 static void
1006 gsf_xml_in_error (G_GNUC_UNUSED GsfXMLInInternal *state, char const *msg, ...) G_GNUC_PRINTF (2, 3);
1007 static void
gsf_xml_in_error(G_GNUC_UNUSED GsfXMLInInternal * state,char const * msg,...)1008 gsf_xml_in_error (G_GNUC_UNUSED GsfXMLInInternal *state, char const *msg, ...)
1009 {
1010 va_list args;
1011
1012 va_start (args, msg);
1013 g_logv ("XML", G_LOG_LEVEL_CRITICAL, msg, args);
1014 va_end (args);
1015 }
1016
1017 static void
1018 gsf_xml_in_fatal_error (G_GNUC_UNUSED GsfXMLInInternal *state, char const *msg, ...) G_GNUC_PRINTF (2, 3);
1019 static void
gsf_xml_in_fatal_error(G_GNUC_UNUSED GsfXMLInInternal * state,char const * msg,...)1020 gsf_xml_in_fatal_error (G_GNUC_UNUSED GsfXMLInInternal *state, char const *msg, ...)
1021 {
1022 va_list args;
1023
1024 va_start (args, msg);
1025 g_logv ("XML", G_LOG_LEVEL_ERROR, msg, args);
1026 va_end (args);
1027 }
1028
1029 static xmlSAXHandler gsfXMLInParser = {
1030 NULL, /* internalSubset */
1031 NULL, /* isStandalone */
1032 NULL, /* hasInternalSubset */
1033 NULL, /* hasExternalSubset */
1034 NULL, /* resolveEntity */
1035 (getEntitySAXFunc)gsf_xml_in_get_entity, /* getEntity */
1036 NULL, /* entityDecl */
1037 NULL, /* notationDecl */
1038 NULL, /* attributeDecl */
1039 NULL, /* elementDecl */
1040 NULL, /* unparsedEntityDecl */
1041 NULL, /* setDocumentLocator */
1042 (startDocumentSAXFunc)gsf_xml_in_start_document, /* startDocument */
1043 (endDocumentSAXFunc)gsf_xml_in_end_document, /* endDocument */
1044 (startElementSAXFunc)gsf_xml_in_start_element, /* startElement */
1045 (endElementSAXFunc)gsf_xml_in_end_element, /* endElement */
1046 NULL, /* reference */
1047 (charactersSAXFunc)gsf_xml_in_characters, /* characters */
1048 NULL, /* ignorableWhitespace */
1049 NULL, /* processingInstruction */
1050 NULL, /* comment */
1051 (warningSAXFunc)gsf_xml_in_warning, /* warning */
1052 (errorSAXFunc)gsf_xml_in_error, /* error */
1053 (fatalErrorSAXFunc)gsf_xml_in_fatal_error, /* fatalError */
1054 NULL, /* getParameterEntity */
1055 NULL, /* cdataBlock */
1056 NULL, /* externalSubset */
1057 0
1058 #if LIBXML_VERSION >= 20600
1059 ,
1060 NULL, NULL, NULL
1061 #if LIBXML_VERSION >= 20602
1062 ,
1063 NULL /* serror */
1064 #endif
1065 #endif
1066 };
1067
1068 static void
gsf_xml_in_node_internal_free(GsfXMLInNodeInternal * node)1069 gsf_xml_in_node_internal_free (GsfXMLInNodeInternal *node)
1070 {
1071 GSList *ptr;
1072 GsfXMLInNodeGroup *group;
1073
1074 if (NULL != node->extensions) {
1075 g_warning ("leaking extensions");
1076 }
1077
1078 for (ptr = node->groups; ptr != NULL ; ptr = ptr->next) {
1079 group = ptr->data;
1080 g_slist_free (group->elem);
1081 g_free (group);
1082 }
1083 g_slist_free (node->groups);
1084 node->groups = NULL;
1085 g_free (node);
1086 }
1087
1088 /**
1089 * gsf_xml_in_doc_free:
1090 * @doc: A #GsfXMLInDoc
1091 *
1092 * Free up resources
1093 **/
1094 void
gsf_xml_in_doc_free(GsfXMLInDoc * doc)1095 gsf_xml_in_doc_free (GsfXMLInDoc *doc)
1096 {
1097 doc->ref_count--;
1098 if (doc->ref_count == 0) {
1099 g_return_if_fail (doc != NULL);
1100 g_return_if_fail (doc->symbols != NULL);
1101
1102 g_hash_table_destroy (doc->symbols);
1103
1104 /* poison the well just in case */
1105 doc->symbols = NULL;
1106 doc->root_node = NULL;
1107 g_free (doc);
1108 }
1109 }
1110
1111 /**
1112 * gsf_xml_in_doc_new:
1113 * @nodes: (array zero-terminated=1): an array of node descriptors
1114 * @ns: (array zero-terminated=1): an array of namespace identifiers
1115 *
1116 * Combine the nodes in the %NULL terminated array starting at @nodes with the
1117 * name spaces in the %NULL terminated array starting at @ns. Prepare the
1118 * data structures necessary to validate a doument based on that description.
1119 *
1120 * Returns: (nullable): a #GsfXMLInDoc
1121 **/
1122 GsfXMLInDoc *
gsf_xml_in_doc_new(GsfXMLInNode const * nodes,GsfXMLInNS const * ns)1123 gsf_xml_in_doc_new (GsfXMLInNode const *nodes, GsfXMLInNS const *ns)
1124 {
1125 GsfXMLInDoc *doc;
1126
1127 g_return_val_if_fail (nodes != NULL, NULL);
1128
1129 doc = g_new0 (GsfXMLInDoc, 1);
1130 doc->root_node = NULL;
1131 doc->symbols = g_hash_table_new_full (g_str_hash, g_str_equal,
1132 NULL, (GDestroyNotify) gsf_xml_in_node_internal_free);
1133 doc->ns = ns;
1134
1135 gsf_xml_in_doc_add_nodes (doc, nodes);
1136
1137 if (NULL == doc->root_node) {
1138 gsf_xml_in_doc_free (doc);
1139 g_return_val_if_fail (NULL != doc->root_node, NULL);
1140 }
1141 doc->ref_count = 1;
1142
1143 return doc;
1144 }
1145
1146 static GsfXMLInDoc *
gsf_xml_in_doc_ref(GsfXMLInDoc * doc)1147 gsf_xml_in_doc_ref (GsfXMLInDoc *doc) {
1148 doc->ref_count++;
1149 return doc;
1150 }
1151
1152 GType
gsf_xml_in_doc_get_type(void)1153 gsf_xml_in_doc_get_type (void)
1154 {
1155 static GType type = 0;
1156
1157 if (type == 0)
1158 type = g_boxed_type_register_static
1159 ("GsfXMLInDoc",
1160 (GBoxedCopyFunc) gsf_xml_in_doc_ref,
1161 (GBoxedFreeFunc) gsf_xml_in_doc_free);
1162
1163 return type;
1164 }
1165
1166 /**
1167 * gsf_xml_in_doc_add_nodes:
1168 * @doc: #GsfXMLInDoc
1169 * @nodes: (array zero-terminated=1): %NULL terminated array of #GsfXMLInNode
1170 *
1171 * Adds additional nodes to the structure of @doc
1172 **/
1173 void
gsf_xml_in_doc_add_nodes(GsfXMLInDoc * doc,GsfXMLInNode const * nodes)1174 gsf_xml_in_doc_add_nodes (GsfXMLInDoc *doc,
1175 GsfXMLInNode const *nodes)
1176 {
1177 GsfXMLInNode const *e_node;
1178
1179 g_return_if_fail (doc != NULL);
1180 g_return_if_fail (nodes != NULL);
1181
1182 for (e_node = nodes; e_node->id != NULL ; e_node++ ) {
1183 GsfXMLInNodeInternal *tmp, *node =
1184 g_hash_table_lookup (doc->symbols, e_node->id);
1185 if (node != NULL) {
1186 /*
1187 * We use the repeat of a node name to attach an
1188 * entire subtree in another place too. The second
1189 * node should either be empty (old method) or
1190 * use GSF_XML_2ND (new, as-of 1.14.33).
1191 */
1192 if (e_node->has_content == GSF_XML_2ND) {
1193 /* Nothing, but e_node contents ignored. */
1194 } else if (e_node->start != NULL || e_node->end != NULL ||
1195 e_node->has_content != GSF_XML_NO_CONTENT ||
1196 e_node->user_data.v_int != 0) {
1197 g_warning ("ID '%s' has already been registered.",
1198 e_node->id);
1199 continue;
1200 }
1201
1202 if (strcmp (e_node->parent_id, node->pub.parent_id) == 0)
1203 g_warning ("Duplicate node %s (under %s)", e_node->id, e_node->parent_id);
1204 } else {
1205 if (e_node->has_content == GSF_XML_2ND) {
1206 g_warning ("ID '%s' is declared 2nd, but is missing.",
1207 e_node->id);
1208 /* Hence e_node contents ignored. */
1209 continue;
1210 }
1211
1212 node = g_new0 (GsfXMLInNodeInternal, 1);
1213 node->pub = *e_node;
1214 /* WARNING VILE HACK :
1215 * The api in 1.8.2 passed has_content as a boolean.
1216 * Too many things still use that to change yet. We
1217 * edit the bool here to be GSF_CONTENT_NONE or
1218 * GSF_XML_CONTENT and try to ignore SHARED_CONTENT */
1219 if (node->pub.has_content != 0 &&
1220 node->pub.has_content != GSF_XML_SHARED_CONTENT)
1221 node->pub.has_content = GSF_XML_CONTENT;
1222 node->groups = NULL;
1223 g_hash_table_insert (doc->symbols,
1224 (gpointer)node->pub.id, node);
1225 }
1226 if (NULL == doc->root_node && e_node == nodes) /* first valid node is the root */
1227 doc->root_node = node;
1228
1229 /* NOTE : use e_node for parent_id rather than node
1230 * in case this is a shared symbol */
1231 tmp = g_hash_table_lookup (doc->symbols, e_node->parent_id);
1232 if (tmp != NULL) {
1233 GSList *ptr;
1234 GsfXMLInNodeGroup *group = NULL;
1235 int const ns_id = node->pub.ns_id;
1236 for (ptr = tmp->groups; ptr != NULL ; ptr = ptr->next) {
1237 group = ptr->data;
1238 if (group->ns_id == ns_id)
1239 break;
1240 }
1241 if (ptr == NULL) {
1242 group = g_new0 (GsfXMLInNodeGroup, 1);
1243 group->ns_id = ns_id;
1244 tmp->groups = g_slist_prepend (tmp->groups, group);
1245 }
1246 group->elem = g_slist_prepend (group->elem, node);
1247 } else if (strcmp (e_node->id, e_node->parent_id)) {
1248 g_warning ("Parent ID '%s' unknown", e_node->parent_id);
1249 continue;
1250 }
1251 }
1252 }
1253
1254 /**
1255 * gsf_xml_in_doc_set_unknown_handler:
1256 * @doc: #GsfXMLInDoc
1257 * @handler: (scope call): The function to call
1258 *
1259 * Call the function @handler when an unexpected child node is found
1260 **/
1261 void
gsf_xml_in_doc_set_unknown_handler(GsfXMLInDoc * doc,GsfXMLInUnknownFunc handler)1262 gsf_xml_in_doc_set_unknown_handler (GsfXMLInDoc *doc,
1263 GsfXMLInUnknownFunc handler)
1264 {
1265 g_return_if_fail (doc != NULL);
1266 doc->unknown_handler = handler;
1267 }
1268
1269 /**
1270 * gsf_xml_in_push_state:
1271 * @xin: #GsfXMLIn
1272 * @doc: #GsfXMLInDoc
1273 * @new_state: arbitrary content for the parser
1274 * @dtor: (scope call): #GsfXMLInExtDtor
1275 * @attrs: (array) (element-type utf8): array of xmlChar const *
1276 *
1277 * Take the first node from @doc as the current node and call its start handler.
1278 **/
1279 void
gsf_xml_in_push_state(GsfXMLIn * xin,GsfXMLInDoc const * doc,gpointer new_state,GsfXMLInExtDtor dtor,xmlChar const ** attrs)1280 gsf_xml_in_push_state (GsfXMLIn *xin, GsfXMLInDoc const *doc,
1281 gpointer new_state, GsfXMLInExtDtor dtor,
1282 xmlChar const **attrs)
1283 {
1284 GsfXMLInInternal *state = (GsfXMLInInternal *)xin;
1285 GsfXMLInExtension *ext;
1286
1287 g_return_if_fail (xin != NULL);
1288 g_return_if_fail (doc != NULL);
1289 g_return_if_fail (doc->root_node != NULL);
1290
1291 ext = g_new (GsfXMLInExtension, 1);
1292 ext->doc = doc;
1293 ext->state = new_state;
1294 ext->dtor = dtor;
1295 if (!(ext->from_unknown = state->from_unknown_handler)) {
1296 GsfXMLInNodeInternal *node = (GsfXMLInNodeInternal *) xin->node;
1297 node->extensions = g_slist_prepend (node->extensions, ext);
1298 } else
1299 push_child (state, &doc->root_node->pub, -1, attrs, ext);
1300 }
1301
1302 /**
1303 * gsf_xml_in_doc_parse:
1304 * @doc: #GsfXMLInDoc
1305 * @input: #GsfInput
1306 * @user_state: arbitrary content stored in the parser
1307 *
1308 * Read an xml document from @input and parse based on the the descriptor in
1309 * @doc
1310 *
1311 * Returns: %FALSE on error
1312 **/
1313 gboolean
gsf_xml_in_doc_parse(GsfXMLInDoc * doc,GsfInput * input,gpointer user_state)1314 gsf_xml_in_doc_parse (GsfXMLInDoc *doc, GsfInput *input, gpointer user_state)
1315 {
1316 xmlParserCtxt *ctxt;
1317 GsfXMLInInternal state;
1318 gboolean res;
1319
1320 g_return_val_if_fail (doc != NULL, FALSE);
1321
1322 state.initialized = FALSE;
1323 ctxt = gsf_xml_parser_context_full (input, &gsfXMLInParser, &state);
1324 if (ctxt == NULL)
1325 return FALSE;
1326
1327 state.pub.doc = doc;
1328 state.pub.user_state = user_state;
1329 state.pub.content = g_string_sized_new (128);
1330 state.input = input;
1331 xmlParseDocument (ctxt);
1332 res = ctxt->wellFormed;
1333 xmlFreeParserCtxt (ctxt);
1334
1335 if (state.pub.content)
1336 g_string_free (state.pub.content, TRUE);
1337
1338 return res;
1339 }
1340
1341 static GsfXMLInNS *
gsf_xml_in_ns_copy(GsfXMLInNS * ns)1342 gsf_xml_in_ns_copy (GsfXMLInNS *ns) {
1343 GsfXMLInNS *res = g_new (GsfXMLInNS, 1);
1344 res->uri = ns->uri;
1345 res->ns_id = ns->ns_id;
1346 return res;
1347 }
1348
1349 GType
gsf_xml_in_ns_get_type(void)1350 gsf_xml_in_ns_get_type (void)
1351 {
1352 static GType type = 0;
1353
1354 if (type == 0)
1355 type = g_boxed_type_register_static
1356 ("GsfXMLInNS",
1357 (GBoxedCopyFunc) gsf_xml_in_ns_copy,
1358 (GBoxedFreeFunc) g_free);
1359
1360 return type;
1361 }
1362
1363 /**
1364 * gsf_xml_in_get_input:
1365 * @xin: #GsfXMLIn
1366 *
1367 * (New in 1.14.2)
1368 *
1369 * Returns: (transfer none): (but does not reference) the stream being parsed.
1370 **/
1371 GsfInput *
gsf_xml_in_get_input(GsfXMLIn const * xin)1372 gsf_xml_in_get_input (GsfXMLIn const *xin)
1373 {
1374 GsfXMLInInternal const *state = (GsfXMLInInternal const *)xin;
1375 return state->input;
1376 }
1377
1378 /**
1379 * gsf_xml_in_set_silent_unknowns:
1380 * @xin: #GsfXMLIn
1381 * @silent: whether to be silent about unknown tags
1382 *
1383 * (New in 1.14.33)
1384 *
1385 * This provides a means to silently ignore unknown tags in contexts where
1386 * they are expected.
1387 **/
1388 void
gsf_xml_in_set_silent_unknowns(GsfXMLIn * xin,gboolean silent)1389 gsf_xml_in_set_silent_unknowns (GsfXMLIn *xin, gboolean silent)
1390 {
1391 GsfXMLInInternal *state = (GsfXMLInInternal *)xin;
1392 state->silent_unknowns = silent;
1393 }
1394
1395
1396 /**
1397 * gsf_xml_in_check_ns:
1398 * @xin: #GsfXMLIn
1399 * @str: string to check
1400 * @ns_id: the namespace id
1401 *
1402 * According to @state is @str in the namespace @ns_id ?
1403 *
1404 * Returns: (transfer none) (nullable): a pointer to @str after the namespace
1405 * if successful, otherwise %NULL.
1406 **/
1407 char const *
gsf_xml_in_check_ns(GsfXMLIn const * xin,char const * str,unsigned int ns_id)1408 gsf_xml_in_check_ns (GsfXMLIn const *xin, char const *str, unsigned int ns_id)
1409 {
1410 GsfXMLInInternal const *state = (GsfXMLInInternal const *)xin;
1411 GsfXMLInNSInstance *inst;
1412
1413 /* If this is the default namespace there will not be an entry in the
1414 * ns_by_id array */
1415 if (ns_id >= state->ns_by_id->len ||
1416 NULL == (inst = g_ptr_array_index (state->ns_by_id, ns_id)) ||
1417 0 != strncmp (str, inst->tag, inst->taglen)) {
1418 if (state->default_ns_id >= 0 &&
1419 state->default_ns_id == (int)ns_id)
1420 return (NULL == strchr (str, ':')) ? str : NULL;
1421 return NULL;
1422 }
1423
1424 return str + inst->taglen;
1425 }
1426
1427 /**
1428 * gsf_xml_in_namecmp:
1429 * @xin: The #GsfXMLIn we are reading from.
1430 * @str: The potentially namespace qualified node name.
1431 * @ns_id: The name space id to check
1432 * @name: The target node name
1433 *
1434 * Checks to see if @str is the same as @ns_id::@name with either an explicit
1435 * namespace or the current default namespace.
1436 *
1437 * Returns: %TRUE if @str == @ns_id:@name according to @state.
1438 **/
1439 gboolean
gsf_xml_in_namecmp(GsfXMLIn const * xin,char const * str,unsigned int ns_id,char const * name)1440 gsf_xml_in_namecmp (GsfXMLIn const *xin, char const *str,
1441 unsigned int ns_id, char const *name)
1442 {
1443 GsfXMLInInternal const *state = (GsfXMLInInternal const *)xin;
1444 GsfXMLInNSInstance *inst;
1445
1446 /* check for default namespace as a likely choice */
1447 if (state->default_ns_id >= 0 &&
1448 state->default_ns_id == (int)ns_id &&
1449 0 == strcmp (name, str))
1450 return TRUE;
1451
1452 if (ns_id >= state->ns_by_id->len ||
1453 NULL == (inst = g_ptr_array_index (state->ns_by_id, ns_id)) ||
1454 0 != strncmp (str, inst->tag, inst->taglen))
1455 return FALSE;
1456 return 0 == strcmp (name, str + inst->taglen);
1457 }
1458
1459 /****************************************************************************/
1460
1461 enum {
1462 PROP_0,
1463 PROP_PRETTY_PRINT,
1464 PROP_SINK
1465 };
1466
1467 typedef enum {
1468 GSF_XML_OUT_NOCONTENT,
1469 GSF_XML_OUT_CHILD,
1470 GSF_XML_OUT_CHILD_PRETTY,
1471 GSF_XML_OUT_CONTENT
1472 } GsfXMLOutState;
1473
1474 typedef struct _GsfXMLOutPrivate {
1475 char *doc_type;
1476 GSList *stack;
1477 GsfXMLOutState state;
1478 unsigned indent;
1479 gboolean needs_header;
1480 gboolean pretty_print;
1481 } GsfXMLOutPrivate;
1482
G_DEFINE_TYPE_WITH_CODE(GsfXMLOut,gsf_xml_out,G_TYPE_OBJECT,G_ADD_PRIVATE (GsfXMLOut))1483 G_DEFINE_TYPE_WITH_CODE (GsfXMLOut, gsf_xml_out, G_TYPE_OBJECT,
1484 G_ADD_PRIVATE (GsfXMLOut))
1485
1486 static void
1487 gsf_xml_out_set_output (GsfXMLOut *xout, GsfOutput *output)
1488 {
1489 if (gsf_output_wrap (G_OBJECT (xout), output))
1490 xout->output = output;
1491 }
1492
1493 static void
gsf_xml_out_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)1494 gsf_xml_out_set_property (GObject *object,
1495 guint property_id,
1496 GValue const *value,
1497 GParamSpec *pspec)
1498 {
1499 GsfXMLOut *xout = (GsfXMLOut *)object;
1500 GsfXMLOutPrivate *priv = xout->priv;
1501
1502 switch (property_id) {
1503 case PROP_PRETTY_PRINT:
1504 priv->pretty_print = g_value_get_boolean (value);
1505 break;
1506 case PROP_SINK:
1507 gsf_xml_out_set_output (xout, g_value_get_object (value));
1508 break;
1509 default:
1510 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1511 break;
1512 }
1513 }
1514
1515 static void
gsf_xml_out_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1516 gsf_xml_out_get_property (GObject *object,
1517 guint property_id,
1518 GValue *value,
1519 GParamSpec *pspec)
1520 {
1521 GsfXMLOut const *xout = (GsfXMLOut const *)object;
1522 GsfXMLOutPrivate const *priv = xout->priv;
1523
1524 switch (property_id) {
1525 case PROP_PRETTY_PRINT:
1526 g_value_set_boolean (value, priv->pretty_print);
1527 break;
1528 case PROP_SINK:
1529 g_value_set_object (value, xout->output);
1530 break;
1531 default:
1532 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1533 break;
1534 }
1535 }
1536
1537 static void
gsf_xml_out_finalize(GObject * obj)1538 gsf_xml_out_finalize (GObject *obj)
1539 {
1540 GsfXMLOut *xout = GSF_XML_OUT (obj);
1541 g_free (xout->priv->doc_type);
1542 parent_class->finalize (obj);
1543 }
1544
1545 static void
gsf_xml_out_init(GsfXMLOut * xout)1546 gsf_xml_out_init (GsfXMLOut *xout)
1547 {
1548 GsfXMLOutPrivate *priv = gsf_xml_out_get_instance_private (xout);
1549
1550 xout->output = NULL;
1551 xout->priv = priv;
1552 priv->stack = NULL;
1553 priv->state = GSF_XML_OUT_CHILD;
1554 priv->indent = 0;
1555 priv->needs_header = TRUE;
1556 priv->doc_type = NULL;
1557 priv->pretty_print = TRUE;
1558 }
1559
1560 static void
gsf_xml_out_class_init(GsfXMLOutClass * klass)1561 gsf_xml_out_class_init (GsfXMLOutClass *klass)
1562 {
1563 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1564
1565 parent_class = g_type_class_peek_parent (gobject_class);
1566
1567 gobject_class->finalize = gsf_xml_out_finalize;
1568 gobject_class->get_property = gsf_xml_out_get_property;
1569 gobject_class->set_property = gsf_xml_out_set_property;
1570
1571 g_object_class_install_property
1572 (gobject_class, PROP_PRETTY_PRINT,
1573 g_param_spec_boolean ("pretty-print",
1574 _("Pretty print"),
1575 _("Should the output auto-indent elements to make reading easier?"),
1576 TRUE, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
1577
1578 g_object_class_install_property
1579 (gobject_class, PROP_SINK,
1580 g_param_spec_object ("sink",
1581 _("Sink"),
1582 _("The destination for writes"),
1583 GSF_OUTPUT_TYPE,
1584 G_PARAM_STATIC_STRINGS |
1585 G_PARAM_READWRITE |
1586 G_PARAM_CONSTRUCT_ONLY));
1587 }
1588
1589 /**
1590 * gsf_xml_out_new:
1591 * @output: #GsfOutput
1592 *
1593 * Create an XML output stream.
1594 *
1595 * Returns: #GsfXMLOut
1596 */
1597 GsfXMLOut *
gsf_xml_out_new(GsfOutput * output)1598 gsf_xml_out_new (GsfOutput *output)
1599 {
1600 g_return_val_if_fail (GSF_IS_OUTPUT (output), NULL);
1601
1602 return g_object_new (GSF_XML_OUT_TYPE,
1603 "sink", output,
1604 NULL);
1605 }
1606
1607 /**
1608 * gsf_xml_out_set_doc_type:
1609 * @xout: #GsfXMLOut
1610 * @type: the document type declaration
1611 *
1612 * Store some optional <!DOCTYPE .. > content
1613 **/
1614 void
gsf_xml_out_set_doc_type(GsfXMLOut * xout,char const * type)1615 gsf_xml_out_set_doc_type (GsfXMLOut *xout, char const *type)
1616 {
1617 GsfXMLOutPrivate *priv = xout->priv;
1618 g_free (priv->doc_type);
1619 priv->doc_type = g_strdup (type);
1620 }
1621
1622 /**
1623 * gsf_xml_out_get_pretty_print:
1624 * @xout: #GsfXMLOut
1625 *
1626 * Returns: the current state of the pretty-print flag. Note, that
1627 * gsf_xml_out_set_pretty_print will return the same value.
1628 **/
1629 gboolean
gsf_xml_out_get_pretty_print(GsfXMLOut * xout)1630 gsf_xml_out_get_pretty_print (GsfXMLOut *xout)
1631 {
1632 g_return_val_if_fail (GSF_IS_XML_OUT (xout), TRUE);
1633 return xout->priv->pretty_print;
1634 }
1635
1636 /**
1637 * gsf_xml_out_set_pretty_print:
1638 * @xout: #GsfXMLOut
1639 * @pp: new state of pretty-print flag.
1640 *
1641 * Returns: the previous state of the pretty-print flag.
1642 **/
1643 gboolean
gsf_xml_out_set_pretty_print(GsfXMLOut * xout,gboolean pp)1644 gsf_xml_out_set_pretty_print (GsfXMLOut *xout, gboolean pp)
1645 {
1646 gboolean res;
1647
1648 g_return_val_if_fail (GSF_IS_XML_OUT (xout), TRUE);
1649
1650 pp = !!pp;
1651
1652 res = xout->priv->pretty_print;
1653 if (pp != res) {
1654 xout->priv->pretty_print = pp;
1655 g_object_notify (G_OBJECT (xout), "pretty-print");
1656 }
1657 return res;
1658 }
1659
1660 static inline void
gsf_xml_out_indent(GsfXMLOut * xout)1661 gsf_xml_out_indent (GsfXMLOut *xout)
1662 {
1663 GsfXMLOutPrivate *priv = xout->priv;
1664 static char const spaces [] =
1665 " "
1666 " "
1667 " "
1668 " "
1669 " "
1670 " ";
1671 if (priv->pretty_print) {
1672 unsigned i;
1673 for (i = priv->indent ; i > (sizeof (spaces)/2) ; i -= sizeof (spaces)/2)
1674 gsf_output_write (xout->output, sizeof (spaces) - 1, spaces);
1675 gsf_output_write (xout->output, i*2, spaces);
1676 }
1677 }
1678
1679 /**
1680 * gsf_xml_out_start_element:
1681 * @xout: #GsfXMLOut
1682 * @id: Element name
1683 *
1684 * Output a start element @id, if necessary preceeded by an XML declaration.
1685 */
1686 void
gsf_xml_out_start_element(GsfXMLOut * xout,char const * id)1687 gsf_xml_out_start_element (GsfXMLOut *xout, char const *id)
1688 {
1689 GsfXMLOutPrivate *priv;
1690
1691 g_return_if_fail (id != NULL);
1692 g_return_if_fail (xout != NULL);
1693 priv = xout->priv;
1694
1695 if (priv->needs_header) {
1696 static char const header0[] =
1697 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1698 gsf_output_write (xout->output, sizeof (header0) - 1, header0);
1699 if (priv->doc_type != NULL)
1700 gsf_output_puts (xout->output, priv->doc_type);
1701 priv->needs_header = FALSE;
1702 }
1703 if (priv->state == GSF_XML_OUT_NOCONTENT) {
1704 if (priv->pretty_print)
1705 gsf_output_write (xout->output, 2, ">\n");
1706 else
1707 gsf_output_write (xout->output, 1, ">");
1708 }
1709
1710 gsf_xml_out_indent (xout);
1711 gsf_output_printf (xout->output, "<%s", id);
1712
1713 priv->stack = g_slist_prepend (priv->stack, (gpointer)id);
1714 priv->indent++;
1715 priv->state = GSF_XML_OUT_NOCONTENT;
1716 }
1717
1718 /**
1719 * gsf_xml_out_end_element:
1720 * @xout: #GsfXMLOut
1721 *
1722 * Closes/ends an XML element.
1723 *
1724 * Returns: (transfer none): the element that has been closed.
1725 */
1726 char const *
gsf_xml_out_end_element(GsfXMLOut * xout)1727 gsf_xml_out_end_element (GsfXMLOut *xout)
1728 {
1729 char const *id;
1730 GsfXMLOutPrivate *priv;
1731
1732 g_return_val_if_fail (xout != NULL, NULL);
1733 priv = xout->priv;
1734 g_return_val_if_fail (priv->stack != NULL, NULL);
1735
1736 id = priv->stack->data;
1737 priv->stack = g_slist_remove (priv->stack, id);
1738 priv->indent--;
1739 switch (priv->state) {
1740 case GSF_XML_OUT_NOCONTENT:
1741 if (priv->pretty_print)
1742 gsf_output_write (xout->output, 3, "/>\n");
1743 else
1744 gsf_output_write (xout->output, 2, "/>");
1745 break;
1746
1747 case GSF_XML_OUT_CHILD_PRETTY:
1748 gsf_xml_out_indent (xout);
1749 /* fall through */
1750 case GSF_XML_OUT_CHILD:
1751 case GSF_XML_OUT_CONTENT:
1752 if (priv->pretty_print)
1753 gsf_output_printf (xout->output, "</%s>\n", id);
1754 else
1755 gsf_output_printf (xout->output, "</%s>", id);
1756 }
1757 priv->state = priv->pretty_print ? GSF_XML_OUT_CHILD_PRETTY : GSF_XML_OUT_CHILD;
1758 return id;
1759 }
1760
1761 /**
1762 * gsf_xml_out_simple_element:
1763 * @xout: #GsfXMLOut
1764 * @id: Element name
1765 * @content: Content of the element
1766 *
1767 * Convenience routine to output a simple @id element with content @content.
1768 **/
1769 void
gsf_xml_out_simple_element(GsfXMLOut * xout,char const * id,char const * content)1770 gsf_xml_out_simple_element (GsfXMLOut *xout, char const *id,
1771 char const *content)
1772 {
1773 gsf_xml_out_start_element (xout, id);
1774 if (content != NULL)
1775 gsf_xml_out_add_cstr (xout, NULL, content);
1776 gsf_xml_out_end_element (xout);
1777 }
1778
1779 /**
1780 * gsf_xml_out_simple_int_element:
1781 * @xout: #GsfXMLOut
1782 * @id: Element name
1783 * @val: Element value
1784 *
1785 * Convenience routine to output an element @id with integer value @val.
1786 **/
1787 void
gsf_xml_out_simple_int_element(GsfXMLOut * xout,char const * id,int val)1788 gsf_xml_out_simple_int_element (GsfXMLOut *xout, char const *id, int val)
1789 {
1790 gsf_xml_out_start_element (xout, id);
1791 gsf_xml_out_add_int (xout, NULL, val);
1792 gsf_xml_out_end_element (xout);
1793 }
1794
1795 /**
1796 * gsf_xml_out_simple_float_element:
1797 * @xout: #GsfXMLOut
1798 * @id: Element name
1799 * @val: Element value
1800 * @precision: the number of significant digits to use, -1 meaning "enough".
1801 *
1802 * Convenience routine to output an element @id with float value @val using
1803 * @precision significant digits.
1804 **/
1805 void
gsf_xml_out_simple_float_element(GsfXMLOut * xout,char const * id,double val,int precision)1806 gsf_xml_out_simple_float_element (GsfXMLOut *xout, char const *id,
1807 double val, int precision)
1808 {
1809 gsf_xml_out_start_element (xout, id);
1810 gsf_xml_out_add_float (xout, NULL, val, precision);
1811 gsf_xml_out_end_element (xout);
1812 }
1813
1814 static void
close_tag_if_neccessary(GsfXMLOut * xout)1815 close_tag_if_neccessary (GsfXMLOut* xout)
1816 {
1817 GsfXMLOutPrivate *priv = xout->priv;
1818 if (priv->state == GSF_XML_OUT_NOCONTENT) {
1819 priv->state = GSF_XML_OUT_CONTENT;
1820 gsf_output_write (xout->output, 1, ">");
1821 }
1822 }
1823
1824 /**
1825 * gsf_xml_out_add_cstr_unchecked:
1826 * @xout: #GsfXMLOut
1827 * @id: (allow-none): tag id, or %NULL for node content
1828 * @val_utf8: (allow-none): a utf8 encoded string to export
1829 *
1830 * dump @val_utf8 to an attribute named @id without checking to see if
1831 * the content needs escaping. A useful performance enhancement when
1832 * the application knows that structure of the content well. If
1833 * @val_utf8 is %NULL do nothing (no warning, no output)
1834 **/
1835 void
gsf_xml_out_add_cstr_unchecked(GsfXMLOut * xout,char const * id,char const * val_utf8)1836 gsf_xml_out_add_cstr_unchecked (GsfXMLOut *xout, char const *id,
1837 char const *val_utf8)
1838 {
1839 g_return_if_fail (xout != NULL);
1840
1841 if (val_utf8 == NULL)
1842 return;
1843
1844 if (id == NULL) {
1845 close_tag_if_neccessary (xout);
1846 gsf_output_write (xout->output, strlen (val_utf8), val_utf8);
1847 } else
1848 gsf_output_printf (xout->output, " %s=\"%s\"", id, val_utf8);
1849 }
1850
1851 /**
1852 * gsf_xml_out_add_cstr:
1853 * @xout: #GsfXMLOut
1854 * @id: (allow-none): tag id, or %NULL for node content
1855 * @val_utf8: (allow-none): a utf8 encoded string
1856 *
1857 * dump @val_utf8 to an attribute named @id or as the nodes content escaping
1858 * characters as necessary. If @val_utf8 is %NULL do nothing (no warning, no
1859 * output)
1860 **/
1861 void
gsf_xml_out_add_cstr(GsfXMLOut * xout,char const * id,char const * val_utf8)1862 gsf_xml_out_add_cstr (GsfXMLOut *xout, char const *id,
1863 char const *val_utf8)
1864 {
1865 guint8 const *cur = val_utf8;
1866 guint8 const *start = val_utf8;
1867
1868 g_return_if_fail (xout != NULL);
1869
1870 if (val_utf8 == NULL)
1871 return;
1872
1873 if (id == NULL) {
1874 close_tag_if_neccessary (xout);
1875 } else
1876 gsf_output_printf (xout->output, " %s=\"", id);
1877 while (*cur != '\0') {
1878 if (*cur == '<') {
1879 if (cur != start)
1880 gsf_output_write (xout->output, cur-start, start);
1881 start = ++cur;
1882 gsf_output_write (xout->output, 4, "<");
1883 } else if (*cur == '>') {
1884 if (cur != start)
1885 gsf_output_write (xout->output, cur-start, start);
1886 start = ++cur;
1887 gsf_output_write (xout->output, 4, ">");
1888 } else if (*cur == '&') {
1889 if (cur != start)
1890 gsf_output_write (xout->output, cur-start, start);
1891 start = ++cur;
1892 gsf_output_write (xout->output, 5, "&");
1893 } else if (*cur == '"') {
1894 if (cur != start)
1895 gsf_output_write (xout->output, cur-start, start);
1896 start = ++cur;
1897 gsf_output_write (xout->output, 6, """);
1898 } else if ((*cur == '\n' || *cur == '\r' || *cur == '\t') &&
1899 id != NULL) {
1900 guint8 buf[8];
1901 sprintf (buf, "&#%d;", *cur);
1902
1903 if (cur != start)
1904 gsf_output_write (xout->output, cur-start, start);
1905 start = ++cur;
1906
1907 gsf_output_write (xout->output, strlen (buf), buf);
1908 } else if ((*cur >= 0x20 && *cur != 0x7f) ||
1909 (*cur == '\n' || *cur == '\r' || *cur == '\t')) {
1910 cur++;
1911 } else {
1912 /*
1913 * This is immensely pathetic, but XML 1.0 does not
1914 * allow certain characters to be encoded. XML 1.1
1915 * does allow this, but libxml2 does not support it.
1916 */
1917 g_warning ("Unknown char 0x%02x in string", *cur);
1918
1919 if (cur != start)
1920 gsf_output_write (xout->output, cur-start, start);
1921 start = ++cur;
1922 }
1923 }
1924 if (cur != start)
1925 gsf_output_write (xout->output, cur-start, start);
1926 if (id != NULL)
1927 gsf_output_write (xout->output, 1, "\"");
1928 }
1929
1930 /**
1931 * gsf_xml_out_add_bool:
1932 * @xout: #GsfXMLOut
1933 * @id: (allow-none): tag id, or %NULL for node content
1934 * @val: a boolean
1935 *
1936 * dump boolean value @val to an attribute named @id or as the nodes content
1937 * Use '1' or '0' to simplify import
1938 **/
1939 void
gsf_xml_out_add_bool(GsfXMLOut * xout,char const * id,gboolean val)1940 gsf_xml_out_add_bool (GsfXMLOut *xout, char const *id,
1941 gboolean val)
1942 {
1943 gsf_xml_out_add_cstr_unchecked (xout, id,
1944 val ? "1" : "0");
1945 }
1946
1947 /**
1948 * gsf_xml_out_add_int:
1949 * @xout: #GsfXMLOut
1950 * @id: (allow-none): tag id, or %NULL for node content
1951 * @val: the value
1952 *
1953 * dump integer value @val to an attribute named @id or as the nodes content
1954 **/
1955 void
gsf_xml_out_add_int(GsfXMLOut * xout,char const * id,int val)1956 gsf_xml_out_add_int (GsfXMLOut *xout, char const *id,
1957 int val)
1958 {
1959 char buf [4 * sizeof (int)];
1960 sprintf (buf, "%d", val);
1961 gsf_xml_out_add_cstr_unchecked (xout, id, buf);
1962 }
1963
1964 /**
1965 * gsf_xml_out_add_uint:
1966 * @xout: #GsfXMLOut
1967 * @id: (allow-none): tag id, or %NULL for node content
1968 * @val: the value
1969 *
1970 * dump unsigned integer value @val to an attribute named @id or as the nodes
1971 * content
1972 **/
1973 void
gsf_xml_out_add_uint(GsfXMLOut * xout,char const * id,unsigned int val)1974 gsf_xml_out_add_uint (GsfXMLOut *xout, char const *id,
1975 unsigned int val)
1976 {
1977 char buf [4 * sizeof (unsigned int)];
1978 sprintf (buf, "%u", val);
1979 gsf_xml_out_add_cstr_unchecked (xout, id, buf);
1980 }
1981
1982 /**
1983 * gsf_xml_out_add_float:
1984 * @xout: #GsfXMLOut
1985 * @id: (allow-none): tag id, or %NULL for node content
1986 * @val: the value
1987 * @precision: the number of significant digits to use, -1 meaning "enough".
1988 *
1989 * dump float value @val to an attribute named @id or as the nodes
1990 * content with precision @precision. The number will be formattted
1991 * according to the "C" locale.
1992 **/
1993 void
gsf_xml_out_add_float(GsfXMLOut * xout,char const * id,double val,int precision)1994 gsf_xml_out_add_float (GsfXMLOut *xout, char const *id,
1995 double val, int precision)
1996 {
1997 char buf[G_ASCII_DTOSTR_BUF_SIZE + DBL_DIG + 17];
1998
1999 if (precision < 0 || precision > 17) {
2000 g_ascii_dtostr (buf, sizeof (buf), val);
2001 } else {
2002 char format_str[4 * sizeof (int) + 10];
2003 sprintf (format_str, "%%.%dg", precision);
2004 g_ascii_formatd (buf, sizeof (buf), format_str, val);
2005 }
2006 gsf_xml_out_add_cstr_unchecked (xout, id, buf);
2007 }
2008
2009 /**
2010 * gsf_xml_out_add_color:
2011 * @xout: #GsfXMLOut
2012 * @id: (allow-none): tag id, or %NULL for node content
2013 * @r: Red value
2014 * @g: Green value
2015 * @b: Blue value
2016 *
2017 * dump Color @r.@g.@b to an attribute named @id or as the nodes content
2018 **/
2019 void
gsf_xml_out_add_color(GsfXMLOut * xout,char const * id,unsigned int r,unsigned int g,unsigned int b)2020 gsf_xml_out_add_color (GsfXMLOut *xout, char const *id,
2021 unsigned int r, unsigned int g, unsigned int b)
2022 {
2023 char buf [3 * 4 * sizeof (unsigned int) + 1];
2024 sprintf (buf, "%X:%X:%X", r, g, b);
2025 gsf_xml_out_add_cstr_unchecked (xout, id, buf);
2026 }
2027
2028 /**
2029 * gsf_xml_out_add_enum:
2030 * @xout: #GsfXMLOut
2031 * @id: (allow-none): tag id, or %NULL for node content
2032 * @etype: #GType
2033 * @val: enum element number
2034 *
2035 * Output the name of value @val of enumeration type @etype.
2036 **/
2037 void
gsf_xml_out_add_enum(GsfXMLOut * xout,char const * id,GType etype,gint val)2038 gsf_xml_out_add_enum (GsfXMLOut *xout, char const *id, GType etype, gint val)
2039 {
2040 GEnumClass *eclass = G_ENUM_CLASS (g_type_class_ref (etype));
2041 GEnumValue *ev = g_enum_get_value (eclass, val);
2042 g_type_class_unref (eclass);
2043
2044 if (ev)
2045 gsf_xml_out_add_cstr_unchecked (xout, id, ev->value_name);
2046 else
2047 g_warning ("Invalid value %d for type %s",
2048 val, g_type_name (etype));
2049 }
2050
2051 /**
2052 * gsf_xml_out_add_gvalue:
2053 * @xout: #GsfXMLOut
2054 * @id: (allow-none): tag id, or %NULL for node content
2055 * @val: #GValue
2056 *
2057 * Output the value of @val as a string. Does NOT store any type information
2058 * with the string, just thevalue.
2059 **/
2060 void
gsf_xml_out_add_gvalue(GsfXMLOut * xout,char const * id,GValue const * val)2061 gsf_xml_out_add_gvalue (GsfXMLOut *xout, char const *id, GValue const *val)
2062 {
2063 GType t;
2064 g_return_if_fail (xout != NULL);
2065 g_return_if_fail (val != NULL);
2066
2067 t = G_VALUE_TYPE (val);
2068 switch (t) {
2069 case G_TYPE_CHAR: {
2070 char c[2] = { 0, 0 };
2071 c[0] = (char)g_value_get_schar (val);
2072 /* FIXME: What if we are in 0x80-0xff? */
2073 gsf_xml_out_add_cstr (xout, id, c);
2074 break;
2075 }
2076
2077 case G_TYPE_UCHAR: {
2078 unsigned char c[2] = { 0, 0 };
2079 c[0] = g_value_get_uchar (val);
2080 /* FIXME: What if we are in 0x80-0xff? */
2081 gsf_xml_out_add_cstr (xout, id, c);
2082 break;
2083 }
2084
2085 case G_TYPE_BOOLEAN:
2086 gsf_xml_out_add_cstr (xout, id,
2087 g_value_get_boolean (val) ? "t" : "f");
2088 break;
2089 case G_TYPE_INT:
2090 gsf_xml_out_add_int (xout, id, g_value_get_int (val));
2091 break;
2092 case G_TYPE_UINT:
2093 gsf_xml_out_add_uint (xout, id, g_value_get_uint (val));
2094 break;
2095 case G_TYPE_LONG:
2096 gsf_xml_out_add_uint (xout, id, g_value_get_long (val));
2097 break;
2098 case G_TYPE_ULONG:
2099 gsf_xml_out_add_uint (xout, id, g_value_get_ulong (val));
2100 break;
2101 case G_TYPE_ENUM:
2102 gsf_xml_out_add_cstr (xout, id,
2103 glade_string_from_enum (t, g_value_get_enum (val)));
2104 break;
2105 case G_TYPE_FLAGS:
2106 gsf_xml_out_add_cstr (xout, id,
2107 glade_string_from_flags (t, g_value_get_flags (val)));
2108 break;
2109 case G_TYPE_FLOAT:
2110 gsf_xml_out_add_float (xout, id, g_value_get_float (val), -1);
2111 break;
2112 case G_TYPE_DOUBLE:
2113 gsf_xml_out_add_float (xout, id, g_value_get_double (val), -1);
2114 break;
2115 case G_TYPE_STRING:
2116 gsf_xml_out_add_cstr (xout, id, g_value_get_string (val));
2117 break;
2118
2119 default:
2120 if (GSF_TIMESTAMP_TYPE == t) {
2121 GsfTimestamp const *ts = g_value_get_boxed (val);
2122 char *str = gsf_timestamp_as_string (ts);
2123 gsf_xml_out_add_cstr (xout, id, str);
2124 g_free (str);
2125 break;
2126 }
2127 }
2128
2129 /* FIXME FIXME FIXME Add some error checking */
2130 }
2131
2132 /**
2133 * gsf_xml_out_add_base64:
2134 * @xout: #GsfXMLOut
2135 * @id: (allow-none): tag id, or %NULL for node content
2136 * @data: (array length=len): Data to be written
2137 * @len: Length of data
2138 *
2139 * Dump @len bytes in @data into the content of node @id using base64
2140 **/
2141 void
gsf_xml_out_add_base64(GsfXMLOut * xout,char const * id,guint8 const * data,unsigned int len)2142 gsf_xml_out_add_base64 (GsfXMLOut *xout, char const *id,
2143 guint8 const *data, unsigned int len)
2144 {
2145 /* We could optimize and stream right to the output,
2146 * or even just keep the buffer around
2147 * for now we start simple */
2148 guint8 *tmp = gsf_base64_encode_simple (data, len);
2149 if (tmp == NULL)
2150 return;
2151 if (id != NULL)
2152 g_warning ("Stream a binary blob into an attribute ??");
2153 gsf_xml_out_add_cstr_unchecked (xout, id, tmp);
2154 g_free (tmp);
2155 }
2156
2157 /**
2158 * gsf_xml_out_get_output:
2159 * @xout: #GsfXMLOut
2160 *
2161 * Get the #GsfOutput we are writing to..
2162 *
2163 * Returns: (transfer none) (nullable): #GsfInput
2164 **/
2165 GsfOutput *
gsf_xml_out_get_output(GsfXMLOut const * xout)2166 gsf_xml_out_get_output (GsfXMLOut const *xout)
2167 {
2168 g_return_val_if_fail (xout != NULL, NULL);
2169 return xout->output;
2170 }
2171