VirtualBox

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

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

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette