1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 #include "evolution-data-server-config.h"
20
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <errno.h>
24
25 #ifndef O_BINARY
26 #define O_BINARY 0
27 #endif
28
29 #include <libxml/parser.h>
30 #include <libxml/catalog.h>
31 #include <libxml/tree.h>
32 #include <libxml/xpathInternals.h>
33
34 #include <glib/gstdio.h>
35
36 #include "e-xml-utils.h"
37
38 #ifdef G_OS_WIN32
39 #define fsync(fd) 0
40 #endif
41
42 /**
43 * e_xml_initialize_in_main: (skip)
44 *
45 * Initializes libxml library global memory. This should be called
46 * in the main thread. The function does nothing, when it had been
47 * called already.
48 *
49 * Since: 3.28
50 **/
51 void
e_xml_initialize_in_main(void)52 e_xml_initialize_in_main (void)
53 {
54 static volatile guint called = 0;
55
56 if (!g_atomic_int_or (&called, 1)) {
57 xmlInitMemory ();
58 xmlInitThreads ();
59 xmlInitGlobals ();
60 xmlInitializeCatalog ();
61 xmlInitParser ();
62 }
63 }
64
65 /**
66 * e_xml_parse_file: (skip)
67 * @filename: path to an XML file
68 *
69 * Reads a local XML file and parses the contents into an XML document
70 * structure. If the XML file cannot be read or its contents are malformed,
71 * the function returns %NULL.
72 *
73 * Returns: (transfer full): an XML document structure, or %NULL
74 **/
75 xmlDoc *
e_xml_parse_file(const gchar * filename)76 e_xml_parse_file (const gchar *filename)
77 {
78 xmlDocPtr result = NULL;
79
80 GMappedFile *mapped_file;
81
82 mapped_file = g_mapped_file_new (filename, FALSE, NULL);
83 if (mapped_file) {
84 result = xmlParseMemory (
85 g_mapped_file_get_contents (mapped_file),
86 g_mapped_file_get_length (mapped_file));
87 g_mapped_file_unref (mapped_file);
88 }
89
90 return result;
91 }
92
93 /**
94 * e_xml_save_file:
95 * @filename: path to a file to save to
96 * @doc: an XML document structure
97 *
98 * Writes the given XML document structure to the file given by @filename.
99 * If an error occurs while saving, the function returns -1 and sets errno.
100 *
101 * Returns: 0 on success, -1 on failure
102 **/
103 gint
e_xml_save_file(const gchar * filename,xmlDoc * doc)104 e_xml_save_file (const gchar *filename,
105 xmlDoc *doc)
106 {
107 gchar *filesave;
108 xmlChar *xmlbuf;
109 gsize n, written = 0;
110 gint ret, fd, size;
111 gint errnosave;
112 gssize w;
113 gchar *dirname = g_path_get_dirname (filename);
114 gchar *basename = g_path_get_basename (filename);
115 gchar *savebasename = g_strconcat (".#", basename, NULL);
116
117 g_free (basename);
118 filesave = g_build_filename (dirname, savebasename, NULL);
119 g_free (savebasename);
120 g_free (dirname);
121
122 fd = g_open (filesave, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
123 if (fd == -1) {
124 g_free (filesave);
125 return -1;
126 }
127
128 xmlDocDumpFormatMemory (doc, &xmlbuf, &size, TRUE);
129 if (size <= 0) {
130 close (fd);
131 g_unlink (filesave);
132 g_free (filesave);
133 errno = ENOMEM;
134 return -1;
135 }
136
137 n = (gsize) size;
138 do {
139 do {
140 w = write (fd, xmlbuf + written, n - written);
141 } while (w == -1 && errno == EINTR);
142
143 if (w > 0)
144 written += w;
145 } while (w != -1 && written < n);
146
147 xmlFree (xmlbuf);
148
149 if (written < n || fsync (fd) == -1) {
150 errnosave = errno;
151 close (fd);
152 g_unlink (filesave);
153 g_free (filesave);
154 errno = errnosave;
155 return -1;
156 }
157
158 while ((ret = close (fd)) == -1 && errno == EINTR)
159 ;
160
161 if (ret == -1) {
162 g_free (filesave);
163 return -1;
164 }
165
166 if (g_rename (filesave, filename) == -1) {
167 errnosave = errno;
168 g_unlink (filesave);
169 g_free (filesave);
170 errno = errnosave;
171 return -1;
172 }
173 g_free (filesave);
174
175 return 0;
176 }
177
178 /**
179 * e_xml_get_child_by_name: (skip)
180 * @parent: an XML node structure
181 * @child_name: element name of a child node
182 *
183 * Attempts to find a child element of @parent named @child_name.
184 * If no such child exists, the function returns %NULL.
185 *
186 * Returns: (nullable): a child XML node structure, or %NULL
187 **/
188 xmlNode *
e_xml_get_child_by_name(const xmlNode * parent,const xmlChar * child_name)189 e_xml_get_child_by_name (const xmlNode *parent,
190 const xmlChar *child_name)
191 {
192 xmlNode *child;
193
194 g_return_val_if_fail (parent != NULL, NULL);
195 g_return_val_if_fail (child_name != NULL, NULL);
196
197 for (child = parent->xmlChildrenNode; child != NULL; child = child->next) {
198 if (xmlStrcmp (child->name, child_name) == 0) {
199 return child;
200 }
201 }
202 return NULL;
203 }
204
205 /**
206 * e_xml_parse_data: (skip)
207 * @data: (array length=length) (element-type guint8): an XML data
208 * @length: length of data, should be greated than zero
209 *
210 * Parses XML data into an #xmlDocPtr. Free returned pointer
211 * with xmlFreeDoc(), when no longer needed.
212 *
213 * Returns: (nullable) (transfer full): a new #xmlDocPtr with parsed @data,
214 * or %NULL on error.
215 *
216 * Since: 3.26
217 **/
218 xmlDoc *
e_xml_parse_data(gconstpointer data,gsize length)219 e_xml_parse_data (gconstpointer data,
220 gsize length)
221 {
222 g_return_val_if_fail (data != NULL, NULL);
223 g_return_val_if_fail (length > 0, NULL);
224
225 return xmlReadMemory (data, length, "data.xml", NULL, XML_PARSE_NOWARNING | XML_PARSE_RECOVER);
226 }
227
228 /**
229 * e_xml_new_xpath_context_with_namespaces: (skip)
230 * @doc: an #xmlDocPtr
231 * @...: %NULL-terminated list of pairs (prefix, href) with namespaces
232 *
233 * Creates a new #xmlXPathContextPtr on @doc with preregistered
234 * namespaces. The namepsaces are pair of (prefix, href), terminated
235 * by %NULL.
236 *
237 * Returns: (transfer full): a new #xmlXPathContextPtr. Free the returned
238 * pointer with xmlXPathFreeContext() when no longer needed.
239 *
240 * Since: 3.26
241 **/
242 xmlXPathContext *
e_xml_new_xpath_context_with_namespaces(xmlDoc * doc,...)243 e_xml_new_xpath_context_with_namespaces (xmlDoc *doc,
244 ...)
245 {
246 xmlXPathContextPtr xpath_ctx;
247 va_list va;
248 const gchar *prefix;
249
250 g_return_val_if_fail (doc != NULL, NULL);
251
252 xpath_ctx = xmlXPathNewContext (doc);
253 g_return_val_if_fail (xpath_ctx != NULL, NULL);
254
255 va_start (va, doc);
256
257 while (prefix = va_arg (va, const gchar *), prefix) {
258 const gchar *href = va_arg (va, const gchar *);
259
260 if (!href) {
261 g_warn_if_fail (href != NULL);
262 break;
263 }
264
265 xmlXPathRegisterNs (xpath_ctx, (const xmlChar *) prefix, (const xmlChar *) href);
266 }
267
268 va_end (va);
269
270 return xpath_ctx;
271 }
272
273 /**
274 * e_xml_xpath_context_register_namespaces: (skip)
275 * @xpath_ctx: an #xmlXPathContextPtr
276 * @prefix: namespace prefix
277 * @href: namespace href
278 * @...: %NULL-terminated list of pairs (prefix, href) with additional namespaces
279 *
280 * Registers one or more additional namespaces. It's a caller's error
281 * to try to register a namespace with the same prefix again, unless
282 * the prefix uses the same namespace href.
283 *
284 * Since: 3.26
285 **/
286 void
e_xml_xpath_context_register_namespaces(xmlXPathContext * xpath_ctx,const gchar * prefix,const gchar * href,...)287 e_xml_xpath_context_register_namespaces (xmlXPathContext *xpath_ctx,
288 const gchar *prefix,
289 const gchar *href,
290 ...)
291 {
292 va_list va;
293 const gchar *used_href;
294
295 g_return_if_fail (xpath_ctx != NULL);
296 g_return_if_fail (prefix != NULL);
297 g_return_if_fail (href != NULL);
298
299 used_href = (const gchar *) xmlXPathNsLookup (xpath_ctx, (const xmlChar *) prefix);
300 if (used_href && g_strcmp0 (used_href, href) != 0) {
301 g_warning ("%s: Trying to register prefix '%s' with href '%s', but it already points to '%s'",
302 G_STRFUNC, prefix, href, used_href);
303 } else if (!used_href) {
304 xmlXPathRegisterNs (xpath_ctx, (const xmlChar *) prefix, (const xmlChar *) href);
305 }
306
307 va_start (va, href);
308
309 while (prefix = va_arg (va, const gchar *), prefix) {
310 href = va_arg (va, const gchar *);
311
312 if (!href) {
313 g_warn_if_fail (href != NULL);
314 break;
315 }
316
317 used_href = (const gchar *) xmlXPathNsLookup (xpath_ctx, (const xmlChar *) prefix);
318 if (used_href && g_strcmp0 (used_href, href) != 0) {
319 g_warning ("%s: Trying to register prefix '%s' with href '%s', but it already points to '%s'",
320 G_STRFUNC, prefix, href, used_href);
321 } else if (!used_href) {
322 xmlXPathRegisterNs (xpath_ctx, (const xmlChar *) prefix, (const xmlChar *) href);
323 }
324 }
325
326 va_end (va);
327 }
328
329 /**
330 * e_xml_xpath_eval: (skip)
331 * @xpath_ctx: an #xmlXPathContextPtr
332 * @format: printf-like format specifier of path to evaluate
333 * @...: arguments for the @format
334 *
335 * Evaluates path specified by @format and returns its #xmlXPathObjectPtr,
336 * in case the path evaluates to a non-empty node set. See also
337 * e_xml_xpath_eval_as_string() which evaluates the path to string.
338 *
339 * Returns: (nullable) (transfer full): a new #xmlXPathObjectPtr which
340 * references given path, or %NULL if path cannot be found or when
341 * it evaluates to an empty nodeset. Free returned pointer with
342 * xmlXPathFreeObject(), when no longer needed.
343 *
344 * Since: 3.26
345 **/
346 xmlXPathObject *
e_xml_xpath_eval(xmlXPathContext * xpath_ctx,const gchar * format,...)347 e_xml_xpath_eval (xmlXPathContext *xpath_ctx,
348 const gchar *format,
349 ...)
350 {
351 xmlXPathObjectPtr object;
352 va_list va;
353 gchar *expr;
354
355 g_return_val_if_fail (xpath_ctx != NULL, NULL);
356 g_return_val_if_fail (format != NULL, NULL);
357
358 va_start (va, format);
359 expr = g_strdup_vprintf (format, va);
360 va_end (va);
361
362 object = xmlXPathEvalExpression ((const xmlChar *) expr, xpath_ctx);
363 g_free (expr);
364
365 if (!object)
366 return NULL;
367
368 if (object->type == XPATH_NODESET &&
369 xmlXPathNodeSetIsEmpty (object->nodesetval)) {
370 xmlXPathFreeObject (object);
371 return NULL;
372 }
373
374 return object;
375 }
376
377 /**
378 * e_xml_xpath_eval_as_string: (skip)
379 * @xpath_ctx: an #xmlXPathContextPtr
380 * @format: printf-like format specifier of path to evaluate
381 * @...: arguments for the @format
382 *
383 * Evaluates path specified by @format and returns its result as string,
384 * in case the path evaluates to a non-empty node set. See also
385 * e_xml_xpath_eval() which evaluates the path to an #xmlXPathObjectPtr.
386 *
387 * Returns: (nullable) (transfer full): a new string which contains value
388 * of the given path, or %NULL if path cannot be found or when
389 * it evaluates to an empty nodeset. Free returned pointer with
390 * g_free(), when no longer needed.
391 *
392 * Since: 3.26
393 **/
394 gchar *
e_xml_xpath_eval_as_string(xmlXPathContext * xpath_ctx,const gchar * format,...)395 e_xml_xpath_eval_as_string (xmlXPathContext *xpath_ctx,
396 const gchar *format,
397 ...)
398 {
399 xmlXPathObjectPtr object;
400 va_list va;
401 gchar *expr, *value;
402
403 g_return_val_if_fail (xpath_ctx != NULL, NULL);
404 g_return_val_if_fail (format != NULL, NULL);
405
406 va_start (va, format);
407 expr = g_strdup_vprintf (format, va);
408 va_end (va);
409
410 if (!g_str_has_prefix (format, "string(")) {
411 gchar *tmp = expr;
412
413 expr = g_strconcat ("string(", expr, ")", NULL);
414
415 g_free (tmp);
416 }
417
418 object = e_xml_xpath_eval (xpath_ctx, "%s", expr);
419 g_free (expr);
420
421 if (!object)
422 return NULL;
423
424 if (object->type == XPATH_STRING &&
425 *object->stringval)
426 value = g_strdup ((const gchar *) object->stringval);
427 else
428 value = NULL;
429
430 xmlXPathFreeObject (object);
431
432 return value;
433 }
434
435 /**
436 * e_xml_xpath_eval_exists: (skip)
437 * @xpath_ctx: an #xmlXPathContextPtr
438 * @format: printf-like format specifier of path to evaluate
439 * @...: arguments for the @format
440 *
441 * Evaluates path specified by @format and returns whether it exists.
442 *
443 * Returns: %TRUE, when the given XPath exists, %FALSE otherwise.
444 *
445 * Since: 3.26
446 **/
447 gboolean
e_xml_xpath_eval_exists(xmlXPathContext * xpath_ctx,const gchar * format,...)448 e_xml_xpath_eval_exists (xmlXPathContext *xpath_ctx,
449 const gchar *format,
450 ...)
451 {
452 xmlXPathObjectPtr object;
453 va_list va;
454 gchar *expr;
455
456 g_return_val_if_fail (xpath_ctx != NULL, FALSE);
457 g_return_val_if_fail (format != NULL, FALSE);
458
459 va_start (va, format);
460 expr = g_strdup_vprintf (format, va);
461 va_end (va);
462
463 object = e_xml_xpath_eval (xpath_ctx, "%s", expr);
464 g_free (expr);
465
466 if (!object)
467 return FALSE;
468
469 xmlXPathFreeObject (object);
470
471 return TRUE;
472 }
473
474 /**
475 * e_xml_is_element_name: (skip)
476 * @node: (nullable): an #xmlNode
477 * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
478 * @name: an element name to search for
479 *
480 * Returns: Whether the @node is an element node of name @name and with a namespace href set to @ns_href
481 *
482 * Since: 3.38
483 **/
484 gboolean
e_xml_is_element_name(xmlNode * node,const gchar * ns_href,const gchar * name)485 e_xml_is_element_name (xmlNode *node,
486 const gchar *ns_href,
487 const gchar *name)
488 {
489 if (!node || node->type != XML_ELEMENT_NODE)
490 return FALSE;
491
492 if (g_strcmp0 ((const gchar *) node->name, name) == 0) {
493 if (!ns_href) {
494 if (!node->ns)
495 return TRUE;
496 } else if (node->ns) {
497 xmlNsPtr nsPtr;
498
499 if (g_strcmp0 ((const gchar *) node->ns->href, ns_href) == 0)
500 return TRUE;
501
502 nsPtr = xmlSearchNsByHref (node->doc, node, (const xmlChar *) ns_href);
503
504 if (nsPtr && node->ns == nsPtr)
505 return TRUE;
506 }
507 }
508
509 return FALSE;
510 }
511
512 /**
513 * e_xml_find_sibling: (skip)
514 * @sibling: (nullable): an #xmlNode, where to start searching
515 * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
516 * @name: an element name to search for
517 *
518 * Searches the sibling nodes of the @sibling for an element named @name in namespace @ns_href.
519 * It checks the @sibling itself too, but it doesn't check the previous siblings of the @sibling.
520 *
521 * Returns: (transfer none) (nullable): an #xmlNode of the given name, or %NULL, if not found
522 * It also returns %NULL, when the @sibling is %NULL.
523 *
524 * See: e_xml_find_next_sibling(), e_xml_find_child()
525 *
526 * Since: 3.38
527 **/
528 xmlNode *
e_xml_find_sibling(xmlNode * sibling,const gchar * ns_href,const gchar * name)529 e_xml_find_sibling (xmlNode *sibling,
530 const gchar *ns_href,
531 const gchar *name)
532 {
533 xmlNode *node;
534
535 for (node = sibling; node; node = xmlNextElementSibling (node)) {
536 if (e_xml_is_element_name (node, ns_href, name))
537 break;
538 }
539
540 return node;
541 }
542
543 /**
544 * e_xml_find_next_sibling: (skip)
545 * @sibling: (nullable): an #xmlNode, where to search from
546 * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
547 * @name: an element name to search for
548 *
549 * Searches for the next sibling node of the @sibling for an element named @name in namespace @ns_href.
550 * Unlike e_xml_find_sibling(), it skips the @sibling itself.
551 *
552 * Returns: (transfer none) (nullable): an #xmlNode of the given name, or %NULL, if not found
553 * It also returns %NULL, when the @sibling is %NULL.
554 *
555 * See: e_xml_find_sibling(), e_xml_find_child()
556 *
557 * Since: 3.38
558 **/
559 xmlNode *
e_xml_find_next_sibling(xmlNode * sibling,const gchar * ns_href,const gchar * name)560 e_xml_find_next_sibling (xmlNode *sibling,
561 const gchar *ns_href,
562 const gchar *name)
563 {
564 if (!sibling)
565 return NULL;
566
567 return e_xml_find_sibling (sibling->next, ns_href, name);
568 }
569
570 /**
571 * e_xml_find_child: (skip)
572 * @parent: (nullable): an #xmlNode, parent of which immediate children to search
573 * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
574 * @name: an element name to search for
575 *
576 * Searches the children nodes of the @parent for an element named @name in namespace @ns_href.
577 *
578 * Returns: (transfer none) (nullable): an #xmlNode of the given name, or %NULL, if not found.
579 * It also returns %NULL, when the @parent is %NULL.
580 *
581 * See: e_xml_find_sibling(), e_xml_find_children_nodes()
582 *
583 * Since: 3.38
584 **/
585 xmlNode *
e_xml_find_child(xmlNode * parent,const gchar * ns_href,const gchar * name)586 e_xml_find_child (xmlNode *parent,
587 const gchar *ns_href,
588 const gchar *name)
589 {
590 if (!parent)
591 return NULL;
592
593 return e_xml_find_sibling (parent->children, ns_href, name);
594 }
595
596 /**
597 * e_xml_dup_node_content: (skip)
598 * @node: (nullable): an #xmlNode
599 *
600 * Duplicates content of the @node. If the @node is %NULL, then the
601 * function does nothing and returns also %NULL.
602 *
603 * Unlike e_xml_get_node_text(), this includes also any element sub-structure
604 * of the @node, if any such exists.
605 *
606 * Returns: (transfer full) (nullable): the @node content as #xmlChar string,
607 * or %NULL, when the content could not be read or was not set. Free
608 * the non-%NULL value with xmlFree(), when no longer needed.
609 *
610 * See: e_xml_find_child_and_dup_content(), e_xml_get_node_text()
611 *
612 * Since: 3.38
613 **/
614 xmlChar *
e_xml_dup_node_content(const xmlNode * node)615 e_xml_dup_node_content (const xmlNode *node)
616 {
617 if (!node)
618 return NULL;
619
620 return xmlNodeGetContent (node);
621 }
622
623 /**
624 * e_xml_find_child_and_dup_content: (skip)
625 * @parent: (nullable): an #xmlNode, parent of which immediate children to search
626 * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
627 * @name: an element name to search for
628 *
629 * Searches the children nodes of the @parent for an element named @name in namespace @ns_href
630 * and returns its content. This combines e_xml_find_child() and e_xml_dup_node_content() calls.
631 *
632 * Returns: (transfer full) (nullable): the found node content as #xmlChar string,
633 * or %NULL, when the node could not be found or the content could not be read
634 * or was not set. Free the non-%NULL value with xmlFree(), when no longer needed.
635 *
636 * See: e_xml_find_child_and_get_text()
637 *
638 * Since: 3.38
639 **/
640 xmlChar *
e_xml_find_child_and_dup_content(xmlNode * parent,const gchar * ns_href,const gchar * name)641 e_xml_find_child_and_dup_content (xmlNode *parent,
642 const gchar *ns_href,
643 const gchar *name)
644 {
645 xmlNode *tmp;
646
647 tmp = e_xml_find_child (parent, ns_href, name);
648
649 if (!tmp)
650 return NULL;
651
652 return e_xml_dup_node_content (tmp);
653 }
654
655 /**
656 * e_xml_get_node_text: (skip)
657 * @node: (nullable): an #xmlNode
658 *
659 * Retrieves content of the @node. If the @node is %NULL, then the
660 * function does nothing and returns also %NULL.
661 *
662 * This is similar to e_xml_dup_node_content(), except it does not
663 * allocate new memory for the string. It also doesn't traverse
664 * the element structure, is returns the first text node's value
665 * only. It can be used to avoid unnecessary allocations, when
666 * reading element values with a single text node as a child.
667 *
668 * Returns: (transfer none) (nullable): The @node content, or %NULL.
669 *
670 * See: e_xml_dup_node_content()
671 *
672 * Since: 3.38
673 **/
674 const xmlChar *
e_xml_get_node_text(const xmlNode * node)675 e_xml_get_node_text (const xmlNode *node)
676 {
677 xmlNode *child;
678
679 if (!node)
680 return NULL;
681
682 if (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE)
683 return node->content;
684
685 for (child = node->children; child; child = child->next) {
686 if (child->type == XML_TEXT_NODE || child->type == XML_CDATA_SECTION_NODE)
687 return child->content;
688 }
689
690 return NULL;
691 }
692
693 /**
694 * e_xml_find_child_and_get_text: (skip)
695 * @parent: (nullable): an #xmlNode, parent of which immediate children to search
696 * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
697 * @name: an element name to search for
698 *
699 * Searches the children nodes of the @parent for an element named @name in namespace @ns_href
700 * and returns its text content.
701 *
702 * It combines e_xml_find_child() and e_xml_get_node_text() calls.
703 *
704 * Returns: (transfer none) (nullable): the found node text as #xmlChar string,
705 * or %NULL, when the node could not be found or the content could not be read
706 * or was not set.
707 *
708 * See: e_xml_find_child_and_dup_content(), e_xml_find_children_nodes()
709 *
710 * Since: 3.38
711 **/
712 const xmlChar *
e_xml_find_child_and_get_text(xmlNode * parent,const gchar * ns_href,const gchar * name)713 e_xml_find_child_and_get_text (xmlNode *parent,
714 const gchar *ns_href,
715 const gchar *name)
716 {
717 xmlNode *tmp;
718
719 tmp = e_xml_find_child (parent, ns_href, name);
720
721 if (!tmp)
722 return NULL;
723
724 return e_xml_get_node_text (tmp);
725 }
726
727 /**
728 * e_xml_find_children_nodes: (skip)
729 * @parent: an #xmlNode, whose children to search
730 * @count: how many nodes will be read
731 * @...: triple of arguments describing the nodes and their out variable
732 *
733 * Retrieve multiple nodes in one go, in an efficient way. It can be
734 * quicker than traversing the children of the @parent @count times
735 * in certain circumstances.
736 *
737 * The variable parameters expect triple of:
738 * const gchar *ns_href;
739 * const gchar *name;
740 * xmlNode **out_node;
741 * where the ns_href is a namespace href the node should have set,
742 * or %NULL for none namespace; the name is an element name to search for.
743 * The names should not be included more than once.
744 *
745 * Since: 3.38
746 **/
747 void
e_xml_find_children_nodes(xmlNode * parent,guint count,...)748 e_xml_find_children_nodes (xmlNode *parent,
749 guint count,
750 ...)
751 {
752 struct _data {
753 const gchar *ns_href;
754 const gchar *name;
755 xmlNode **out_node;
756 } *data;
757 va_list args;
758 xmlNode *node;
759 guint ii;
760
761 g_return_if_fail (count > 0);
762
763 data = g_alloca (sizeof (struct _data) * count);
764
765 va_start (args, count);
766
767 for (ii = 0; ii < count; ii++) {
768 data[ii].ns_href = va_arg (args, const gchar *);
769 data[ii].name = va_arg (args, const gchar *);
770 data[ii].out_node = va_arg (args, xmlNode **);
771
772 *(data[ii].out_node) = NULL;
773 }
774
775 va_end (args);
776
777 for (node = parent->children; node; node = count ? xmlNextElementSibling (node) : NULL) {
778 for (ii = 0; ii < count; ii++) {
779 if (e_xml_is_element_name (node, data[ii].ns_href, data[ii].name)) {
780 *(data[ii].out_node) = node;
781 count--;
782
783 if (ii < count)
784 data[ii] = data[count];
785
786 break;
787 }
788 }
789 }
790 }
791
792 /**
793 * e_xml_find_in_hierarchy: (skip)
794 * @parent: (nullable): an #xmlNode, or %NULL, in which case function does nothing and just returns %NULL
795 * @child_ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
796 * @child_name: an element name to search for
797 * @...: a two-%NULL-terminated pair of hierarchy children
798 *
799 * Checks whether the @parent has a hierarchy of children described by pair
800 * of 'ns_href' and 'name'.
801 *
802 * Note: It requires two %NULL-s at the end of the arguments, because the `ns_href' can
803 * be %NULL, thus it could not distinguish between no namespace href and the end of
804 * the hierarchy children, thus it stops only on the 'name' being %NULL.
805 *
806 * Returns: (transfer none) (nullable): an #xmlNode referencing the node in the hierarchy
807 * of the children of the @parent, or %NULL, when no such found.
808 *
809 * Since: 3.38
810 **/
811 xmlNode *
e_xml_find_in_hierarchy(xmlNode * parent,const gchar * child_ns_href,const gchar * child_name,...)812 e_xml_find_in_hierarchy (xmlNode *parent,
813 const gchar *child_ns_href,
814 const gchar *child_name,
815 ...)
816 {
817 xmlNode *node;
818 va_list va;
819
820 if (!parent)
821 return NULL;
822
823 node = e_xml_find_child (parent, child_ns_href, child_name);
824
825 if (!node)
826 return NULL;
827
828 va_start (va, child_name);
829
830 while (node) {
831 child_ns_href = va_arg (va, const gchar *);
832 child_name = va_arg (va, const gchar *);
833
834 if (!child_name)
835 break;
836
837 node = e_xml_find_child (node, child_ns_href, child_name);
838 }
839
840 va_end (va);
841
842 return child_name ? NULL : node;
843 }
844