VirtualBox

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

Last change on this file since 46668 was 46169, checked in by vboxsync, 12 years ago

Changes in parsing, extracting and processing an OVF version in/from a XML file in the OVF package.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.3 KB
Line 
1/* $Id: xml.cpp 46169 2013-05-20 07:57:55Z vboxsync $ */
2/** @file
3 * IPRT - XML Manipulation API.
4 */
5
6/*
7 * Copyright (C) 2007-2012 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#include <iprt/dir.h>
28#include <iprt/file.h>
29#include <iprt/err.h>
30#include <iprt/param.h>
31#include <iprt/path.h>
32#include <iprt/cpp/lock.h>
33#include <iprt/cpp/xml.h>
34
35#include <libxml/tree.h>
36#include <libxml/parser.h>
37#include <libxml/globals.h>
38#include <libxml/xmlIO.h>
39#include <libxml/xmlsave.h>
40#include <libxml/uri.h>
41
42#include <libxml/xmlschemas.h>
43
44#include <map>
45#include <boost/shared_ptr.hpp>
46
47////////////////////////////////////////////////////////////////////////////////
48//
49// globals
50//
51////////////////////////////////////////////////////////////////////////////////
52
53/**
54 * Global module initialization structure. This is to wrap non-reentrant bits
55 * of libxml, among other things.
56 *
57 * The constructor and destructor of this structure are used to perform global
58 * module initialization and cleanup. There must be only one global variable of
59 * this structure.
60 */
61static
62class Global
63{
64public:
65
66 Global()
67 {
68 /* Check the parser version. The docs say it will kill the app if
69 * there is a serious version mismatch, but I couldn't find it in the
70 * source code (it only prints the error/warning message to the console) so
71 * let's leave it as is for informational purposes. */
72 LIBXML_TEST_VERSION
73
74 /* Init libxml */
75 xmlInitParser();
76
77 /* Save the default entity resolver before someone has replaced it */
78 sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
79 }
80
81 ~Global()
82 {
83 /* Shutdown libxml */
84 xmlCleanupParser();
85 }
86
87 struct
88 {
89 xmlExternalEntityLoader defaultEntityLoader;
90
91 /** Used to provide some thread safety missing in libxml2 (see e.g.
92 * XmlTreeBackend::read()) */
93 RTCLockMtx lock;
94 }
95 sxml; /* XXX naming this xml will break with gcc-3.3 */
96}
97gGlobal;
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
135char *XmlError::Format(xmlErrorPtr aErr)
136{
137 const char *msg = aErr->message ? aErr->message : "<none>";
138 size_t msgLen = strlen(msg);
139 /* strip spaces, trailing EOLs and dot-like char */
140 while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
141 --msgLen;
142
143 char *finalMsg = NULL;
144 RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
145 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
146
147 return finalMsg;
148}
149
150EIPRTFailure::EIPRTFailure(int aRC, const char *pcszContext, ...)
151 : RuntimeError(NULL),
152 mRC(aRC)
153{
154 char *pszContext2;
155 va_list args;
156 va_start(args, pcszContext);
157 RTStrAPrintfV(&pszContext2, pcszContext, args);
158 char *newMsg;
159 RTStrAPrintf(&newMsg, "%s: %d (%s)", pszContext2, aRC, RTErrGetShort(aRC));
160 setWhat(newMsg);
161 RTStrFree(newMsg);
162 RTStrFree(pszContext2);
163}
164
165////////////////////////////////////////////////////////////////////////////////
166//
167// File Class
168//
169//////////////////////////////////////////////////////////////////////////////
170
171struct File::Data
172{
173 Data()
174 : handle(NIL_RTFILE), opened(false)
175 { }
176
177 RTCString strFileName;
178 RTFILE handle;
179 bool opened : 1;
180 bool flushOnClose : 1;
181};
182
183File::File(Mode aMode, const char *aFileName, bool aFlushIt /* = false */)
184 : m(new Data())
185{
186 m->strFileName = aFileName;
187 m->flushOnClose = aFlushIt;
188
189 uint32_t flags = 0;
190 switch (aMode)
191 {
192 /** @todo change to RTFILE_O_DENY_WRITE where appropriate. */
193 case Mode_Read:
194 flags = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
195 break;
196 case Mode_WriteCreate: // fail if file exists
197 flags = RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE;
198 break;
199 case Mode_Overwrite: // overwrite if file exists
200 flags = RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE;
201 break;
202 case Mode_ReadWrite:
203 flags = RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;;
204 }
205
206 int vrc = RTFileOpen(&m->handle, aFileName, flags);
207 if (RT_FAILURE(vrc))
208 throw EIPRTFailure(vrc, "Runtime error opening '%s' for reading", aFileName);
209
210 m->opened = true;
211 m->flushOnClose = aFlushIt && (flags & RTFILE_O_ACCESS_MASK) != RTFILE_O_READ;
212}
213
214File::File(RTFILE aHandle, const char *aFileName /* = NULL */, bool aFlushIt /* = false */)
215 : m(new Data())
216{
217 if (aHandle == NIL_RTFILE)
218 throw EInvalidArg(RT_SRC_POS);
219
220 m->handle = aHandle;
221
222 if (aFileName)
223 m->strFileName = aFileName;
224
225 m->flushOnClose = aFlushIt;
226
227 setPos(0);
228}
229
230File::~File()
231{
232 if (m->flushOnClose)
233 {
234 RTFileFlush(m->handle);
235 if (!m->strFileName.isEmpty())
236 RTDirFlushParent(m->strFileName.c_str());
237 }
238
239 if (m->opened)
240 RTFileClose(m->handle);
241 delete m;
242}
243
244const char* File::uri() const
245{
246 return m->strFileName.c_str();
247}
248
249uint64_t File::pos() const
250{
251 uint64_t p = 0;
252 int vrc = RTFileSeek(m->handle, 0, RTFILE_SEEK_CURRENT, &p);
253 if (RT_SUCCESS(vrc))
254 return p;
255
256 throw EIPRTFailure(vrc, "Runtime error seeking in file '%s'", m->strFileName.c_str());
257}
258
259void File::setPos(uint64_t aPos)
260{
261 uint64_t p = 0;
262 unsigned method = RTFILE_SEEK_BEGIN;
263 int vrc = VINF_SUCCESS;
264
265 /* check if we overflow int64_t and move to INT64_MAX first */
266 if ((int64_t)aPos < 0)
267 {
268 vrc = RTFileSeek(m->handle, INT64_MAX, method, &p);
269 aPos -= (uint64_t)INT64_MAX;
270 method = RTFILE_SEEK_CURRENT;
271 }
272 /* seek the rest */
273 if (RT_SUCCESS(vrc))
274 vrc = RTFileSeek(m->handle, (int64_t) aPos, method, &p);
275 if (RT_SUCCESS(vrc))
276 return;
277
278 throw EIPRTFailure(vrc, "Runtime error seeking in file '%s'", m->strFileName.c_str());
279}
280
281int File::read(char *aBuf, int aLen)
282{
283 size_t len = aLen;
284 int vrc = RTFileRead(m->handle, aBuf, len, &len);
285 if (RT_SUCCESS(vrc))
286 return (int)len;
287
288 throw EIPRTFailure(vrc, "Runtime error reading from file '%s'", m->strFileName.c_str());
289}
290
291int File::write(const char *aBuf, int aLen)
292{
293 size_t len = aLen;
294 int vrc = RTFileWrite (m->handle, aBuf, len, &len);
295 if (RT_SUCCESS (vrc))
296 return (int)len;
297
298 throw EIPRTFailure(vrc, "Runtime error writing to file '%s'", m->strFileName.c_str());
299
300 return -1 /* failure */;
301}
302
303void File::truncate()
304{
305 int vrc = RTFileSetSize (m->handle, pos());
306 if (RT_SUCCESS (vrc))
307 return;
308
309 throw EIPRTFailure(vrc, "Runtime error truncating file '%s'", m->strFileName.c_str());
310}
311
312////////////////////////////////////////////////////////////////////////////////
313//
314// MemoryBuf Class
315//
316//////////////////////////////////////////////////////////////////////////////
317
318struct MemoryBuf::Data
319{
320 Data()
321 : buf (NULL), len (0), uri (NULL), pos (0) {}
322
323 const char *buf;
324 size_t len;
325 char *uri;
326
327 size_t pos;
328};
329
330MemoryBuf::MemoryBuf (const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
331 : m (new Data())
332{
333 if (aBuf == NULL)
334 throw EInvalidArg (RT_SRC_POS);
335
336 m->buf = aBuf;
337 m->len = aLen;
338 m->uri = RTStrDup (aURI);
339}
340
341MemoryBuf::~MemoryBuf()
342{
343 RTStrFree (m->uri);
344}
345
346const char *MemoryBuf::uri() const
347{
348 return m->uri;
349}
350
351uint64_t MemoryBuf::pos() const
352{
353 return m->pos;
354}
355
356void MemoryBuf::setPos (uint64_t aPos)
357{
358 size_t off = (size_t) aPos;
359 if ((uint64_t) off != aPos)
360 throw EInvalidArg();
361
362 if (off > m->len)
363 throw EInvalidArg();
364
365 m->pos = off;
366}
367
368int MemoryBuf::read (char *aBuf, int aLen)
369{
370 if (m->pos >= m->len)
371 return 0 /* nothing to read */;
372
373 size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
374 memcpy (aBuf, m->buf + m->pos, len);
375 m->pos += len;
376
377 return (int)len;
378}
379
380////////////////////////////////////////////////////////////////////////////////
381//
382// GlobalLock class
383//
384////////////////////////////////////////////////////////////////////////////////
385
386struct GlobalLock::Data
387{
388 PFNEXTERNALENTITYLOADER pOldLoader;
389 RTCLock lock;
390
391 Data()
392 : pOldLoader(NULL),
393 lock(gGlobal.sxml.lock)
394 {
395 }
396};
397
398GlobalLock::GlobalLock()
399 : m(new Data())
400{
401}
402
403GlobalLock::~GlobalLock()
404{
405 if (m->pOldLoader)
406 xmlSetExternalEntityLoader(m->pOldLoader);
407 delete m;
408 m = NULL;
409}
410
411void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
412{
413 m->pOldLoader = xmlGetExternalEntityLoader();
414 xmlSetExternalEntityLoader(pLoader);
415}
416
417// static
418xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
419 const char *aID,
420 xmlParserCtxt *aCtxt)
421{
422 return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
423}
424
425////////////////////////////////////////////////////////////////////////////////
426//
427// Node class
428//
429////////////////////////////////////////////////////////////////////////////////
430
431struct Node::Data
432{
433 struct compare_const_char
434 {
435 bool operator()(const char* s1, const char* s2) const
436 {
437 return strcmp(s1, s2) < 0;
438 }
439 };
440
441 // attributes, if this is an element; can be empty
442 typedef std::map<const char*, boost::shared_ptr<AttributeNode>, compare_const_char > AttributesMap;
443 AttributesMap attribs;
444
445 // child elements, if this is an element; can be empty
446 typedef std::list< boost::shared_ptr<Node> > InternalNodesList;
447 InternalNodesList children;
448};
449
450Node::Node(EnumType type,
451 Node *pParent,
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(new Data)
462{
463}
464
465Node::~Node()
466{
467 delete m;
468}
469
470/**
471 * Private implementation.
472 * @param elmRoot
473 */
474void Node::buildChildren(const ElementNode &elmRoot) // private
475{
476 // go thru this element's attributes
477 xmlAttr *plibAttr = m_plibNode->properties;
478 while (plibAttr)
479 {
480 const char *pcszKey;
481 boost::shared_ptr<AttributeNode> pNew(new AttributeNode(elmRoot, this, plibAttr, &pcszKey));
482 // store
483 m->attribs[pcszKey] = pNew;
484
485 plibAttr = plibAttr->next;
486 }
487
488 // go thru this element's child elements
489 xmlNodePtr plibNode = m_plibNode->children;
490 while (plibNode)
491 {
492 boost::shared_ptr<Node> pNew;
493
494 if (plibNode->type == XML_ELEMENT_NODE)
495 pNew = boost::shared_ptr<Node>(new ElementNode(&elmRoot, this, plibNode));
496 else if (plibNode->type == XML_TEXT_NODE)
497 pNew = boost::shared_ptr<Node>(new ContentNode(this, plibNode));
498 if (pNew)
499 {
500 // store
501 m->children.push_back(pNew);
502
503 // recurse for this child element to get its own children
504 pNew->buildChildren(elmRoot);
505 }
506
507 plibNode = plibNode->next;
508 }
509}
510
511/**
512 * Returns the name of the node, which is either the element name or
513 * the attribute name. For other node types it probably returns NULL.
514 * @return
515 */
516const char* Node::getName() const
517{
518 return m_pcszName;
519}
520
521/**
522 * Returns the name of the node, which is either the element name or
523 * the attribute name. For other node types it probably returns NULL.
524 * @return
525 */
526const char* Node::getPrefix() const
527{
528 return m_pcszNamespacePrefix;
529}
530
531/**
532 * Returns the XML namespace URI, which is the attribute name. For other node types it probably
533 * returns NULL.
534 * @return
535 */
536const char* Node::getNamespaceURI() const
537{
538 return m_pcszNamespaceHref;
539}
540
541/**
542 * Variant of nameEquals that checks the namespace as well.
543 * @param pcszNamespace
544 * @param pcsz
545 * @return
546 */
547bool Node::nameEquals(const char *pcszNamespace, const char *pcsz) const
548{
549 if (m_pcszName == pcsz)
550 return true;
551 if (m_pcszName == NULL)
552 return false;
553 if (pcsz == NULL)
554 return false;
555 if (strcmp(m_pcszName, pcsz))
556 return false;
557
558 // name matches: then check namespaces as well
559 if (!pcszNamespace)
560 return true;
561 // caller wants namespace:
562 if (!m_pcszNamespacePrefix)
563 // but node has no namespace:
564 return false;
565 return !strcmp(m_pcszNamespacePrefix, pcszNamespace);
566}
567
568/**
569 * Returns the value of a node. If this node is an attribute, returns
570 * the attribute value; if this node is an element, then this returns
571 * the element text content.
572 * @return
573 */
574const char* Node::getValue() const
575{
576 if ( (m_plibAttr)
577 && (m_plibAttr->children)
578 )
579 // libxml hides attribute values in another node created as a
580 // single child of the attribute node, and it's in the content field
581 return (const char*)m_plibAttr->children->content;
582
583 if ( (m_plibNode)
584 && (m_plibNode->children)
585 )
586 return (const char*)m_plibNode->children->content;
587
588 return NULL;
589}
590
591/**
592 * Copies the value of a node into the given integer variable.
593 * Returns TRUE only if a value was found and was actually an
594 * integer of the given type.
595 * @return
596 */
597bool Node::copyValue(int32_t &i) const
598{
599 const char *pcsz;
600 if ( ((pcsz = getValue()))
601 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
602 )
603 return true;
604
605 return false;
606}
607
608/**
609 * Copies the value of a node into the given integer variable.
610 * Returns TRUE only if a value was found and was actually an
611 * integer of the given type.
612 * @return
613 */
614bool Node::copyValue(uint32_t &i) const
615{
616 const char *pcsz;
617 if ( ((pcsz = getValue()))
618 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
619 )
620 return true;
621
622 return false;
623}
624
625/**
626 * Copies the value of a node into the given integer variable.
627 * Returns TRUE only if a value was found and was actually an
628 * integer of the given type.
629 * @return
630 */
631bool Node::copyValue(int64_t &i) const
632{
633 const char *pcsz;
634 if ( ((pcsz = getValue()))
635 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
636 )
637 return true;
638
639 return false;
640}
641
642/**
643 * Copies the value of a node into the given integer variable.
644 * Returns TRUE only if a value was found and was actually an
645 * integer of the given type.
646 * @return
647 */
648bool Node::copyValue(uint64_t &i) const
649{
650 const char *pcsz;
651 if ( ((pcsz = getValue()))
652 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
653 )
654 return true;
655
656 return false;
657}
658
659/**
660 * Returns the line number of the current node in the source XML file.
661 * Useful for error messages.
662 * @return
663 */
664int Node::getLineNumber() const
665{
666 if (m_plibAttr)
667 return m_pParent->m_plibNode->line;
668
669 return m_plibNode->line;
670}
671
672/**
673 * Private element constructor.
674 * @param pelmRoot
675 * @param pParent
676 * @param plibNode
677 */
678ElementNode::ElementNode(const ElementNode *pelmRoot,
679 Node *pParent,
680 xmlNode *plibNode)
681 : Node(IsElement,
682 pParent,
683 plibNode,
684 NULL)
685{
686 if (!(m_pelmRoot = pelmRoot))
687 // NULL passed, then this is the root element
688 m_pelmRoot = this;
689
690 m_pcszName = (const char*)plibNode->name;
691
692 if (plibNode->ns)
693 {
694 m_pcszNamespacePrefix = (const char*)m_plibNode->ns->prefix;
695 m_pcszNamespaceHref = (const char*)m_plibNode->ns->href;
696 }
697}
698
699/**
700 * Builds a list of direct child elements of the current element that
701 * match the given string; if pcszMatch is NULL, all direct child
702 * elements are returned.
703 * @param children out: list of nodes to which children will be appended.
704 * @param pcszMatch in: match string, or NULL to return all children.
705 * @return Number of items appended to the list (0 if none).
706 */
707int ElementNode::getChildElements(ElementNodesList &children,
708 const char *pcszMatch /*= NULL*/)
709 const
710{
711 int i = 0;
712 for (Data::InternalNodesList::iterator it = m->children.begin();
713 it != m->children.end();
714 ++it)
715 {
716 // export this child node if ...
717 Node *p = it->get();
718 if (p->isElement())
719 if ( (!pcszMatch) // the caller wants all nodes or
720 || (!strcmp(pcszMatch, p->getName())) // the element name matches
721 )
722 {
723 children.push_back(static_cast<ElementNode*>(p));
724 ++i;
725 }
726 }
727 return i;
728}
729
730/**
731 * Returns the first child element whose name matches pcszMatch.
732 *
733 * @param pcszNamespace Namespace prefix (e.g. "vbox") or NULL to match any namespace.
734 * @param pcszMatch Element name to match.
735 * @return
736 */
737const ElementNode* ElementNode::findChildElement(const char *pcszNamespace,
738 const char *pcszMatch)
739 const
740{
741 Data::InternalNodesList::const_iterator
742 it,
743 last = m->children.end();
744 for (it = m->children.begin();
745 it != last;
746 ++it)
747 {
748 if ((**it).isElement())
749 {
750 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
751 if (pelm->nameEquals(pcszNamespace, pcszMatch))
752 return pelm;
753 }
754 }
755
756 return NULL;
757}
758
759/**
760 * Returns the first child element whose "id" attribute matches pcszId.
761 * @param pcszId identifier to look for.
762 * @return child element or NULL if not found.
763 */
764const ElementNode* ElementNode::findChildElementFromId(const char *pcszId) const
765{
766 Data::InternalNodesList::const_iterator
767 it,
768 last = m->children.end();
769 for (it = m->children.begin();
770 it != last;
771 ++it)
772 {
773 if ((**it).isElement())
774 {
775 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
776 const AttributeNode *pAttr;
777 if ( ((pAttr = pelm->findAttribute("id")))
778 && (!strcmp(pAttr->getValue(), pcszId))
779 )
780 return pelm;
781 }
782 }
783
784 return NULL;
785}
786
787/**
788 * Looks up the given attribute node in this element's attribute map.
789 *
790 * With respect to namespaces, the internal attributes map stores namespace
791 * prefixes with attribute names only if the attribute uses a non-default
792 * namespace. As a result, the following rules apply:
793 *
794 * -- To find attributes from a non-default namespace, pcszMatch must not
795 * be prefixed with a namespace.
796 *
797 * -- To find attributes from the default namespace (or if the document does
798 * not use namespaces), pcszMatch must be prefixed with the namespace
799 * prefix and a colon.
800 *
801 * For example, if the document uses the "vbox:" namespace by default, you
802 * must omit "vbox:" from pcszMatch to find such attributes, whether they
803 * are specifed in the xml or not.
804 *
805 * @param pcszMatch
806 * @return
807 */
808const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const
809{
810 Data::AttributesMap::const_iterator it;
811
812 it = m->attribs.find(pcszMatch);
813 if (it != m->attribs.end())
814 return it->second.get();
815
816 return NULL;
817}
818
819/**
820 * Convenience method which attempts to find the attribute with the given
821 * name and returns its value as a string.
822 *
823 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
824 * @param ppcsz out: attribute value
825 * @return TRUE if attribute was found and str was thus updated.
826 */
827bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const
828{
829 const Node* pAttr;
830 if ((pAttr = findAttribute(pcszMatch)))
831 {
832 ppcsz = pAttr->getValue();
833 return true;
834 }
835
836 return false;
837}
838
839/**
840 * Convenience method which attempts to find the attribute with the given
841 * name and returns its value as a string.
842 *
843 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
844 * @param str out: attribute value; overwritten only if attribute was found
845 * @return TRUE if attribute was found and str was thus updated.
846 */
847bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString &str) const
848{
849 const Node* pAttr;
850 if ((pAttr = findAttribute(pcszMatch)))
851 {
852 str = pAttr->getValue();
853 return true;
854 }
855
856 return false;
857}
858
859/**
860 * Like getAttributeValue (ministring variant), but makes sure that all backslashes
861 * are converted to forward slashes.
862 * @param pcszMatch
863 * @param str
864 * @return
865 */
866bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString &str) const
867{
868 if (getAttributeValue(pcszMatch, str))
869 {
870 str.findReplace('\\', '/');
871 return true;
872 }
873
874 return false;
875}
876
877/**
878 * Convenience method which attempts to find the attribute with the given
879 * name and returns its value as a signed integer. This calls
880 * RTStrToInt32Ex internally and will only output the integer if that
881 * function returns no error.
882 *
883 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
884 * @param i out: attribute value; overwritten only if attribute was found
885 * @return TRUE if attribute was found and str was thus updated.
886 */
887bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t &i) const
888{
889 const char *pcsz;
890 if ( (getAttributeValue(pcszMatch, pcsz))
891 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 0, &i))
892 )
893 return true;
894
895 return false;
896}
897
898/**
899 * Convenience method which attempts to find the attribute with the given
900 * name and returns its value as an unsigned integer.This calls
901 * RTStrToUInt32Ex internally and will only output the integer if that
902 * function returns no error.
903 *
904 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
905 * @param i out: attribute value; overwritten only if attribute was found
906 * @return TRUE if attribute was found and str was thus updated.
907 */
908bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t &i) const
909{
910 const char *pcsz;
911 if ( (getAttributeValue(pcszMatch, pcsz))
912 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 0, &i))
913 )
914 return true;
915
916 return false;
917}
918
919/**
920 * Convenience method which attempts to find the attribute with the given
921 * name and returns its value as a signed long integer. This calls
922 * RTStrToInt64Ex internally and will only output the integer if that
923 * function returns no error.
924 *
925 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
926 * @param i out: attribute value
927 * @return TRUE if attribute was found and str was thus updated.
928 */
929bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const
930{
931 const char *pcsz;
932 if ( (getAttributeValue(pcszMatch, pcsz))
933 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 0, &i))
934 )
935 return true;
936
937 return false;
938}
939
940/**
941 * Convenience method which attempts to find the attribute with the given
942 * name and returns its value as an unsigned long integer.This calls
943 * RTStrToUInt64Ex internally and will only output the integer if that
944 * function returns no error.
945 *
946 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
947 * @param i out: attribute value; overwritten only if attribute was found
948 * @return TRUE if attribute was found and str was thus updated.
949 */
950bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const
951{
952 const char *pcsz;
953 if ( (getAttributeValue(pcszMatch, pcsz))
954 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 0, &i))
955 )
956 return true;
957
958 return false;
959}
960
961/**
962 * Convenience method which attempts to find the attribute with the given
963 * name and returns its value as a boolean. This accepts "true", "false",
964 * "yes", "no", "1" or "0" as valid values.
965 *
966 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
967 * @param f out: attribute value; overwritten only if attribute was found
968 * @return TRUE if attribute was found and str was thus updated.
969 */
970bool ElementNode::getAttributeValue(const char *pcszMatch, bool &f) const
971{
972 const char *pcsz;
973 if (getAttributeValue(pcszMatch, pcsz))
974 {
975 if ( (!strcmp(pcsz, "true"))
976 || (!strcmp(pcsz, "yes"))
977 || (!strcmp(pcsz, "1"))
978 )
979 {
980 f = true;
981 return true;
982 }
983 if ( (!strcmp(pcsz, "false"))
984 || (!strcmp(pcsz, "no"))
985 || (!strcmp(pcsz, "0"))
986 )
987 {
988 f = false;
989 return true;
990 }
991 }
992
993 return false;
994}
995
996/**
997 * Creates a new child element node and appends it to the list
998 * of children in "this".
999 *
1000 * @param pcszElementName
1001 * @return
1002 */
1003ElementNode* ElementNode::createChild(const char *pcszElementName)
1004{
1005 // we must be an element, not an attribute
1006 if (!m_plibNode)
1007 throw ENodeIsNotElement(RT_SRC_POS);
1008
1009 // libxml side: create new node
1010 xmlNode *plibNode;
1011 if (!(plibNode = xmlNewNode(NULL, // namespace
1012 (const xmlChar*)pcszElementName)))
1013 throw std::bad_alloc();
1014 xmlAddChild(m_plibNode, plibNode);
1015
1016 // now wrap this in C++
1017 ElementNode *p = new ElementNode(m_pelmRoot, this, plibNode);
1018 boost::shared_ptr<ElementNode> pNew(p);
1019 m->children.push_back(pNew);
1020
1021 return p;
1022}
1023
1024
1025/**
1026 * Creates a content node and appends it to the list of children
1027 * in "this".
1028 *
1029 * @param pcszContent
1030 * @return
1031 */
1032ContentNode* ElementNode::addContent(const char *pcszContent)
1033{
1034 // libxml side: create new node
1035 xmlNode *plibNode;
1036 if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
1037 throw std::bad_alloc();
1038 xmlAddChild(m_plibNode, plibNode);
1039
1040 // now wrap this in C++
1041 ContentNode *p = new ContentNode(this, plibNode);
1042 boost::shared_ptr<ContentNode> pNew(p);
1043 m->children.push_back(pNew);
1044
1045 return p;
1046}
1047
1048/**
1049 * Sets the given attribute; overloaded version for const char *.
1050 *
1051 * If an attribute with the given name exists, it is overwritten,
1052 * otherwise a new attribute is created. Returns the attribute node
1053 * that was either created or changed.
1054 *
1055 * @param pcszName
1056 * @param pcszValue
1057 * @return
1058 */
1059AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
1060{
1061 AttributeNode *pattrReturn;
1062 Data::AttributesMap::const_iterator it;
1063
1064 it = m->attribs.find(pcszName);
1065 if (it == m->attribs.end())
1066 {
1067 // libxml side: xmlNewProp creates an attribute
1068 xmlAttr *plibAttr = xmlNewProp(m_plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
1069
1070 // C++ side: create an attribute node around it
1071 const char *pcszKey;
1072 boost::shared_ptr<AttributeNode> pNew(new AttributeNode(*m_pelmRoot, this, plibAttr, &pcszKey));
1073 // store
1074 m->attribs[pcszKey] = pNew;
1075 pattrReturn = pNew.get();
1076 }
1077 else
1078 {
1079 // overwrite existing libxml attribute node
1080 xmlAttrPtr plibAttr = xmlSetProp(m_plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
1081
1082 // and fix our existing C++ side around it
1083 boost::shared_ptr<AttributeNode> pattr = it->second;
1084 pattr->m_plibAttr = plibAttr; // in case the xmlAttrPtr is different, I'm not sure
1085
1086 pattrReturn = pattr.get();
1087 }
1088
1089 return pattrReturn;
1090
1091}
1092
1093/**
1094 * Like setAttribute (ministring variant), but replaces all backslashes with forward slashes
1095 * before calling that one.
1096 * @param pcszName
1097 * @param strValue
1098 * @return
1099 */
1100AttributeNode* ElementNode::setAttributePath(const char *pcszName, const RTCString &strValue)
1101{
1102 RTCString strTemp(strValue);
1103 strTemp.findReplace('\\', '/');
1104 return setAttribute(pcszName, strTemp.c_str());
1105}
1106
1107/**
1108 * Sets the given attribute; overloaded version for int32_t.
1109 *
1110 * If an attribute with the given name exists, it is overwritten,
1111 * otherwise a new attribute is created. Returns the attribute node
1112 * that was either created or changed.
1113 *
1114 * @param pcszName
1115 * @param i
1116 * @return
1117 */
1118AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
1119{
1120 char szValue[12]; // negative sign + 10 digits + \0
1121 RTStrPrintf(szValue, sizeof(szValue), "%RI32", i);
1122 AttributeNode *p = setAttribute(pcszName, szValue);
1123 return p;
1124}
1125
1126/**
1127 * Sets the given attribute; overloaded version for uint32_t.
1128 *
1129 * If an attribute with the given name exists, it is overwritten,
1130 * otherwise a new attribute is created. Returns the attribute node
1131 * that was either created or changed.
1132 *
1133 * @param pcszName
1134 * @param u
1135 * @return
1136 */
1137AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t u)
1138{
1139 char szValue[11]; // 10 digits + \0
1140 RTStrPrintf(szValue, sizeof(szValue), "%RU32", u);
1141 AttributeNode *p = setAttribute(pcszName, szValue);
1142 return p;
1143}
1144
1145/**
1146 * Sets the given attribute; overloaded version for int64_t.
1147 *
1148 * If an attribute with the given name exists, it is overwritten,
1149 * otherwise a new attribute is created. Returns the attribute node
1150 * that was either created or changed.
1151 *
1152 * @param pcszName
1153 * @param i
1154 * @return
1155 */
1156AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
1157{
1158 char szValue[21]; // negative sign + 19 digits + \0
1159 RTStrPrintf(szValue, sizeof(szValue), "%RI64", i);
1160 AttributeNode *p = setAttribute(pcszName, szValue);
1161 return p;
1162}
1163
1164/**
1165 * Sets the given attribute; overloaded version for uint64_t.
1166 *
1167 * If an attribute with the given name exists, it is overwritten,
1168 * otherwise a new attribute is created. Returns the attribute node
1169 * that was either created or changed.
1170 *
1171 * @param pcszName
1172 * @param u
1173 * @return
1174 */
1175AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t u)
1176{
1177 char szValue[21]; // 20 digits + \0
1178 RTStrPrintf(szValue, sizeof(szValue), "%RU64", u);
1179 AttributeNode *p = setAttribute(pcszName, szValue);
1180 return p;
1181}
1182
1183/**
1184 * Sets the given attribute to the given uint32_t, outputs a hexadecimal string.
1185 *
1186 * If an attribute with the given name exists, it is overwritten,
1187 * otherwise a new attribute is created. Returns the attribute node
1188 * that was either created or changed.
1189 *
1190 * @param pcszName
1191 * @param u
1192 * @return
1193 */
1194AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t u)
1195{
1196 char szValue[11]; // "0x" + 8 digits + \0
1197 RTStrPrintf(szValue, sizeof(szValue), "0x%RX32", u);
1198 AttributeNode *p = setAttribute(pcszName, szValue);
1199 return p;
1200}
1201
1202/**
1203 * Sets the given attribute; overloaded version for bool.
1204 *
1205 * If an attribute with the given name exists, it is overwritten,
1206 * otherwise a new attribute is created. Returns the attribute node
1207 * that was either created or changed.
1208 *
1209 * @param pcszName
1210 * @param i
1211 * @return
1212 */
1213AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
1214{
1215 return setAttribute(pcszName, (f) ? "true" : "false");
1216}
1217
1218/**
1219 * Private constructor for a new attribute node. This one is special:
1220 * in ppcszKey, it returns a pointer to a string buffer that should be
1221 * used to index the attribute correctly with namespaces.
1222 *
1223 * @param pParent
1224 * @param elmRoot
1225 * @param plibAttr
1226 * @param ppcszKey
1227 */
1228AttributeNode::AttributeNode(const ElementNode &elmRoot,
1229 Node *pParent,
1230 xmlAttr *plibAttr,
1231 const char **ppcszKey)
1232 : Node(IsAttribute,
1233 pParent,
1234 NULL,
1235 plibAttr)
1236{
1237 m_pcszName = (const char*)plibAttr->name;
1238
1239 *ppcszKey = m_pcszName;
1240
1241 if ( plibAttr->ns
1242 && plibAttr->ns->prefix
1243 )
1244 {
1245 m_pcszNamespacePrefix = (const char*)plibAttr->ns->prefix;
1246 m_pcszNamespaceHref = (const char*)plibAttr->ns->href;
1247
1248 if ( !elmRoot.m_pcszNamespaceHref
1249 || (strcmp(m_pcszNamespaceHref, elmRoot.m_pcszNamespaceHref))
1250 )
1251 {
1252 // not default namespace:
1253 m_strKey = m_pcszNamespacePrefix;
1254 m_strKey.append(':');
1255 m_strKey.append(m_pcszName);
1256
1257 *ppcszKey = m_strKey.c_str();
1258 }
1259 }
1260}
1261
1262ContentNode::ContentNode(Node *pParent, xmlNode *plibNode)
1263 : Node(IsContent,
1264 pParent,
1265 plibNode,
1266 NULL)
1267{
1268}
1269
1270/*
1271 * NodesLoop
1272 *
1273 */
1274
1275struct NodesLoop::Data
1276{
1277 ElementNodesList listElements;
1278 ElementNodesList::const_iterator it;
1279};
1280
1281NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
1282{
1283 m = new Data;
1284 node.getChildElements(m->listElements, pcszMatch);
1285 m->it = m->listElements.begin();
1286}
1287
1288NodesLoop::~NodesLoop()
1289{
1290 delete m;
1291}
1292
1293
1294/**
1295 * Handy convenience helper for looping over all child elements. Create an
1296 * instance of NodesLoop on the stack and call this method until it returns
1297 * NULL, like this:
1298 * <code>
1299 * xml::ElementNode node; // should point to an element
1300 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
1301 * const xml::ElementNode *pChild = NULL;
1302 * while (pChild = loop.forAllNodes())
1303 * ...;
1304 * </code>
1305 * @return
1306 */
1307const ElementNode* NodesLoop::forAllNodes() const
1308{
1309 const ElementNode *pNode = NULL;
1310
1311 if (m->it != m->listElements.end())
1312 {
1313 pNode = *(m->it);
1314 ++(m->it);
1315 }
1316
1317 return pNode;
1318}
1319
1320////////////////////////////////////////////////////////////////////////////////
1321//
1322// Document class
1323//
1324////////////////////////////////////////////////////////////////////////////////
1325
1326struct Document::Data
1327{
1328 xmlDocPtr plibDocument;
1329 ElementNode *pRootElement;
1330 ElementNode *pComment;
1331
1332 Data()
1333 {
1334 plibDocument = NULL;
1335 pRootElement = NULL;
1336 pComment = NULL;
1337 }
1338
1339 ~Data()
1340 {
1341 reset();
1342 }
1343
1344 void reset()
1345 {
1346 if (plibDocument)
1347 {
1348 xmlFreeDoc(plibDocument);
1349 plibDocument = NULL;
1350 }
1351 if (pRootElement)
1352 {
1353 delete pRootElement;
1354 pRootElement = NULL;
1355 }
1356 if (pComment)
1357 {
1358 delete pComment;
1359 pComment = NULL;
1360 }
1361 }
1362
1363 void copyFrom(const Document::Data *p)
1364 {
1365 if (p->plibDocument)
1366 {
1367 plibDocument = xmlCopyDoc(p->plibDocument,
1368 1); // recursive == copy all
1369 }
1370 }
1371};
1372
1373Document::Document()
1374 : m(new Data)
1375{
1376}
1377
1378Document::Document(const Document &x)
1379 : m(new Data)
1380{
1381 m->copyFrom(x.m);
1382}
1383
1384Document& Document::operator=(const Document &x)
1385{
1386 m->reset();
1387 m->copyFrom(x.m);
1388 return *this;
1389}
1390
1391Document::~Document()
1392{
1393 delete m;
1394}
1395
1396/**
1397 * private method to refresh all internal structures after the internal pDocument
1398 * has changed. Called from XmlFileParser::read(). m->reset() must have been
1399 * called before to make sure all members except the internal pDocument are clean.
1400 */
1401void Document::refreshInternals() // private
1402{
1403 m->pRootElement = new ElementNode(NULL, NULL, xmlDocGetRootElement(m->plibDocument));
1404
1405 m->pRootElement->buildChildren(*m->pRootElement);
1406}
1407
1408/**
1409 * Returns the root element of the document, or NULL if the document is empty.
1410 * Const variant.
1411 * @return
1412 */
1413const ElementNode* Document::getRootElement() const
1414{
1415 return m->pRootElement;
1416}
1417
1418/**
1419 * Returns the root element of the document, or NULL if the document is empty.
1420 * Non-const variant.
1421 * @return
1422 */
1423ElementNode* Document::getRootElement()
1424{
1425 return m->pRootElement;
1426}
1427
1428/**
1429 * Creates a new element node and sets it as the root element. This will
1430 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
1431 */
1432ElementNode* Document::createRootElement(const char *pcszRootElementName,
1433 const char *pcszComment /* = NULL */)
1434{
1435 if (m->pRootElement || m->plibDocument)
1436 throw EDocumentNotEmpty(RT_SRC_POS);
1437
1438 // libxml side: create document, create root node
1439 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
1440 xmlNode *plibRootNode;
1441 if (!(plibRootNode = xmlNewNode(NULL, // namespace
1442 (const xmlChar*)pcszRootElementName)))
1443 throw std::bad_alloc();
1444 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1445 // now wrap this in C++
1446 m->pRootElement = new ElementNode(NULL, NULL, plibRootNode);
1447
1448 // add document global comment if specified
1449 if (pcszComment != NULL)
1450 {
1451 xmlNode *pComment;
1452 if (!(pComment = xmlNewDocComment(m->plibDocument,
1453 (const xmlChar *)pcszComment)))
1454 throw std::bad_alloc();
1455 xmlAddPrevSibling(plibRootNode, pComment);
1456 // now wrap this in C++
1457 m->pComment = new ElementNode(NULL, NULL, pComment);
1458 }
1459
1460 return m->pRootElement;
1461}
1462
1463////////////////////////////////////////////////////////////////////////////////
1464//
1465// XmlParserBase class
1466//
1467////////////////////////////////////////////////////////////////////////////////
1468
1469XmlParserBase::XmlParserBase()
1470{
1471 m_ctxt = xmlNewParserCtxt();
1472 if (m_ctxt == NULL)
1473 throw std::bad_alloc();
1474}
1475
1476XmlParserBase::~XmlParserBase()
1477{
1478 xmlFreeParserCtxt (m_ctxt);
1479 m_ctxt = NULL;
1480}
1481
1482////////////////////////////////////////////////////////////////////////////////
1483//
1484// XmlMemParser class
1485//
1486////////////////////////////////////////////////////////////////////////////////
1487
1488XmlMemParser::XmlMemParser()
1489 : XmlParserBase()
1490{
1491}
1492
1493XmlMemParser::~XmlMemParser()
1494{
1495}
1496
1497/**
1498 * Parse the given buffer and fills the given Document object with its contents.
1499 * Throws XmlError on parsing errors.
1500 *
1501 * The document that is passed in will be reset before being filled if not empty.
1502 *
1503 * @param pvBuf in: memory buffer to parse.
1504 * @param cbSize in: size of the memory buffer.
1505 * @param strFilename in: name fo file to parse.
1506 * @param doc out: document to be reset and filled with data according to file contents.
1507 */
1508void XmlMemParser::read(const void* pvBuf, size_t cbSize,
1509 const RTCString &strFilename,
1510 Document &doc)
1511{
1512 GlobalLock lock;
1513// global.setExternalEntityLoader(ExternalEntityLoader);
1514
1515 const char *pcszFilename = strFilename.c_str();
1516
1517 doc.m->reset();
1518 if (!(doc.m->plibDocument = xmlCtxtReadMemory(m_ctxt,
1519 (const char*)pvBuf,
1520 (int)cbSize,
1521 pcszFilename,
1522 NULL, // encoding = auto
1523 XML_PARSE_NOBLANKS | XML_PARSE_NONET)))
1524 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1525
1526 doc.refreshInternals();
1527}
1528
1529////////////////////////////////////////////////////////////////////////////////
1530//
1531// XmlMemWriter class
1532//
1533////////////////////////////////////////////////////////////////////////////////
1534
1535XmlMemWriter::XmlMemWriter()
1536 : m_pBuf(0)
1537{
1538}
1539
1540XmlMemWriter::~XmlMemWriter()
1541{
1542 if (m_pBuf)
1543 xmlFree(m_pBuf);
1544}
1545
1546void XmlMemWriter::write(const Document &doc, void **ppvBuf, size_t *pcbSize)
1547{
1548 if (m_pBuf)
1549 {
1550 xmlFree(m_pBuf);
1551 m_pBuf = 0;
1552 }
1553 int size;
1554 xmlDocDumpFormatMemory(doc.m->plibDocument, (xmlChar**)&m_pBuf, &size, 1);
1555 *ppvBuf = m_pBuf;
1556 *pcbSize = size;
1557}
1558
1559////////////////////////////////////////////////////////////////////////////////
1560//
1561// XmlFileParser class
1562//
1563////////////////////////////////////////////////////////////////////////////////
1564
1565struct XmlFileParser::Data
1566{
1567 RTCString strXmlFilename;
1568
1569 Data()
1570 {
1571 }
1572
1573 ~Data()
1574 {
1575 }
1576};
1577
1578XmlFileParser::XmlFileParser()
1579 : XmlParserBase(),
1580 m(new Data())
1581{
1582}
1583
1584XmlFileParser::~XmlFileParser()
1585{
1586 delete m;
1587 m = NULL;
1588}
1589
1590struct IOContext
1591{
1592 File file;
1593 RTCString error;
1594
1595 IOContext(const char *pcszFilename, File::Mode mode, bool fFlush = false)
1596 : file(mode, pcszFilename, fFlush)
1597 {
1598 }
1599
1600 void setError(const RTCError &x)
1601 {
1602 error = x.what();
1603 }
1604
1605 void setError(const std::exception &x)
1606 {
1607 error = x.what();
1608 }
1609};
1610
1611struct ReadContext : IOContext
1612{
1613 ReadContext(const char *pcszFilename)
1614 : IOContext(pcszFilename, File::Mode_Read)
1615 {
1616 }
1617};
1618
1619struct WriteContext : IOContext
1620{
1621 WriteContext(const char *pcszFilename, bool fFlush)
1622 : IOContext(pcszFilename, File::Mode_Overwrite, fFlush)
1623 {
1624 }
1625};
1626
1627/**
1628 * Reads the given file and fills the given Document object with its contents.
1629 * Throws XmlError on parsing errors.
1630 *
1631 * The document that is passed in will be reset before being filled if not empty.
1632 *
1633 * @param strFilename in: name fo file to parse.
1634 * @param doc out: document to be reset and filled with data according to file contents.
1635 */
1636void XmlFileParser::read(const RTCString &strFilename,
1637 Document &doc)
1638{
1639 GlobalLock lock;
1640// global.setExternalEntityLoader(ExternalEntityLoader);
1641
1642 m->strXmlFilename = strFilename;
1643 const char *pcszFilename = strFilename.c_str();
1644
1645 ReadContext context(pcszFilename);
1646 doc.m->reset();
1647 if (!(doc.m->plibDocument = xmlCtxtReadIO(m_ctxt,
1648 ReadCallback,
1649 CloseCallback,
1650 &context,
1651 pcszFilename,
1652 NULL, // encoding = auto
1653 XML_PARSE_NOBLANKS | XML_PARSE_NONET)))
1654 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1655
1656 doc.refreshInternals();
1657}
1658
1659// static
1660int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1661{
1662 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1663
1664 /* To prevent throwing exceptions while inside libxml2 code, we catch
1665 * them and forward to our level using a couple of variables. */
1666
1667 try
1668 {
1669 return pContext->file.read(aBuf, aLen);
1670 }
1671 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1672 catch (const RTCError &err) { pContext->setError(err); }
1673 catch (const std::exception &err) { pContext->setError(err); }
1674 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1675
1676 return -1 /* failure */;
1677}
1678
1679int XmlFileParser::CloseCallback(void *aCtxt)
1680{
1681 /// @todo to be written
1682 NOREF(aCtxt);
1683
1684 return -1;
1685}
1686
1687////////////////////////////////////////////////////////////////////////////////
1688//
1689// XmlFileWriter class
1690//
1691////////////////////////////////////////////////////////////////////////////////
1692
1693struct XmlFileWriter::Data
1694{
1695 Document *pDoc;
1696};
1697
1698XmlFileWriter::XmlFileWriter(Document &doc)
1699{
1700 m = new Data();
1701 m->pDoc = &doc;
1702}
1703
1704XmlFileWriter::~XmlFileWriter()
1705{
1706 delete m;
1707}
1708
1709void XmlFileWriter::writeInternal(const char *pcszFilename, bool fSafe)
1710{
1711 WriteContext context(pcszFilename, fSafe);
1712
1713 GlobalLock lock;
1714
1715 /* serialize to the stream */
1716 xmlIndentTreeOutput = 1;
1717 xmlTreeIndentString = " ";
1718 xmlSaveNoEmptyTags = 0;
1719
1720 xmlSaveCtxtPtr saveCtxt;
1721 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1722 CloseCallback,
1723 &context,
1724 NULL,
1725 XML_SAVE_FORMAT)))
1726 throw xml::LogicError(RT_SRC_POS);
1727
1728 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1729 if (rc == -1)
1730 {
1731 /* look if there was a forwarded exception from the lower level */
1732// if (m->trappedErr.get() != NULL)
1733// m->trappedErr->rethrow();
1734
1735 /* there must be an exception from the Output implementation,
1736 * otherwise the save operation must always succeed. */
1737 throw xml::LogicError(RT_SRC_POS);
1738 }
1739
1740 xmlSaveClose(saveCtxt);
1741}
1742
1743void XmlFileWriter::write(const char *pcszFilename, bool fSafe)
1744{
1745 if (!fSafe)
1746 writeInternal(pcszFilename, fSafe);
1747 else
1748 {
1749 /* Empty string and directory spec must be avoid. */
1750 if (RTPathFilename(pcszFilename) == NULL)
1751 throw xml::LogicError(RT_SRC_POS);
1752
1753 /* Construct both filenames first to ease error handling. */
1754 char szTmpFilename[RTPATH_MAX];
1755 int rc = RTStrCopy(szTmpFilename, sizeof(szTmpFilename) - strlen(s_pszTmpSuff), pcszFilename);
1756 if (RT_FAILURE(rc))
1757 throw EIPRTFailure(rc, "RTStrCopy");
1758 strcat(szTmpFilename, s_pszTmpSuff);
1759
1760 char szPrevFilename[RTPATH_MAX];
1761 rc = RTStrCopy(szPrevFilename, sizeof(szPrevFilename) - strlen(s_pszPrevSuff), pcszFilename);
1762 if (RT_FAILURE(rc))
1763 throw EIPRTFailure(rc, "RTStrCopy");
1764 strcat(szPrevFilename, s_pszPrevSuff);
1765
1766 /* Write the XML document to the temporary file. */
1767 writeInternal(szTmpFilename, fSafe);
1768
1769 /* Make a backup of any existing file (ignore failure). */
1770 uint64_t cbPrevFile;
1771 rc = RTFileQuerySize(pcszFilename, &cbPrevFile);
1772 if (RT_SUCCESS(rc) && cbPrevFile >= 16)
1773 RTFileRename(pcszFilename, szPrevFilename, RTPATHRENAME_FLAGS_REPLACE);
1774
1775 /* Commit the temporary file. Just leave the tmp file behind on failure. */
1776 rc = RTFileRename(szTmpFilename, pcszFilename, RTPATHRENAME_FLAGS_REPLACE);
1777 if (RT_FAILURE(rc))
1778 throw EIPRTFailure(rc, "Failed to replace '%s' with '%s'", pcszFilename, szTmpFilename);
1779
1780 /* Flush the directory changes (required on linux at least). */
1781 RTPathStripFilename(szTmpFilename);
1782 rc = RTDirFlush(szTmpFilename);
1783 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
1784 }
1785}
1786
1787int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1788{
1789 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1790
1791 /* To prevent throwing exceptions while inside libxml2 code, we catch
1792 * them and forward to our level using a couple of variables. */
1793 try
1794 {
1795 return pContext->file.write(aBuf, aLen);
1796 }
1797 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1798 catch (const RTCError &err) { pContext->setError(err); }
1799 catch (const std::exception &err) { pContext->setError(err); }
1800 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1801
1802 return -1 /* failure */;
1803}
1804
1805int XmlFileWriter::CloseCallback(void *aCtxt)
1806{
1807 /// @todo to be written
1808 NOREF(aCtxt);
1809
1810 return -1;
1811}
1812
1813/*static*/ const char * const XmlFileWriter::s_pszTmpSuff = "-tmp";
1814/*static*/ const char * const XmlFileWriter::s_pszPrevSuff = "-prev";
1815
1816
1817} // end namespace xml
1818
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