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 &lt;!DOCTYPE .. &gt; 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, "&lt;");
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, "&gt;");
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, "&amp;");
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, "&quot;");
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