VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/xml.cpp@ 82968

Last change on this file since 82968 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.2 KB
Line 
1/* $Id: xml.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * IPRT - XML Manipulation API.
4 */
5
6/*
7 * Copyright (C) 2007-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/dir.h>
32#include <iprt/file.h>
33#include <iprt/err.h>
34#include <iprt/log.h>
35#include <iprt/param.h>
36#include <iprt/path.h>
37#include <iprt/cpp/lock.h>
38#include <iprt/cpp/xml.h>
39
40#include <libxml/tree.h>
41#include <libxml/parser.h>
42#include <libxml/globals.h>
43#include <libxml/xmlIO.h>
44#include <libxml/xmlsave.h>
45#include <libxml/uri.h>
46
47#include <libxml/xmlschemas.h>
48
49#include <map>
50
51
52/*********************************************************************************************************************************
53* Global Variables *
54*********************************************************************************************************************************/
55/**
56 * Global module initialization structure. This is to wrap non-reentrant bits
57 * of libxml, among other things.
58 *
59 * The constructor and destructor of this structure are used to perform global
60 * module initialization and cleanup. There must be only one global variable of
61 * this structure.
62 */
63static class Global
64{
65public:
66
67 Global()
68 {
69 /* Check the parser version. The docs say it will kill the app if
70 * there is a serious version mismatch, but I couldn't find it in the
71 * source code (it only prints the error/warning message to the console) so
72 * let's leave it as is for informational purposes. */
73 LIBXML_TEST_VERSION
74
75 /* Init libxml */
76 xmlInitParser();
77
78 /* Save the default entity resolver before someone has replaced it */
79 sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
80 }
81
82 ~Global()
83 {
84 /* Shutdown libxml */
85 xmlCleanupParser();
86 }
87
88 struct
89 {
90 xmlExternalEntityLoader defaultEntityLoader;
91
92 /** Used to provide some thread safety missing in libxml2 (see e.g.
93 * XmlTreeBackend::read()) */
94 RTCLockMtx lock;
95 }
96 sxml; /* XXX naming this xml will break with gcc-3.3 */
97} gGlobal;
98
99
100
101namespace xml
102{
103
104////////////////////////////////////////////////////////////////////////////////
105//
106// Exceptions
107//
108////////////////////////////////////////////////////////////////////////////////
109
110LogicError::LogicError(RT_SRC_POS_DECL)
111 : RTCError(NULL)
112{
113 char *msg = NULL;
114 RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
115 pszFunction, pszFile, iLine);
116 setWhat(msg);
117 RTStrFree(msg);
118}
119
120XmlError::XmlError(xmlErrorPtr aErr)
121{
122 if (!aErr)
123 throw EInvalidArg(RT_SRC_POS);
124
125 char *msg = Format(aErr);
126 setWhat(msg);
127 RTStrFree(msg);
128}
129
130/**
131 * Composes a single message for the given error. The caller must free the
132 * returned string using RTStrFree() when no more necessary.
133 */
134/* static */ char *XmlError::Format(xmlErrorPtr aErr)
135{
136 const char *msg = aErr->message ? aErr->message : "<none>";
137 size_t msgLen = strlen(msg);
138 /* strip spaces, trailing EOLs and dot-like char */
139 while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
140 --msgLen;
141
142 char *finalMsg = NULL;
143 RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
144 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
145
146 return finalMsg;
147}
148
149EIPRTFailure::EIPRTFailure(int aRC, const char *pcszContext, ...)
150 : RuntimeError(NULL),
151 mRC(aRC)
152{
153 char *pszContext2;
154 va_list args;
155 va_start(args, pcszContext);
156 RTStrAPrintfV(&pszContext2, pcszContext, args);
157 va_end(args);
158 char *newMsg;
159 RTStrAPrintf(&newMsg, "%s: %d (%s)", pszContext2, aRC, RTErrGetShort(aRC));
160 setWhat(newMsg);
161 RTStrFree(newMsg);
162 RTStrFree(pszContext2);
163}
164
165////////////////////////////////////////////////////////////////////////////////
166//
167// File Class
168//
169//////////////////////////////////////////////////////////////////////////////
170
171struct File::Data
172{
173 Data()
174 : handle(NIL_RTFILE), opened(false)
175 { }
176
177 RTCString strFileName;
178 RTFILE handle;
179 bool opened : 1;
180 bool flushOnClose : 1;
181};
182
183File::File(Mode aMode, const char *aFileName, bool aFlushIt /* = false */)
184 : m(new Data())
185{
186 m->strFileName = aFileName;
187 m->flushOnClose = aFlushIt;
188
189 uint32_t flags = 0;
190 const char *pcszMode = "???";
191 switch (aMode)
192 {
193 /** @todo change to RTFILE_O_DENY_WRITE where appropriate. */
194 case Mode_Read:
195 flags = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
196 pcszMode = "reading";
197 break;
198 case Mode_WriteCreate: // fail if file exists
199 flags = RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE;
200 pcszMode = "writing";
201 break;
202 case Mode_Overwrite: // overwrite if file exists
203 flags = RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE;
204 pcszMode = "overwriting";
205 break;
206 case Mode_ReadWrite:
207 flags = RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
208 pcszMode = "reading/writing";
209 break;
210 }
211
212 int vrc = RTFileOpen(&m->handle, aFileName, flags);
213 if (RT_FAILURE(vrc))
214 throw EIPRTFailure(vrc, "Runtime error opening '%s' for %s", aFileName, pcszMode);
215
216 m->opened = true;
217 m->flushOnClose = aFlushIt && (flags & RTFILE_O_ACCESS_MASK) != RTFILE_O_READ;
218}
219
220File::File(RTFILE aHandle, const char *aFileName /* = NULL */, bool aFlushIt /* = false */)
221 : m(new Data())
222{
223 if (aHandle == NIL_RTFILE)
224 throw EInvalidArg(RT_SRC_POS);
225
226 m->handle = aHandle;
227
228 if (aFileName)
229 m->strFileName = aFileName;
230
231 m->flushOnClose = aFlushIt;
232
233 setPos(0);
234}
235
236File::~File()
237{
238 if (m->flushOnClose)
239 {
240 RTFileFlush(m->handle);
241 if (!m->strFileName.isEmpty())
242 RTDirFlushParent(m->strFileName.c_str());
243 }
244
245 if (m->opened)
246 RTFileClose(m->handle);
247 delete m;
248}
249
250const char *File::uri() const
251{
252 return m->strFileName.c_str();
253}
254
255uint64_t File::pos() const
256{
257 uint64_t p = 0;
258 int vrc = RTFileSeek(m->handle, 0, RTFILE_SEEK_CURRENT, &p);
259 if (RT_SUCCESS(vrc))
260 return p;
261
262 throw EIPRTFailure(vrc, "Runtime error seeking in file '%s'", m->strFileName.c_str());
263}
264
265void File::setPos(uint64_t aPos)
266{
267 uint64_t p = 0;
268 unsigned method = RTFILE_SEEK_BEGIN;
269 int vrc = VINF_SUCCESS;
270
271 /* check if we overflow int64_t and move to INT64_MAX first */
272 if ((int64_t)aPos < 0)
273 {
274 vrc = RTFileSeek(m->handle, INT64_MAX, method, &p);
275 aPos -= (uint64_t)INT64_MAX;
276 method = RTFILE_SEEK_CURRENT;
277 }
278 /* seek the rest */
279 if (RT_SUCCESS(vrc))
280 vrc = RTFileSeek(m->handle, (int64_t) aPos, method, &p);
281 if (RT_SUCCESS(vrc))
282 return;
283
284 throw EIPRTFailure(vrc, "Runtime error seeking in file '%s'", m->strFileName.c_str());
285}
286
287int File::read(char *aBuf, int aLen)
288{
289 size_t len = aLen;
290 int vrc = RTFileRead(m->handle, aBuf, len, &len);
291 if (RT_SUCCESS(vrc))
292 return (int)len;
293
294 throw EIPRTFailure(vrc, "Runtime error reading from file '%s'", m->strFileName.c_str());
295}
296
297int File::write(const char *aBuf, int aLen)
298{
299 size_t len = aLen;
300 int vrc = RTFileWrite(m->handle, aBuf, len, &len);
301 if (RT_SUCCESS(vrc))
302 return (int)len;
303
304 throw EIPRTFailure(vrc, "Runtime error writing to file '%s'", m->strFileName.c_str());
305}
306
307void File::truncate()
308{
309 int vrc = RTFileSetSize(m->handle, pos());
310 if (RT_SUCCESS(vrc))
311 return;
312
313 throw EIPRTFailure(vrc, "Runtime error truncating file '%s'", m->strFileName.c_str());
314}
315
316////////////////////////////////////////////////////////////////////////////////
317//
318// MemoryBuf Class
319//
320//////////////////////////////////////////////////////////////////////////////
321
322struct MemoryBuf::Data
323{
324 Data()
325 : buf(NULL), len(0), uri(NULL), pos(0) {}
326
327 const char *buf;
328 size_t len;
329 char *uri;
330
331 size_t pos;
332};
333
334MemoryBuf::MemoryBuf(const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
335 : m(new Data())
336{
337 if (aBuf == NULL)
338 throw EInvalidArg(RT_SRC_POS);
339
340 m->buf = aBuf;
341 m->len = aLen;
342 m->uri = RTStrDup(aURI);
343}
344
345MemoryBuf::~MemoryBuf()
346{
347 RTStrFree(m->uri);
348}
349
350const char *MemoryBuf::uri() const
351{
352 return m->uri;
353}
354
355uint64_t MemoryBuf::pos() const
356{
357 return m->pos;
358}
359
360void MemoryBuf::setPos(uint64_t aPos)
361{
362 size_t off = (size_t)aPos;
363 if ((uint64_t) off != aPos)
364 throw EInvalidArg();
365
366 if (off > m->len)
367 throw EInvalidArg();
368
369 m->pos = off;
370}
371
372int MemoryBuf::read(char *aBuf, int aLen)
373{
374 if (m->pos >= m->len)
375 return 0 /* nothing to read */;
376
377 size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
378 memcpy(aBuf, m->buf + m->pos, len);
379 m->pos += len;
380
381 return (int)len;
382}
383
384////////////////////////////////////////////////////////////////////////////////
385//
386// GlobalLock class
387//
388////////////////////////////////////////////////////////////////////////////////
389
390struct GlobalLock::Data
391{
392 PFNEXTERNALENTITYLOADER pOldLoader;
393 RTCLock lock;
394
395 Data()
396 : pOldLoader(NULL),
397 lock(gGlobal.sxml.lock)
398 {
399 }
400};
401
402GlobalLock::GlobalLock()
403 : m(new Data())
404{
405}
406
407GlobalLock::~GlobalLock()
408{
409 if (m->pOldLoader)
410 xmlSetExternalEntityLoader(m->pOldLoader);
411 delete m;
412 m = NULL;
413}
414
415void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
416{
417 m->pOldLoader = xmlGetExternalEntityLoader();
418 xmlSetExternalEntityLoader(pLoader);
419}
420
421// static
422xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
423 const char *aID,
424 xmlParserCtxt *aCtxt)
425{
426 return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
427}
428
429
430
431////////////////////////////////////////////////////////////////////////////////
432//
433// Node class
434//
435////////////////////////////////////////////////////////////////////////////////
436
437Node::Node(EnumType type,
438 Node *pParent,
439 PRTLISTANCHOR pListAnchor,
440 xmlNode *pLibNode,
441 xmlAttr *pLibAttr)
442 : m_Type(type)
443 , m_pParent(pParent)
444 , m_pLibNode(pLibNode)
445 , m_pLibAttr(pLibAttr)
446 , m_pcszNamespacePrefix(NULL)
447 , m_pcszNamespaceHref(NULL)
448 , m_pcszName(NULL)
449 , m_pParentListAnchor(pListAnchor)
450{
451 RTListInit(&m_listEntry);
452}
453
454Node::~Node()
455{
456}
457
458/**
459 * Returns the name of the node, which is either the element name or
460 * the attribute name. For other node types it probably returns NULL.
461 * @return
462 */
463const char *Node::getName() const
464{
465 return m_pcszName;
466}
467
468/**
469 * Returns the name of the node, which is either the element name or
470 * the attribute name. For other node types it probably returns NULL.
471 * @return
472 */
473const char *Node::getPrefix() const
474{
475 return m_pcszNamespacePrefix;
476}
477
478/**
479 * Returns the XML namespace URI, which is the attribute name. For other node types it probably
480 * returns NULL.
481 * @return
482 */
483const char *Node::getNamespaceURI() const
484{
485 return m_pcszNamespaceHref;
486}
487
488/**
489 * Variant of nameEquals that checks the namespace as well.
490 * @param pcszNamespace
491 * @param pcsz
492 * @return
493 */
494bool Node::nameEqualsNS(const char *pcszNamespace, const char *pcsz) const
495{
496 if (m_pcszName == pcsz)
497 return true;
498 if (m_pcszName == NULL)
499 return false;
500 if (pcsz == NULL)
501 return false;
502 if (strcmp(m_pcszName, pcsz))
503 return false;
504
505 // name matches: then check namespaces as well
506 if (!pcszNamespace)
507 return true;
508 // caller wants namespace:
509 if (!m_pcszNamespacePrefix)
510 // but node has no namespace:
511 return false;
512 return !strcmp(m_pcszNamespacePrefix, pcszNamespace);
513}
514
515/**
516 * Variant of nameEquals that checks the namespace as well.
517 *
518 * @returns true if equal, false if not.
519 * @param pcsz The element name.
520 * @param cchMax The maximum number of character from @a pcsz to
521 * match.
522 * @param pcszNamespace The name space prefix or NULL (default).
523 */
524bool Node::nameEqualsN(const char *pcsz, size_t cchMax, const char *pcszNamespace /* = NULL*/) const
525{
526 /* Match the name. */
527 if (!m_pcszName)
528 return false;
529 if (!pcsz || cchMax == 0)
530 return false;
531 if (strncmp(m_pcszName, pcsz, cchMax))
532 return false;
533 if (strlen(m_pcszName) > cchMax)
534 return false;
535
536 /* Match name space. */
537 if (!pcszNamespace)
538 return true; /* NULL, anything goes. */
539 if (!m_pcszNamespacePrefix)
540 return false; /* Element has no namespace. */
541 return !strcmp(m_pcszNamespacePrefix, pcszNamespace);
542}
543
544/**
545 * Returns the value of a node. If this node is an attribute, returns
546 * the attribute value; if this node is an element, then this returns
547 * the element text content.
548 * @return
549 */
550const char *Node::getValue() const
551{
552 if ( m_pLibAttr
553 && m_pLibAttr->children
554 )
555 // libxml hides attribute values in another node created as a
556 // single child of the attribute node, and it's in the content field
557 return (const char *)m_pLibAttr->children->content;
558
559 if ( m_pLibNode
560 && m_pLibNode->children)
561 return (const char *)m_pLibNode->children->content;
562
563 return NULL;
564}
565
566/**
567 * Returns the value of a node. If this node is an attribute, returns
568 * the attribute value; if this node is an element, then this returns
569 * the element text content.
570 * @return
571 * @param cchValueLimit If the length of the returned value exceeds this
572 * limit a EIPRTFailure exception will be thrown.
573 */
574const char *Node::getValueN(size_t cchValueLimit) const
575{
576 if ( m_pLibAttr
577 && m_pLibAttr->children
578 )
579 {
580 // libxml hides attribute values in another node created as a
581 // single child of the attribute node, and it's in the content field
582 AssertStmt(strlen((const char *)m_pLibAttr->children->content) <= cchValueLimit, throw EIPRTFailure(VERR_BUFFER_OVERFLOW, "Attribute '%s' exceeds limit of %zu bytes", m_pcszName, cchValueLimit));
583 return (const char *)m_pLibAttr->children->content;
584 }
585
586 if ( m_pLibNode
587 && m_pLibNode->children)
588 {
589 AssertStmt(strlen((const char *)m_pLibNode->children->content) <= cchValueLimit, throw EIPRTFailure(VERR_BUFFER_OVERFLOW, "Element '%s' exceeds limit of %zu bytes", m_pcszName, cchValueLimit));
590 return (const char *)m_pLibNode->children->content;
591 }
592
593 return NULL;
594}
595
596/**
597 * Copies the value of a node into the given integer variable.
598 * Returns TRUE only if a value was found and was actually an
599 * integer of the given type.
600 * @return
601 */
602bool Node::copyValue(int32_t &i) const
603{
604 const char *pcsz;
605 if ( ((pcsz = getValue()))
606 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
607 )
608 return true;
609
610 return false;
611}
612
613/**
614 * Copies the value of a node into the given integer variable.
615 * Returns TRUE only if a value was found and was actually an
616 * integer of the given type.
617 * @return
618 */
619bool Node::copyValue(uint32_t &i) const
620{
621 const char *pcsz;
622 if ( ((pcsz = getValue()))
623 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
624 )
625 return true;
626
627 return false;
628}
629
630/**
631 * Copies the value of a node into the given integer variable.
632 * Returns TRUE only if a value was found and was actually an
633 * integer of the given type.
634 * @return
635 */
636bool Node::copyValue(int64_t &i) const
637{
638 const char *pcsz;
639 if ( ((pcsz = getValue()))
640 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
641 )
642 return true;
643
644 return false;
645}
646
647/**
648 * Copies the value of a node into the given integer variable.
649 * Returns TRUE only if a value was found and was actually an
650 * integer of the given type.
651 * @return
652 */
653bool Node::copyValue(uint64_t &i) const
654{
655 const char *pcsz;
656 if ( ((pcsz = getValue()))
657 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
658 )
659 return true;
660
661 return false;
662}
663
664/**
665 * Returns the line number of the current node in the source XML file.
666 * Useful for error messages.
667 * @return
668 */
669int Node::getLineNumber() const
670{
671 if (m_pLibAttr)
672 return m_pParent->m_pLibNode->line;
673
674 return m_pLibNode->line;
675}
676
677/**
678 * Private element constructor.
679 *
680 * @param pElmRoot Pointer to the root element.
681 * @param pParent Pointer to the parent element (always an ElementNode,
682 * despite the type). NULL for the root node.
683 * @param pListAnchor Pointer to the m_children member of the parent. NULL
684 * for the root node.
685 * @param pLibNode Pointer to the libxml2 node structure.
686 */
687ElementNode::ElementNode(const ElementNode *pElmRoot,
688 Node *pParent,
689 PRTLISTANCHOR pListAnchor,
690 xmlNode *pLibNode)
691 : Node(IsElement,
692 pParent,
693 pListAnchor,
694 pLibNode,
695 NULL)
696{
697 m_pElmRoot = pElmRoot ? pElmRoot : this; // If NULL is passed, then this is the root element.
698 m_pcszName = (const char *)pLibNode->name;
699
700 if (pLibNode->ns)
701 {
702 m_pcszNamespacePrefix = (const char *)m_pLibNode->ns->prefix;
703 m_pcszNamespaceHref = (const char *)m_pLibNode->ns->href;
704 }
705
706 RTListInit(&m_children);
707 RTListInit(&m_attributes);
708}
709
710ElementNode::~ElementNode()
711{
712 Node *pCur, *pNext;
713 RTListForEachSafeCpp(&m_children, pCur, pNext, Node, m_listEntry)
714 {
715 delete pCur;
716 }
717 RTListInit(&m_children);
718
719 RTListForEachSafeCpp(&m_attributes, pCur, pNext, Node, m_listEntry)
720 {
721 delete pCur;
722 }
723 RTListInit(&m_attributes);
724}
725
726
727/**
728 * Gets the next tree element in a full tree enumeration.
729 *
730 * @returns Pointer to the next element in the tree, NULL if we're done.
731 * @param pElmRoot The root of the tree we're enumerating. NULL if
732 * it's the entire tree.
733 */
734ElementNode const *ElementNode::getNextTreeElement(ElementNode const *pElmRoot /*= NULL */) const
735{
736 /*
737 * Consider children first.
738 */
739 ElementNode const *pChild = getFirstChildElement();
740 if (pChild)
741 return pChild;
742
743 /*
744 * Then siblings, aunts and uncles.
745 */
746 ElementNode const *pCur = this;
747 do
748 {
749 ElementNode const *pSibling = pCur->getNextSibilingElement();
750 if (pSibling != NULL)
751 return pSibling;
752
753 pCur = static_cast<const xml::ElementNode *>(pCur->m_pParent);
754 Assert(pCur || pCur == pElmRoot);
755 } while (pCur != pElmRoot);
756
757 return NULL;
758}
759
760
761/**
762 * Private implementation.
763 *
764 * @param pElmRoot The root element.
765 */
766/*static*/ void ElementNode::buildChildren(ElementNode *pElmRoot) // protected
767{
768 for (ElementNode *pCur = pElmRoot; pCur; pCur = pCur->getNextTreeElement(pElmRoot))
769 {
770 /*
771 * Go thru this element's attributes creating AttributeNodes for them.
772 */
773 for (xmlAttr *pLibAttr = pCur->m_pLibNode->properties; pLibAttr; pLibAttr = pLibAttr->next)
774 {
775 AttributeNode *pNew = new AttributeNode(pElmRoot, pCur, &pCur->m_attributes, pLibAttr);
776 RTListAppend(&pCur->m_attributes, &pNew->m_listEntry);
777 }
778
779 /*
780 * Go thru this element's child elements (element and text nodes).
781 */
782 for (xmlNodePtr pLibNode = pCur->m_pLibNode->children; pLibNode; pLibNode = pLibNode->next)
783 {
784 Node *pNew;
785 if (pLibNode->type == XML_ELEMENT_NODE)
786 pNew = new ElementNode(pElmRoot, pCur, &pCur->m_children, pLibNode);
787 else if (pLibNode->type == XML_TEXT_NODE)
788 pNew = new ContentNode(pCur, &pCur->m_children, pLibNode);
789 else
790 continue;
791 RTListAppend(&pCur->m_children, &pNew->m_listEntry);
792 }
793 }
794}
795
796
797/**
798 * Builds a list of direct child elements of the current element that
799 * match the given string; if pcszMatch is NULL, all direct child
800 * elements are returned.
801 * @param children out: list of nodes to which children will be appended.
802 * @param pcszMatch in: match string, or NULL to return all children.
803 * @return Number of items appended to the list (0 if none).
804 */
805int ElementNode::getChildElements(ElementNodesList &children,
806 const char *pcszMatch /*= NULL*/)
807 const
808{
809 int i = 0;
810 Node *p;
811 RTListForEachCpp(&m_children, p, Node, m_listEntry)
812 {
813 // export this child node if ...
814 if (p->isElement())
815 if ( !pcszMatch // ... the caller wants all nodes or ...
816 || !strcmp(pcszMatch, p->getName()) // ... the element name matches.
817 )
818 {
819 children.push_back(static_cast<ElementNode *>(p));
820 ++i;
821 }
822 }
823 return i;
824}
825
826/**
827 * Returns the first child element whose name matches pcszMatch.
828 *
829 * @param pcszNamespace Namespace prefix (e.g. "vbox") or NULL to match any namespace.
830 * @param pcszMatch Element name to match.
831 * @return
832 */
833const ElementNode *ElementNode::findChildElementNS(const char *pcszNamespace, const char *pcszMatch) const
834{
835 Node *p;
836 RTListForEachCpp(&m_children, p, Node, m_listEntry)
837 {
838 if (p->isElement())
839 {
840 ElementNode *pelm = static_cast<ElementNode*>(p);
841 if (pelm->nameEqualsNS(pcszNamespace, pcszMatch))
842 return pelm;
843 }
844 }
845 return NULL;
846}
847
848/**
849 * Returns the first child element whose "id" attribute matches pcszId.
850 * @param pcszId identifier to look for.
851 * @return child element or NULL if not found.
852 */
853const ElementNode *ElementNode::findChildElementFromId(const char *pcszId) const
854{
855 const Node *p;
856 RTListForEachCpp(&m_children, p, Node, m_listEntry)
857 {
858 if (p->isElement())
859 {
860 const ElementNode *pElm = static_cast<const ElementNode *>(p);
861 const AttributeNode *pAttr = pElm->findAttribute("id");
862 if (pAttr && !strcmp(pAttr->getValue(), pcszId))
863 return pElm;
864 }
865 }
866 return NULL;
867}
868
869
870const ElementNode *ElementNode::findChildElementP(const char *pcszPath, const char *pcszNamespace /*= NULL*/) const
871{
872 size_t cchThis = strchr(pcszPath, '/') - pcszPath;
873 if (cchThis == (size_t)((const char *)0 - pcszPath))
874 return findChildElementNS(pcszNamespace, pcszPath);
875
876 /** @todo Can be done without recursion as we have both sibling lists and parent
877 * pointers in this variant. */
878 const Node *p;
879 RTListForEachCpp(&m_children, p, Node, m_listEntry)
880 {
881 if (p->isElement())
882 {
883 const ElementNode *pElm = static_cast<const ElementNode *>(p);
884 if (pElm->nameEqualsN(pcszPath, cchThis, pcszNamespace))
885 {
886 pElm = findChildElementP(pcszPath + cchThis, pcszNamespace);
887 if (pElm)
888 return pElm;
889 }
890 }
891 }
892
893 return NULL;
894}
895
896const ElementNode *ElementNode::getFirstChildElement() const
897{
898 const Node *p;
899 RTListForEachCpp(&m_children, p, Node, m_listEntry)
900 {
901 if (p->isElement())
902 return static_cast<const ElementNode *>(p);
903 }
904 return NULL;
905}
906
907const ElementNode *ElementNode::getLastChildElement() const
908{
909 const Node *p;
910 RTListForEachReverseCpp(&m_children, p, Node, m_listEntry)
911 {
912 if (p->isElement())
913 return static_cast<const ElementNode *>(p);
914 }
915 return NULL;
916}
917
918const ElementNode *ElementNode::getPrevSibilingElement() const
919{
920 if (!m_pParent)
921 return NULL;
922 const Node *pSibling = this;
923 for (;;)
924 {
925 pSibling = RTListGetPrevCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
926 if (!pSibling)
927 return NULL;
928 if (pSibling->isElement())
929 return static_cast<const ElementNode *>(pSibling);
930 }
931}
932
933const ElementNode *ElementNode::getNextSibilingElement() const
934{
935 if (!m_pParent)
936 return NULL;
937 const Node *pSibling = this;
938 for (;;)
939 {
940 pSibling = RTListGetNextCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
941 if (!pSibling)
942 return NULL;
943 if (pSibling->isElement())
944 return static_cast<const ElementNode *>(pSibling);
945 }
946}
947
948const ElementNode *ElementNode::findPrevSibilingElement(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
949{
950 if (!m_pParent)
951 return NULL;
952 const Node *pSibling = this;
953 for (;;)
954 {
955 pSibling = RTListGetPrevCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
956 if (!pSibling)
957 return NULL;
958 if (pSibling->isElement())
959 {
960 const ElementNode *pElem = static_cast<const ElementNode *>(pSibling);
961 if (pElem->nameEqualsNS(pcszNamespace, pcszMatch))
962 return pElem;
963 }
964 }
965}
966
967const ElementNode *ElementNode::findNextSibilingElement(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
968{
969 if (!m_pParent)
970 return NULL;
971 const Node *pSibling = this;
972 for (;;)
973 {
974 pSibling = RTListGetNextCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
975 if (!pSibling)
976 return NULL;
977 if (pSibling->isElement())
978 {
979 const ElementNode *pElem = static_cast<const ElementNode *>(pSibling);
980 if (pElem->nameEqualsNS(pcszNamespace, pcszMatch))
981 return pElem;
982 }
983 }
984}
985
986
987/**
988 * Looks up the given attribute node in this element's attribute map.
989 *
990 * @param pcszMatch The name of the attribute to find.
991 * @param pcszNamespace The attribute name space prefix or NULL.
992 */
993const AttributeNode *ElementNode::findAttribute(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
994{
995 AttributeNode *p;
996 RTListForEachCpp(&m_attributes, p, AttributeNode, m_listEntry)
997 {
998 if (p->nameEqualsNS(pcszNamespace, pcszMatch))
999 return p;
1000 }
1001 return NULL;
1002}
1003
1004/**
1005 * Convenience method which attempts to find the attribute with the given
1006 * name and returns its value as a string.
1007 *
1008 * @param pcszMatch Name of attribute to find.
1009 * @param ppcsz Where to return the attribute.
1010 * @param pcszNamespace The attribute name space prefix or NULL.
1011 * @returns Boolean success indicator.
1012 */
1013bool ElementNode::getAttributeValue(const char *pcszMatch, const char **ppcsz, const char *pcszNamespace /*= NULL*/) const
1014{
1015 const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
1016 if (pAttr)
1017 {
1018 *ppcsz = pAttr->getValue();
1019 return true;
1020 }
1021 return false;
1022}
1023
1024/**
1025 * Convenience method which attempts to find the attribute with the given
1026 * name and returns its value as a string.
1027 *
1028 * @param pcszMatch Name of attribute to find.
1029 * @param pStr Pointer to the string object that should receive the
1030 * attribute value.
1031 * @param pcszNamespace The attribute name space prefix or NULL.
1032 * @returns Boolean success indicator.
1033 *
1034 * @throws Whatever the string class may throw on assignment.
1035 */
1036bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace /*= NULL*/) const
1037{
1038 const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
1039 if (pAttr)
1040 {
1041 *pStr = pAttr->getValue();
1042 return true;
1043 }
1044
1045 return false;
1046}
1047
1048/**
1049 * Like getAttributeValue (ministring variant), but makes sure that all backslashes
1050 * are converted to forward slashes.
1051 *
1052 * @param pcszMatch Name of attribute to find.
1053 * @param pStr Pointer to the string object that should
1054 * receive the attribute path value.
1055 * @param pcszNamespace The attribute name space prefix or NULL.
1056 * @returns Boolean success indicator.
1057 */
1058bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace /*= NULL*/) const
1059{
1060 if (getAttributeValue(pcszMatch, pStr, pcszNamespace))
1061 {
1062 pStr->findReplace('\\', '/');
1063 return true;
1064 }
1065
1066 return false;
1067}
1068
1069/**
1070 * Convenience method which attempts to find the attribute with the given
1071 * name and returns its value as a signed 32-bit integer.
1072 *
1073 * @param pcszMatch Name of attribute to find.
1074 * @param piValue Where to return the value.
1075 * @param pcszNamespace The attribute name space prefix or NULL.
1076 * @returns Boolean success indicator.
1077 */
1078bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t *piValue, const char *pcszNamespace /*= NULL*/) const
1079{
1080 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1081 if (pcsz)
1082 {
1083 int rc = RTStrToInt32Ex(pcsz, NULL, 0, piValue);
1084 if (rc == VINF_SUCCESS)
1085 return true;
1086 }
1087 return false;
1088}
1089
1090/**
1091 * Convenience method which attempts to find the attribute with the given
1092 * name and returns its value as an unsigned 32-bit integer.
1093 *
1094 * @param pcszMatch Name of attribute to find.
1095 * @param puValue Where to return the value.
1096 * @param pcszNamespace The attribute name space prefix or NULL.
1097 * @returns Boolean success indicator.
1098 */
1099bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t *puValue, const char *pcszNamespace /*= NULL*/) const
1100{
1101 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1102 if (pcsz)
1103 {
1104 int rc = RTStrToUInt32Ex(pcsz, NULL, 0, puValue);
1105 if (rc == VINF_SUCCESS)
1106 return true;
1107 }
1108 return false;
1109}
1110
1111/**
1112 * Convenience method which attempts to find the attribute with the given
1113 * name and returns its value as a signed 64-bit integer.
1114 *
1115 * @param pcszMatch Name of attribute to find.
1116 * @param piValue Where to return the value.
1117 * @param pcszNamespace The attribute name space prefix or NULL.
1118 * @returns Boolean success indicator.
1119 */
1120bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t *piValue, const char *pcszNamespace /*= NULL*/) const
1121{
1122 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1123 if (pcsz)
1124 {
1125 int rc = RTStrToInt64Ex(pcsz, NULL, 0, piValue);
1126 if (rc == VINF_SUCCESS)
1127 return true;
1128 }
1129 return false;
1130}
1131
1132/**
1133 * Convenience method which attempts to find the attribute with the given
1134 * name and returns its value as an unsigned 64-bit integer.
1135 *
1136 * @param pcszMatch Name of attribute to find.
1137 * @param puValue Where to return the value.
1138 * @param pcszNamespace The attribute name space prefix or NULL.
1139 * @returns Boolean success indicator.
1140 */
1141bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t *puValue, const char *pcszNamespace /*= NULL*/) const
1142{
1143 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1144 if (pcsz)
1145 {
1146 int rc = RTStrToUInt64Ex(pcsz, NULL, 0, puValue);
1147 if (rc == VINF_SUCCESS)
1148 return true;
1149 }
1150 return false;
1151}
1152
1153/**
1154 * Convenience method which attempts to find the attribute with the given
1155 * name and returns its value as a boolean. This accepts "true", "false",
1156 * "yes", "no", "1" or "0" as valid values.
1157 *
1158 * @param pcszMatch Name of attribute to find.
1159 * @param pfValue Where to return the value.
1160 * @param pcszNamespace The attribute name space prefix or NULL.
1161 * @returns Boolean success indicator.
1162 */
1163bool ElementNode::getAttributeValue(const char *pcszMatch, bool *pfValue, const char *pcszNamespace /*= NULL*/) const
1164{
1165 const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
1166 if (pcsz)
1167 {
1168 if ( !strcmp(pcsz, "true")
1169 || !strcmp(pcsz, "yes")
1170 || !strcmp(pcsz, "1")
1171 )
1172 {
1173 *pfValue = true;
1174 return true;
1175 }
1176 if ( !strcmp(pcsz, "false")
1177 || !strcmp(pcsz, "no")
1178 || !strcmp(pcsz, "0")
1179 )
1180 {
1181 *pfValue = false;
1182 return true;
1183 }
1184 }
1185
1186 return false;
1187}
1188
1189/**
1190 * Convenience method which attempts to find the attribute with the given
1191 * name and returns its value as a string.
1192 *
1193 * @param pcszMatch Name of attribute to find.
1194 * @param ppcsz Where to return the attribute.
1195 * @param cchValueLimit If the length of the returned value exceeds this
1196 * limit a EIPRTFailure exception will be thrown.
1197 * @param pcszNamespace The attribute name space prefix or NULL.
1198 * @returns Boolean success indicator.
1199 */
1200bool ElementNode::getAttributeValueN(const char *pcszMatch, const char **ppcsz, size_t cchValueLimit, const char *pcszNamespace /*= NULL*/) const
1201{
1202 const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
1203 if (pAttr)
1204 {
1205 *ppcsz = pAttr->getValueN(cchValueLimit);
1206 return true;
1207 }
1208 return false;
1209}
1210
1211/**
1212 * Convenience method which attempts to find the attribute with the given
1213 * name and returns its value as a string.
1214 *
1215 * @param pcszMatch Name of attribute to find.
1216 * @param pStr Pointer to the string object that should receive the
1217 * attribute value.
1218 * @param cchValueLimit If the length of the returned value exceeds this
1219 * limit a EIPRTFailure exception will be thrown.
1220 * @param pcszNamespace The attribute name space prefix or NULL.
1221 * @returns Boolean success indicator.
1222 *
1223 * @throws Whatever the string class may throw on assignment.
1224 */
1225bool ElementNode::getAttributeValueN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace /*= NULL*/) const
1226{
1227 const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
1228 if (pAttr)
1229 {
1230 *pStr = pAttr->getValueN(cchValueLimit);
1231 return true;
1232 }
1233
1234 return false;
1235}
1236
1237/**
1238 * Like getAttributeValue (ministring variant), but makes sure that all backslashes
1239 * are converted to forward slashes.
1240 *
1241 * @param pcszMatch Name of attribute to find.
1242 * @param pStr Pointer to the string object that should
1243 * receive the attribute path value.
1244 * @param cchValueLimit If the length of the returned value exceeds this
1245 * limit a EIPRTFailure exception will be thrown.
1246 * @param pcszNamespace The attribute name space prefix or NULL.
1247 * @returns Boolean success indicator.
1248 */
1249bool ElementNode::getAttributeValuePathN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace /*= NULL*/) const
1250{
1251 if (getAttributeValueN(pcszMatch, pStr, cchValueLimit, pcszNamespace))
1252 {
1253 pStr->findReplace('\\', '/');
1254 return true;
1255 }
1256
1257 return false;
1258}
1259
1260
1261bool ElementNode::getElementValue(int32_t *piValue) const
1262{
1263 const char *pszValue = getValue();
1264 if (pszValue)
1265 {
1266 int rc = RTStrToInt32Ex(pszValue, NULL, 0, piValue);
1267 if (rc == VINF_SUCCESS)
1268 return true;
1269 }
1270 return false;
1271}
1272
1273bool ElementNode::getElementValue(uint32_t *puValue) const
1274{
1275 const char *pszValue = getValue();
1276 if (pszValue)
1277 {
1278 int rc = RTStrToUInt32Ex(pszValue, NULL, 0, puValue);
1279 if (rc == VINF_SUCCESS)
1280 return true;
1281 }
1282 return false;
1283}
1284
1285bool ElementNode::getElementValue(int64_t *piValue) const
1286{
1287 const char *pszValue = getValue();
1288 if (pszValue)
1289 {
1290 int rc = RTStrToInt64Ex(pszValue, NULL, 0, piValue);
1291 if (rc == VINF_SUCCESS)
1292 return true;
1293 }
1294 return false;
1295}
1296
1297bool ElementNode::getElementValue(uint64_t *puValue) const
1298{
1299 const char *pszValue = getValue();
1300 if (pszValue)
1301 {
1302 int rc = RTStrToUInt64Ex(pszValue, NULL, 0, puValue);
1303 if (rc == VINF_SUCCESS)
1304 return true;
1305 }
1306 return false;
1307}
1308
1309bool ElementNode::getElementValue(bool *pfValue) const
1310{
1311 const char *pszValue = getValue();
1312 if (pszValue)
1313 {
1314 if ( !strcmp(pszValue, "true")
1315 || !strcmp(pszValue, "yes")
1316 || !strcmp(pszValue, "1")
1317 )
1318 {
1319 *pfValue = true;
1320 return true;
1321 }
1322 if ( !strcmp(pszValue, "false")
1323 || !strcmp(pszValue, "no")
1324 || !strcmp(pszValue, "0")
1325 )
1326 {
1327 *pfValue = true;
1328 return true;
1329 }
1330 }
1331 return false;
1332}
1333
1334
1335/**
1336 * Creates a new child element node and appends it to the list
1337 * of children in "this".
1338 *
1339 * @param pcszElementName
1340 * @return
1341 */
1342ElementNode *ElementNode::createChild(const char *pcszElementName)
1343{
1344 // we must be an element, not an attribute
1345 if (!m_pLibNode)
1346 throw ENodeIsNotElement(RT_SRC_POS);
1347
1348 // libxml side: create new node
1349 xmlNode *pLibNode;
1350 if (!(pLibNode = xmlNewNode(NULL, // namespace
1351 (const xmlChar*)pcszElementName)))
1352 throw std::bad_alloc();
1353 xmlAddChild(m_pLibNode, pLibNode);
1354
1355 // now wrap this in C++
1356 ElementNode *p = new ElementNode(m_pElmRoot, this, &m_children, pLibNode);
1357 RTListAppend(&m_children, &p->m_listEntry);
1358
1359 return p;
1360}
1361
1362
1363/**
1364 * Creates a content node and appends it to the list of children
1365 * in "this".
1366 *
1367 * @param pcszContent
1368 * @return
1369 */
1370ContentNode *ElementNode::addContent(const char *pcszContent)
1371{
1372 // libxml side: create new node
1373 xmlNode *pLibNode = xmlNewText((const xmlChar*)pcszContent);
1374 if (!pLibNode)
1375 throw std::bad_alloc();
1376 xmlAddChild(m_pLibNode, pLibNode);
1377
1378 // now wrap this in C++
1379 ContentNode *p = new ContentNode(this, &m_children, pLibNode);
1380 RTListAppend(&m_children, &p->m_listEntry);
1381
1382 return p;
1383}
1384
1385/**
1386 * Changes the contents of node and appends it to the list of
1387 * children
1388 *
1389 * @param pcszContent
1390 * @return
1391 */
1392ContentNode *ElementNode::setContent(const char *pcszContent)
1393{
1394// 1. Update content
1395 xmlNodeSetContent(m_pLibNode, (const xmlChar*)pcszContent);
1396
1397// 2. Remove Content node from the list
1398 /* Check that the order is right. */
1399 xml::Node * pNode;
1400 RTListForEachCpp(&m_children, pNode, xml::Node, m_listEntry)
1401 {
1402 bool fLast = RTListNodeIsLast(&m_children, &pNode->m_listEntry);
1403
1404 if (pNode->isContent())
1405 {
1406 RTListNodeRemove(&pNode->m_listEntry);
1407 }
1408
1409 if (fLast)
1410 break;
1411 }
1412
1413// 3. Create a new node and append to the list
1414 // now wrap this in C++
1415 ContentNode *pCNode = new ContentNode(this, &m_children, m_pLibNode);
1416 RTListAppend(&m_children, &pCNode->m_listEntry);
1417
1418 return pCNode;
1419}
1420
1421/**
1422 * Sets the given attribute; overloaded version for const char *.
1423 *
1424 * If an attribute with the given name exists, it is overwritten,
1425 * otherwise a new attribute is created. Returns the attribute node
1426 * that was either created or changed.
1427 *
1428 * @param pcszName The attribute name.
1429 * @param pcszValue The attribute value.
1430 * @return Pointer to the attribute node that was created or modified.
1431 */
1432AttributeNode *ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
1433{
1434 /*
1435 * Do we already have an attribute and should we just update it?
1436 */
1437 AttributeNode *pAttr;
1438 RTListForEachCpp(&m_attributes, pAttr, AttributeNode, m_listEntry)
1439 {
1440 if (pAttr->nameEquals(pcszName))
1441 {
1442 /* Overwrite existing libxml attribute node ... */
1443 xmlAttrPtr pLibAttr = xmlSetProp(m_pLibNode, (xmlChar *)pcszName, (xmlChar *)pcszValue);
1444
1445 /* ... and update our C++ wrapper in case the attrib pointer changed. */
1446 pAttr->m_pLibAttr = pLibAttr;
1447 return pAttr;
1448 }
1449 }
1450
1451 /*
1452 * No existing attribute, create a new one.
1453 */
1454 /* libxml side: xmlNewProp creates an attribute. */
1455 xmlAttr *pLibAttr = xmlNewProp(m_pLibNode, (xmlChar *)pcszName, (xmlChar *)pcszValue);
1456
1457 /* C++ side: Create an attribute node around it. */
1458 pAttr = new AttributeNode(m_pElmRoot, this, &m_attributes, pLibAttr);
1459 RTListAppend(&m_attributes, &pAttr->m_listEntry);
1460
1461 return pAttr;
1462}
1463
1464/**
1465 * Like setAttribute (ministring variant), but replaces all backslashes with forward slashes
1466 * before calling that one.
1467 * @param pcszName
1468 * @param strValue
1469 * @return
1470 */
1471AttributeNode* ElementNode::setAttributePath(const char *pcszName, const RTCString &strValue)
1472{
1473 RTCString strTemp(strValue);
1474 strTemp.findReplace('\\', '/');
1475 return setAttribute(pcszName, strTemp.c_str());
1476}
1477
1478/**
1479 * Sets the given attribute; overloaded version for int32_t.
1480 *
1481 * If an attribute with the given name exists, it is overwritten,
1482 * otherwise a new attribute is created. Returns the attribute node
1483 * that was either created or changed.
1484 *
1485 * @param pcszName
1486 * @param i
1487 * @return
1488 */
1489AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
1490{
1491 char szValue[12]; // negative sign + 10 digits + \0
1492 RTStrPrintf(szValue, sizeof(szValue), "%RI32", i);
1493 AttributeNode *p = setAttribute(pcszName, szValue);
1494 return p;
1495}
1496
1497/**
1498 * Sets the given attribute; overloaded version for uint32_t.
1499 *
1500 * If an attribute with the given name exists, it is overwritten,
1501 * otherwise a new attribute is created. Returns the attribute node
1502 * that was either created or changed.
1503 *
1504 * @param pcszName
1505 * @param u
1506 * @return
1507 */
1508AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t u)
1509{
1510 char szValue[11]; // 10 digits + \0
1511 RTStrPrintf(szValue, sizeof(szValue), "%RU32", u);
1512 AttributeNode *p = setAttribute(pcszName, szValue);
1513 return p;
1514}
1515
1516/**
1517 * Sets the given attribute; overloaded version for int64_t.
1518 *
1519 * If an attribute with the given name exists, it is overwritten,
1520 * otherwise a new attribute is created. Returns the attribute node
1521 * that was either created or changed.
1522 *
1523 * @param pcszName
1524 * @param i
1525 * @return
1526 */
1527AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
1528{
1529 char szValue[21]; // negative sign + 19 digits + \0
1530 RTStrPrintf(szValue, sizeof(szValue), "%RI64", i);
1531 AttributeNode *p = setAttribute(pcszName, szValue);
1532 return p;
1533}
1534
1535/**
1536 * Sets the given attribute; overloaded version for uint64_t.
1537 *
1538 * If an attribute with the given name exists, it is overwritten,
1539 * otherwise a new attribute is created. Returns the attribute node
1540 * that was either created or changed.
1541 *
1542 * @param pcszName
1543 * @param u
1544 * @return
1545 */
1546AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t u)
1547{
1548 char szValue[21]; // 20 digits + \0
1549 RTStrPrintf(szValue, sizeof(szValue), "%RU64", u);
1550 AttributeNode *p = setAttribute(pcszName, szValue);
1551 return p;
1552}
1553
1554/**
1555 * Sets the given attribute to the given uint32_t, outputs a hexadecimal string.
1556 *
1557 * If an attribute with the given name exists, it is overwritten,
1558 * otherwise a new attribute is created. Returns the attribute node
1559 * that was either created or changed.
1560 *
1561 * @param pcszName
1562 * @param u
1563 * @return
1564 */
1565AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t u)
1566{
1567 char szValue[11]; // "0x" + 8 digits + \0
1568 RTStrPrintf(szValue, sizeof(szValue), "0x%RX32", u);
1569 AttributeNode *p = setAttribute(pcszName, szValue);
1570 return p;
1571}
1572
1573/**
1574 * Sets the given attribute; overloaded version for bool.
1575 *
1576 * If an attribute with the given name exists, it is overwritten,
1577 * otherwise a new attribute is created. Returns the attribute node
1578 * that was either created or changed.
1579 *
1580 * @param pcszName The attribute name.
1581 * @param f The attribute value.
1582 * @return
1583 */
1584AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
1585{
1586 return setAttribute(pcszName, (f) ? "true" : "false");
1587}
1588
1589/**
1590 * Private constructor for a new attribute node.
1591 *
1592 * @param pElmRoot Pointer to the root element. Needed for getting the
1593 * default name space.
1594 * @param pParent Pointer to the parent element (always an ElementNode,
1595 * despite the type). NULL for the root node.
1596 * @param pListAnchor Pointer to the m_children member of the parent. NULL
1597 * for the root node.
1598 * @param pLibAttr Pointer to the libxml2 attribute structure.
1599 */
1600AttributeNode::AttributeNode(const ElementNode *pElmRoot,
1601 Node *pParent,
1602 PRTLISTANCHOR pListAnchor,
1603 xmlAttr *pLibAttr)
1604 : Node(IsAttribute,
1605 pParent,
1606 pListAnchor,
1607 NULL,
1608 pLibAttr)
1609{
1610 m_pcszName = (const char *)pLibAttr->name;
1611 RT_NOREF_PV(pElmRoot);
1612
1613 if ( pLibAttr->ns
1614 && pLibAttr->ns->prefix)
1615 {
1616 m_pcszNamespacePrefix = (const char *)pLibAttr->ns->prefix;
1617 m_pcszNamespaceHref = (const char *)pLibAttr->ns->href;
1618 }
1619}
1620
1621ContentNode::ContentNode(Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode)
1622 : Node(IsContent,
1623 pParent,
1624 pListAnchor,
1625 pLibNode,
1626 NULL)
1627{
1628}
1629
1630/*
1631 * NodesLoop
1632 *
1633 */
1634
1635struct NodesLoop::Data
1636{
1637 ElementNodesList listElements;
1638 ElementNodesList::const_iterator it;
1639};
1640
1641NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
1642{
1643 m = new Data;
1644 node.getChildElements(m->listElements, pcszMatch);
1645 m->it = m->listElements.begin();
1646}
1647
1648NodesLoop::~NodesLoop()
1649{
1650 delete m;
1651}
1652
1653
1654/**
1655 * Handy convenience helper for looping over all child elements. Create an
1656 * instance of NodesLoop on the stack and call this method until it returns
1657 * NULL, like this:
1658 * <code>
1659 * xml::ElementNode node; // should point to an element
1660 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
1661 * const xml::ElementNode *pChild = NULL;
1662 * while (pChild = loop.forAllNodes())
1663 * ...;
1664 * </code>
1665 * @return
1666 */
1667const ElementNode* NodesLoop::forAllNodes() const
1668{
1669 const ElementNode *pNode = NULL;
1670
1671 if (m->it != m->listElements.end())
1672 {
1673 pNode = *(m->it);
1674 ++(m->it);
1675 }
1676
1677 return pNode;
1678}
1679
1680////////////////////////////////////////////////////////////////////////////////
1681//
1682// Document class
1683//
1684////////////////////////////////////////////////////////////////////////////////
1685
1686struct Document::Data
1687{
1688 xmlDocPtr plibDocument;
1689 ElementNode *pRootElement;
1690 ElementNode *pComment;
1691
1692 Data()
1693 {
1694 plibDocument = NULL;
1695 pRootElement = NULL;
1696 pComment = NULL;
1697 }
1698
1699 ~Data()
1700 {
1701 reset();
1702 }
1703
1704 void reset()
1705 {
1706 if (plibDocument)
1707 {
1708 xmlFreeDoc(plibDocument);
1709 plibDocument = NULL;
1710 }
1711 if (pRootElement)
1712 {
1713 delete pRootElement;
1714 pRootElement = NULL;
1715 }
1716 if (pComment)
1717 {
1718 delete pComment;
1719 pComment = NULL;
1720 }
1721 }
1722
1723 void copyFrom(const Document::Data *p)
1724 {
1725 if (p->plibDocument)
1726 {
1727 plibDocument = xmlCopyDoc(p->plibDocument,
1728 1); // recursive == copy all
1729 }
1730 }
1731};
1732
1733Document::Document()
1734 : m(new Data)
1735{
1736}
1737
1738Document::Document(const Document &x)
1739 : m(new Data)
1740{
1741 m->copyFrom(x.m);
1742}
1743
1744Document& Document::operator=(const Document &x)
1745{
1746 m->reset();
1747 m->copyFrom(x.m);
1748 return *this;
1749}
1750
1751Document::~Document()
1752{
1753 delete m;
1754}
1755
1756/**
1757 * private method to refresh all internal structures after the internal pDocument
1758 * has changed. Called from XmlFileParser::read(). m->reset() must have been
1759 * called before to make sure all members except the internal pDocument are clean.
1760 */
1761void Document::refreshInternals() // private
1762{
1763 m->pRootElement = new ElementNode(NULL, NULL, NULL, xmlDocGetRootElement(m->plibDocument));
1764
1765 ElementNode::buildChildren(m->pRootElement);
1766}
1767
1768/**
1769 * Returns the root element of the document, or NULL if the document is empty.
1770 * Const variant.
1771 * @return
1772 */
1773const ElementNode *Document::getRootElement() const
1774{
1775 return m->pRootElement;
1776}
1777
1778/**
1779 * Returns the root element of the document, or NULL if the document is empty.
1780 * Non-const variant.
1781 * @return
1782 */
1783ElementNode *Document::getRootElement()
1784{
1785 return m->pRootElement;
1786}
1787
1788/**
1789 * Creates a new element node and sets it as the root element.
1790 *
1791 * This will only work if the document is empty; otherwise EDocumentNotEmpty is
1792 * thrown.
1793 */
1794ElementNode *Document::createRootElement(const char *pcszRootElementName,
1795 const char *pcszComment /* = NULL */)
1796{
1797 if (m->pRootElement || m->plibDocument)
1798 throw EDocumentNotEmpty(RT_SRC_POS);
1799
1800 // libxml side: create document, create root node
1801 m->plibDocument = xmlNewDoc((const xmlChar *)"1.0");
1802 xmlNode *plibRootNode = xmlNewNode(NULL /*namespace*/ , (const xmlChar *)pcszRootElementName);
1803 if (!plibRootNode)
1804 throw std::bad_alloc();
1805 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1806
1807 // now wrap this in C++
1808 m->pRootElement = new ElementNode(NULL, NULL, NULL, plibRootNode);
1809
1810 // add document global comment if specified
1811 if (pcszComment != NULL)
1812 {
1813 xmlNode *pComment = xmlNewDocComment(m->plibDocument, (const xmlChar *)pcszComment);
1814 if (!pComment)
1815 throw std::bad_alloc();
1816 xmlAddPrevSibling(plibRootNode, pComment);
1817
1818 // now wrap this in C++
1819 m->pComment = new ElementNode(NULL, NULL, NULL, pComment);
1820 }
1821
1822 return m->pRootElement;
1823}
1824
1825////////////////////////////////////////////////////////////////////////////////
1826//
1827// XmlParserBase class
1828//
1829////////////////////////////////////////////////////////////////////////////////
1830
1831static void xmlParserBaseGenericError(void *pCtx, const char *pszMsg, ...)
1832{
1833 NOREF(pCtx);
1834 va_list args;
1835 va_start(args, pszMsg);
1836 RTLogRelPrintfV(pszMsg, args);
1837 va_end(args);
1838}
1839
1840static void xmlParserBaseStructuredError(void *pCtx, xmlErrorPtr error)
1841{
1842 NOREF(pCtx);
1843 /* we expect that there is always a trailing NL */
1844 LogRel(("XML error at '%s' line %d: %s", error->file, error->line, error->message));
1845}
1846
1847XmlParserBase::XmlParserBase()
1848{
1849 m_ctxt = xmlNewParserCtxt();
1850 if (m_ctxt == NULL)
1851 throw std::bad_alloc();
1852 /* per-thread so it must be here */
1853 xmlSetGenericErrorFunc(NULL, xmlParserBaseGenericError);
1854 xmlSetStructuredErrorFunc(NULL, xmlParserBaseStructuredError);
1855}
1856
1857XmlParserBase::~XmlParserBase()
1858{
1859 xmlSetStructuredErrorFunc(NULL, NULL);
1860 xmlSetGenericErrorFunc(NULL, NULL);
1861 xmlFreeParserCtxt (m_ctxt);
1862 m_ctxt = NULL;
1863}
1864
1865////////////////////////////////////////////////////////////////////////////////
1866//
1867// XmlMemParser class
1868//
1869////////////////////////////////////////////////////////////////////////////////
1870
1871XmlMemParser::XmlMemParser()
1872 : XmlParserBase()
1873{
1874}
1875
1876XmlMemParser::~XmlMemParser()
1877{
1878}
1879
1880/**
1881 * Parse the given buffer and fills the given Document object with its contents.
1882 * Throws XmlError on parsing errors.
1883 *
1884 * The document that is passed in will be reset before being filled if not empty.
1885 *
1886 * @param pvBuf Memory buffer to parse.
1887 * @param cbSize Size of the memory buffer.
1888 * @param strFilename Refernece to the name of the file we're parsing.
1889 * @param doc Reference to the output document. This will be reset
1890 * and filled with data according to file contents.
1891 */
1892void XmlMemParser::read(const void *pvBuf, size_t cbSize,
1893 const RTCString &strFilename,
1894 Document &doc)
1895{
1896 GlobalLock lock;
1897// global.setExternalEntityLoader(ExternalEntityLoader);
1898
1899 const char *pcszFilename = strFilename.c_str();
1900
1901 doc.m->reset();
1902 const int options = XML_PARSE_NOBLANKS /* remove blank nodes */
1903 | XML_PARSE_NONET /* forbit any network access */
1904#if LIBXML_VERSION >= 20700
1905 | XML_PARSE_HUGE /* don't restrict the node depth
1906 to 256 (bad for snapshots!) */
1907#endif
1908 ;
1909 if (!(doc.m->plibDocument = xmlCtxtReadMemory(m_ctxt,
1910 (const char*)pvBuf,
1911 (int)cbSize,
1912 pcszFilename,
1913 NULL, // encoding = auto
1914 options)))
1915 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1916
1917 doc.refreshInternals();
1918}
1919
1920////////////////////////////////////////////////////////////////////////////////
1921//
1922// XmlMemWriter class
1923//
1924////////////////////////////////////////////////////////////////////////////////
1925
1926XmlMemWriter::XmlMemWriter()
1927 : m_pBuf(0)
1928{
1929}
1930
1931XmlMemWriter::~XmlMemWriter()
1932{
1933 if (m_pBuf)
1934 xmlFree(m_pBuf);
1935}
1936
1937void XmlMemWriter::write(const Document &doc, void **ppvBuf, size_t *pcbSize)
1938{
1939 if (m_pBuf)
1940 {
1941 xmlFree(m_pBuf);
1942 m_pBuf = 0;
1943 }
1944 int size;
1945 xmlDocDumpFormatMemory(doc.m->plibDocument, (xmlChar**)&m_pBuf, &size, 1);
1946 *ppvBuf = m_pBuf;
1947 *pcbSize = size;
1948}
1949
1950
1951////////////////////////////////////////////////////////////////////////////////
1952//
1953// XmlStringWriter class
1954//
1955////////////////////////////////////////////////////////////////////////////////
1956
1957XmlStringWriter::XmlStringWriter()
1958 : m_pStrDst(NULL), m_fOutOfMemory(false)
1959{
1960}
1961
1962int XmlStringWriter::write(const Document &rDoc, RTCString *pStrDst)
1963{
1964 /*
1965 * Clear the output string and take the global libxml2 lock so we can
1966 * safely configure the output formatting.
1967 */
1968 pStrDst->setNull();
1969
1970 GlobalLock lock;
1971
1972 xmlIndentTreeOutput = 1;
1973 xmlTreeIndentString = " ";
1974 xmlSaveNoEmptyTags = 0;
1975
1976 /*
1977 * Do a pass to calculate the size.
1978 */
1979 size_t cbOutput = 1; /* zero term */
1980
1981 xmlSaveCtxtPtr pSaveCtx= xmlSaveToIO(WriteCallbackForSize, CloseCallback, &cbOutput, NULL /*pszEncoding*/, XML_SAVE_FORMAT);
1982 if (!pSaveCtx)
1983 return VERR_NO_MEMORY;
1984
1985 long rcXml = xmlSaveDoc(pSaveCtx, rDoc.m->plibDocument);
1986 xmlSaveClose(pSaveCtx);
1987 if (rcXml == -1)
1988 return VERR_GENERAL_FAILURE;
1989
1990 /*
1991 * Try resize the string.
1992 */
1993 int rc = pStrDst->reserveNoThrow(cbOutput);
1994 if (RT_SUCCESS(rc))
1995 {
1996 /*
1997 * Do the real run where we feed output to the string.
1998 */
1999 m_pStrDst = pStrDst;
2000 m_fOutOfMemory = false;
2001 pSaveCtx = xmlSaveToIO(WriteCallbackForReal, CloseCallback, this, NULL /*pszEncoding*/, XML_SAVE_FORMAT);
2002 if (pSaveCtx)
2003 {
2004 rcXml = xmlSaveDoc(pSaveCtx, rDoc.m->plibDocument);
2005 xmlSaveClose(pSaveCtx);
2006 m_pStrDst = NULL;
2007 if (rcXml != -1)
2008 {
2009 if (!m_fOutOfMemory)
2010 return VINF_SUCCESS;
2011
2012 rc = VERR_NO_STR_MEMORY;
2013 }
2014 else
2015 rc = VERR_GENERAL_FAILURE;
2016 }
2017 else
2018 rc = VERR_NO_MEMORY;
2019 pStrDst->setNull();
2020 m_pStrDst = NULL;
2021 }
2022 return rc;
2023}
2024
2025/*static*/ int XmlStringWriter::WriteCallbackForSize(void *pvUser, const char *pachBuf, int cbToWrite)
2026{
2027 if (cbToWrite > 0)
2028 *(size_t *)pvUser += (unsigned)cbToWrite;
2029 RT_NOREF(pachBuf);
2030 return cbToWrite;
2031}
2032
2033/*static*/ int XmlStringWriter::WriteCallbackForReal(void *pvUser, const char *pachBuf, int cbToWrite)
2034{
2035 XmlStringWriter *pThis = static_cast<XmlStringWriter*>(pvUser);
2036 if (!pThis->m_fOutOfMemory)
2037 {
2038 if (cbToWrite > 0)
2039 {
2040 try
2041 {
2042 pThis->m_pStrDst->append(pachBuf, (size_t)cbToWrite);
2043 }
2044 catch (std::bad_alloc &)
2045 {
2046 pThis->m_fOutOfMemory = true;
2047 return -1;
2048 }
2049 }
2050 return cbToWrite;
2051 }
2052 return -1; /* failure */
2053}
2054
2055int XmlStringWriter::CloseCallback(void *pvUser)
2056{
2057 /* Nothing to do here. */
2058 RT_NOREF(pvUser);
2059 return 0;
2060}
2061
2062
2063
2064////////////////////////////////////////////////////////////////////////////////
2065//
2066// XmlFileParser class
2067//
2068////////////////////////////////////////////////////////////////////////////////
2069
2070struct XmlFileParser::Data
2071{
2072 RTCString strXmlFilename;
2073
2074 Data()
2075 {
2076 }
2077
2078 ~Data()
2079 {
2080 }
2081};
2082
2083XmlFileParser::XmlFileParser()
2084 : XmlParserBase(),
2085 m(new Data())
2086{
2087}
2088
2089XmlFileParser::~XmlFileParser()
2090{
2091 delete m;
2092 m = NULL;
2093}
2094
2095struct IOContext
2096{
2097 File file;
2098 RTCString error;
2099
2100 IOContext(const char *pcszFilename, File::Mode mode, bool fFlush = false)
2101 : file(mode, pcszFilename, fFlush)
2102 {
2103 }
2104
2105 void setError(const RTCError &x)
2106 {
2107 error = x.what();
2108 }
2109
2110 void setError(const std::exception &x)
2111 {
2112 error = x.what();
2113 }
2114
2115private:
2116 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(IOContext); /* (shuts up C4626 and C4625 MSC warnings) */
2117};
2118
2119struct ReadContext : IOContext
2120{
2121 ReadContext(const char *pcszFilename)
2122 : IOContext(pcszFilename, File::Mode_Read)
2123 {
2124 }
2125
2126private:
2127 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(ReadContext); /* (shuts up C4626 and C4625 MSC warnings) */
2128};
2129
2130struct WriteContext : IOContext
2131{
2132 WriteContext(const char *pcszFilename, bool fFlush)
2133 : IOContext(pcszFilename, File::Mode_Overwrite, fFlush)
2134 {
2135 }
2136
2137private:
2138 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(WriteContext); /* (shuts up C4626 and C4625 MSC warnings) */
2139};
2140
2141/**
2142 * Reads the given file and fills the given Document object with its contents.
2143 * Throws XmlError on parsing errors.
2144 *
2145 * The document that is passed in will be reset before being filled if not empty.
2146 *
2147 * @param strFilename in: name fo file to parse.
2148 * @param doc out: document to be reset and filled with data according to file contents.
2149 */
2150void XmlFileParser::read(const RTCString &strFilename,
2151 Document &doc)
2152{
2153 GlobalLock lock;
2154// global.setExternalEntityLoader(ExternalEntityLoader);
2155
2156 m->strXmlFilename = strFilename;
2157 const char *pcszFilename = strFilename.c_str();
2158
2159 ReadContext context(pcszFilename);
2160 doc.m->reset();
2161 const int options = XML_PARSE_NOBLANKS /* remove blank nodes */
2162 | XML_PARSE_NONET /* forbit any network access */
2163#if LIBXML_VERSION >= 20700
2164 | XML_PARSE_HUGE /* don't restrict the node depth
2165 to 256 (bad for snapshots!) */
2166#endif
2167 ;
2168 if (!(doc.m->plibDocument = xmlCtxtReadIO(m_ctxt,
2169 ReadCallback,
2170 CloseCallback,
2171 &context,
2172 pcszFilename,
2173 NULL, // encoding = auto
2174 options)))
2175 throw XmlError(xmlCtxtGetLastError(m_ctxt));
2176
2177 doc.refreshInternals();
2178}
2179
2180// static
2181int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
2182{
2183 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
2184
2185 /* To prevent throwing exceptions while inside libxml2 code, we catch
2186 * them and forward to our level using a couple of variables. */
2187
2188 try
2189 {
2190 return pContext->file.read(aBuf, aLen);
2191 }
2192 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
2193 catch (const RTCError &err) { pContext->setError(err); }
2194 catch (const std::exception &err) { pContext->setError(err); }
2195 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
2196
2197 return -1 /* failure */;
2198}
2199
2200int XmlFileParser::CloseCallback(void *aCtxt)
2201{
2202 /// @todo to be written
2203 NOREF(aCtxt);
2204
2205 return -1;
2206}
2207
2208////////////////////////////////////////////////////////////////////////////////
2209//
2210// XmlFileWriter class
2211//
2212////////////////////////////////////////////////////////////////////////////////
2213
2214struct XmlFileWriter::Data
2215{
2216 Document *pDoc;
2217};
2218
2219XmlFileWriter::XmlFileWriter(Document &doc)
2220{
2221 m = new Data();
2222 m->pDoc = &doc;
2223}
2224
2225XmlFileWriter::~XmlFileWriter()
2226{
2227 delete m;
2228}
2229
2230void XmlFileWriter::writeInternal(const char *pcszFilename, bool fSafe)
2231{
2232 WriteContext context(pcszFilename, fSafe);
2233
2234 GlobalLock lock;
2235
2236 /* serialize to the stream */
2237 xmlIndentTreeOutput = 1;
2238 xmlTreeIndentString = " ";
2239 xmlSaveNoEmptyTags = 0;
2240
2241 xmlSaveCtxtPtr saveCtxt;
2242 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
2243 CloseCallback,
2244 &context,
2245 NULL,
2246 XML_SAVE_FORMAT)))
2247 throw xml::LogicError(RT_SRC_POS);
2248
2249 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
2250 if (rc == -1)
2251 {
2252 /* look if there was a forwarded exception from the lower level */
2253// if (m->trappedErr.get() != NULL)
2254// m->trappedErr->rethrow();
2255
2256 /* there must be an exception from the Output implementation,
2257 * otherwise the save operation must always succeed. */
2258 throw xml::LogicError(RT_SRC_POS);
2259 }
2260
2261 xmlSaveClose(saveCtxt);
2262}
2263
2264void XmlFileWriter::write(const char *pcszFilename, bool fSafe)
2265{
2266 if (!fSafe)
2267 writeInternal(pcszFilename, fSafe);
2268 else
2269 {
2270 /* Empty string and directory spec must be avoid. */
2271 if (RTPathFilename(pcszFilename) == NULL)
2272 throw xml::LogicError(RT_SRC_POS);
2273
2274 /* Construct both filenames first to ease error handling. */
2275 char szTmpFilename[RTPATH_MAX];
2276 int rc = RTStrCopy(szTmpFilename, sizeof(szTmpFilename) - strlen(s_pszTmpSuff), pcszFilename);
2277 if (RT_FAILURE(rc))
2278 throw EIPRTFailure(rc, "RTStrCopy");
2279 strcat(szTmpFilename, s_pszTmpSuff);
2280
2281 char szPrevFilename[RTPATH_MAX];
2282 rc = RTStrCopy(szPrevFilename, sizeof(szPrevFilename) - strlen(s_pszPrevSuff), pcszFilename);
2283 if (RT_FAILURE(rc))
2284 throw EIPRTFailure(rc, "RTStrCopy");
2285 strcat(szPrevFilename, s_pszPrevSuff);
2286
2287 /* Write the XML document to the temporary file. */
2288 writeInternal(szTmpFilename, fSafe);
2289
2290 /* Make a backup of any existing file (ignore failure). */
2291 uint64_t cbPrevFile;
2292 rc = RTFileQuerySizeByPath(pcszFilename, &cbPrevFile);
2293 if (RT_SUCCESS(rc) && cbPrevFile >= 16)
2294 RTFileRename(pcszFilename, szPrevFilename, RTPATHRENAME_FLAGS_REPLACE);
2295
2296 /* Commit the temporary file. Just leave the tmp file behind on failure. */
2297 rc = RTFileRename(szTmpFilename, pcszFilename, RTPATHRENAME_FLAGS_REPLACE);
2298 if (RT_FAILURE(rc))
2299 throw EIPRTFailure(rc, "Failed to replace '%s' with '%s'", pcszFilename, szTmpFilename);
2300
2301 /* Flush the directory changes (required on linux at least). */
2302 RTPathStripFilename(szTmpFilename);
2303 rc = RTDirFlush(szTmpFilename);
2304 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
2305 }
2306}
2307
2308int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
2309{
2310 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
2311
2312 /* To prevent throwing exceptions while inside libxml2 code, we catch
2313 * them and forward to our level using a couple of variables. */
2314 try
2315 {
2316 return pContext->file.write(aBuf, aLen);
2317 }
2318 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
2319 catch (const RTCError &err) { pContext->setError(err); }
2320 catch (const std::exception &err) { pContext->setError(err); }
2321 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
2322
2323 return -1 /* failure */;
2324}
2325
2326int XmlFileWriter::CloseCallback(void *aCtxt)
2327{
2328 /// @todo to be written
2329 NOREF(aCtxt);
2330
2331 return -1;
2332}
2333
2334/*static*/ const char * const XmlFileWriter::s_pszTmpSuff = "-tmp";
2335/*static*/ const char * const XmlFileWriter::s_pszPrevSuff = "-prev";
2336
2337
2338} // end namespace xml
2339
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use