VirtualBox

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

Last change on this file was 104176, checked in by vboxsync, 2 months ago

Runtime/xml: preparation for libxml2-2.12.6 bugref:10640

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

© 2023 Oracle
ContactPrivacy policyTerms of Use