VirtualBox

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

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

IPRT/xml: Fixed tiny leak in File::File(). bugref:9841

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