VirtualBox

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

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

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.1 KB
Line 
1/* $Id: xml.cpp 44528 2013-02-04 14:27:54Z 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 * Variant of nameEquals that checks the namespace as well.
533 * @param pcszNamespace
534 * @param pcsz
535 * @return
536 */
537bool Node::nameEquals(const char *pcszNamespace, const char *pcsz) const
538{
539 if (m_pcszName == pcsz)
540 return true;
541 if (m_pcszName == NULL)
542 return false;
543 if (pcsz == NULL)
544 return false;
545 if (strcmp(m_pcszName, pcsz))
546 return false;
547
548 // name matches: then check namespaces as well
549 if (!pcszNamespace)
550 return true;
551 // caller wants namespace:
552 if (!m_pcszNamespacePrefix)
553 // but node has no namespace:
554 return false;
555 return !strcmp(m_pcszNamespacePrefix, pcszNamespace);
556}
557
558/**
559 * Returns the value of a node. If this node is an attribute, returns
560 * the attribute value; if this node is an element, then this returns
561 * the element text content.
562 * @return
563 */
564const char* Node::getValue() const
565{
566 if ( (m_plibAttr)
567 && (m_plibAttr->children)
568 )
569 // libxml hides attribute values in another node created as a
570 // single child of the attribute node, and it's in the content field
571 return (const char*)m_plibAttr->children->content;
572
573 if ( (m_plibNode)
574 && (m_plibNode->children)
575 )
576 return (const char*)m_plibNode->children->content;
577
578 return NULL;
579}
580
581/**
582 * Copies the value of a node into the given integer variable.
583 * Returns TRUE only if a value was found and was actually an
584 * integer of the given type.
585 * @return
586 */
587bool Node::copyValue(int32_t &i) const
588{
589 const char *pcsz;
590 if ( ((pcsz = getValue()))
591 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
592 )
593 return true;
594
595 return false;
596}
597
598/**
599 * Copies the value of a node into the given integer variable.
600 * Returns TRUE only if a value was found and was actually an
601 * integer of the given type.
602 * @return
603 */
604bool Node::copyValue(uint32_t &i) const
605{
606 const char *pcsz;
607 if ( ((pcsz = getValue()))
608 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
609 )
610 return true;
611
612 return false;
613}
614
615/**
616 * Copies the value of a node into the given integer variable.
617 * Returns TRUE only if a value was found and was actually an
618 * integer of the given type.
619 * @return
620 */
621bool Node::copyValue(int64_t &i) const
622{
623 const char *pcsz;
624 if ( ((pcsz = getValue()))
625 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
626 )
627 return true;
628
629 return false;
630}
631
632/**
633 * Copies the value of a node into the given integer variable.
634 * Returns TRUE only if a value was found and was actually an
635 * integer of the given type.
636 * @return
637 */
638bool Node::copyValue(uint64_t &i) const
639{
640 const char *pcsz;
641 if ( ((pcsz = getValue()))
642 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
643 )
644 return true;
645
646 return false;
647}
648
649/**
650 * Returns the line number of the current node in the source XML file.
651 * Useful for error messages.
652 * @return
653 */
654int Node::getLineNumber() const
655{
656 if (m_plibAttr)
657 return m_pParent->m_plibNode->line;
658
659 return m_plibNode->line;
660}
661
662/**
663 * Private element constructor.
664 * @param pelmRoot
665 * @param pParent
666 * @param plibNode
667 */
668ElementNode::ElementNode(const ElementNode *pelmRoot,
669 Node *pParent,
670 xmlNode *plibNode)
671 : Node(IsElement,
672 pParent,
673 plibNode,
674 NULL)
675{
676 if (!(m_pelmRoot = pelmRoot))
677 // NULL passed, then this is the root element
678 m_pelmRoot = this;
679
680 m_pcszName = (const char*)plibNode->name;
681
682 if (plibNode->ns)
683 {
684 m_pcszNamespacePrefix = (const char*)m_plibNode->ns->prefix;
685 m_pcszNamespaceHref = (const char*)m_plibNode->ns->href;
686 }
687}
688
689/**
690 * Builds a list of direct child elements of the current element that
691 * match the given string; if pcszMatch is NULL, all direct child
692 * elements are returned.
693 * @param children out: list of nodes to which children will be appended.
694 * @param pcszMatch in: match string, or NULL to return all children.
695 * @return Number of items appended to the list (0 if none).
696 */
697int ElementNode::getChildElements(ElementNodesList &children,
698 const char *pcszMatch /*= NULL*/)
699 const
700{
701 int i = 0;
702 for (Data::InternalNodesList::iterator it = m->children.begin();
703 it != m->children.end();
704 ++it)
705 {
706 // export this child node if ...
707 Node *p = it->get();
708 if (p->isElement())
709 if ( (!pcszMatch) // the caller wants all nodes or
710 || (!strcmp(pcszMatch, p->getName())) // the element name matches
711 )
712 {
713 children.push_back(static_cast<ElementNode*>(p));
714 ++i;
715 }
716 }
717 return i;
718}
719
720/**
721 * Returns the first child element whose name matches pcszMatch.
722 *
723 * @param pcszNamespace Namespace prefix (e.g. "vbox") or NULL to match any namespace.
724 * @param pcszMatch Element name to match.
725 * @return
726 */
727const ElementNode* ElementNode::findChildElement(const char *pcszNamespace,
728 const char *pcszMatch)
729 const
730{
731 Data::InternalNodesList::const_iterator
732 it,
733 last = m->children.end();
734 for (it = m->children.begin();
735 it != last;
736 ++it)
737 {
738 if ((**it).isElement())
739 {
740 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
741 if (pelm->nameEquals(pcszNamespace, pcszMatch))
742 return pelm;
743 }
744 }
745
746 return NULL;
747}
748
749/**
750 * Returns the first child element whose "id" attribute matches pcszId.
751 * @param pcszId identifier to look for.
752 * @return child element or NULL if not found.
753 */
754const ElementNode* ElementNode::findChildElementFromId(const char *pcszId) const
755{
756 Data::InternalNodesList::const_iterator
757 it,
758 last = m->children.end();
759 for (it = m->children.begin();
760 it != last;
761 ++it)
762 {
763 if ((**it).isElement())
764 {
765 ElementNode *pelm = static_cast<ElementNode*>((*it).get());
766 const AttributeNode *pAttr;
767 if ( ((pAttr = pelm->findAttribute("id")))
768 && (!strcmp(pAttr->getValue(), pcszId))
769 )
770 return pelm;
771 }
772 }
773
774 return NULL;
775}
776
777/**
778 * Looks up the given attribute node in this element's attribute map.
779 *
780 * With respect to namespaces, the internal attributes map stores namespace
781 * prefixes with attribute names only if the attribute uses a non-default
782 * namespace. As a result, the following rules apply:
783 *
784 * -- To find attributes from a non-default namespace, pcszMatch must not
785 * be prefixed with a namespace.
786 *
787 * -- To find attributes from the default namespace (or if the document does
788 * not use namespaces), pcszMatch must be prefixed with the namespace
789 * prefix and a colon.
790 *
791 * For example, if the document uses the "vbox:" namespace by default, you
792 * must omit "vbox:" from pcszMatch to find such attributes, whether they
793 * are specifed in the xml or not.
794 *
795 * @param pcszMatch
796 * @return
797 */
798const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const
799{
800 Data::AttributesMap::const_iterator it;
801
802 it = m->attribs.find(pcszMatch);
803 if (it != m->attribs.end())
804 return it->second.get();
805
806 return NULL;
807}
808
809/**
810 * Convenience method which attempts to find the attribute with the given
811 * name and returns its value as a string.
812 *
813 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
814 * @param ppcsz out: attribute value
815 * @return TRUE if attribute was found and str was thus updated.
816 */
817bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const
818{
819 const Node* pAttr;
820 if ((pAttr = findAttribute(pcszMatch)))
821 {
822 ppcsz = pAttr->getValue();
823 return true;
824 }
825
826 return false;
827}
828
829/**
830 * Convenience method which attempts to find the attribute with the given
831 * name and returns its value as a string.
832 *
833 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
834 * @param str out: attribute value; overwritten only if attribute was found
835 * @return TRUE if attribute was found and str was thus updated.
836 */
837bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString &str) const
838{
839 const Node* pAttr;
840 if ((pAttr = findAttribute(pcszMatch)))
841 {
842 str = pAttr->getValue();
843 return true;
844 }
845
846 return false;
847}
848
849/**
850 * Like getAttributeValue (ministring variant), but makes sure that all backslashes
851 * are converted to forward slashes.
852 * @param pcszMatch
853 * @param str
854 * @return
855 */
856bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString &str) const
857{
858 if (getAttributeValue(pcszMatch, str))
859 {
860 str.findReplace('\\', '/');
861 return true;
862 }
863
864 return false;
865}
866
867/**
868 * Convenience method which attempts to find the attribute with the given
869 * name and returns its value as a signed integer. This calls
870 * RTStrToInt32Ex internally and will only output the integer if that
871 * function returns no error.
872 *
873 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
874 * @param i out: attribute value; overwritten only if attribute was found
875 * @return TRUE if attribute was found and str was thus updated.
876 */
877bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t &i) const
878{
879 const char *pcsz;
880 if ( (getAttributeValue(pcszMatch, pcsz))
881 && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 0, &i))
882 )
883 return true;
884
885 return false;
886}
887
888/**
889 * Convenience method which attempts to find the attribute with the given
890 * name and returns its value as an unsigned integer.This calls
891 * RTStrToUInt32Ex internally and will only output the integer if that
892 * function returns no error.
893 *
894 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
895 * @param i out: attribute value; overwritten only if attribute was found
896 * @return TRUE if attribute was found and str was thus updated.
897 */
898bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t &i) const
899{
900 const char *pcsz;
901 if ( (getAttributeValue(pcszMatch, pcsz))
902 && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 0, &i))
903 )
904 return true;
905
906 return false;
907}
908
909/**
910 * Convenience method which attempts to find the attribute with the given
911 * name and returns its value as a signed long integer. This calls
912 * RTStrToInt64Ex internally and will only output the integer if that
913 * function returns no error.
914 *
915 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
916 * @param i out: attribute value
917 * @return TRUE if attribute was found and str was thus updated.
918 */
919bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const
920{
921 const char *pcsz;
922 if ( (getAttributeValue(pcszMatch, pcsz))
923 && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 0, &i))
924 )
925 return true;
926
927 return false;
928}
929
930/**
931 * Convenience method which attempts to find the attribute with the given
932 * name and returns its value as an unsigned long integer.This calls
933 * RTStrToUInt64Ex internally and will only output the integer if that
934 * function returns no error.
935 *
936 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
937 * @param i out: attribute value; overwritten only if attribute was found
938 * @return TRUE if attribute was found and str was thus updated.
939 */
940bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const
941{
942 const char *pcsz;
943 if ( (getAttributeValue(pcszMatch, pcsz))
944 && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 0, &i))
945 )
946 return true;
947
948 return false;
949}
950
951/**
952 * Convenience method which attempts to find the attribute with the given
953 * name and returns its value as a boolean. This accepts "true", "false",
954 * "yes", "no", "1" or "0" as valid values.
955 *
956 * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks)
957 * @param f out: attribute value; overwritten only if attribute was found
958 * @return TRUE if attribute was found and str was thus updated.
959 */
960bool ElementNode::getAttributeValue(const char *pcszMatch, bool &f) const
961{
962 const char *pcsz;
963 if (getAttributeValue(pcszMatch, pcsz))
964 {
965 if ( (!strcmp(pcsz, "true"))
966 || (!strcmp(pcsz, "yes"))
967 || (!strcmp(pcsz, "1"))
968 )
969 {
970 f = true;
971 return true;
972 }
973 if ( (!strcmp(pcsz, "false"))
974 || (!strcmp(pcsz, "no"))
975 || (!strcmp(pcsz, "0"))
976 )
977 {
978 f = false;
979 return true;
980 }
981 }
982
983 return false;
984}
985
986/**
987 * Creates a new child element node and appends it to the list
988 * of children in "this".
989 *
990 * @param pcszElementName
991 * @return
992 */
993ElementNode* ElementNode::createChild(const char *pcszElementName)
994{
995 // we must be an element, not an attribute
996 if (!m_plibNode)
997 throw ENodeIsNotElement(RT_SRC_POS);
998
999 // libxml side: create new node
1000 xmlNode *plibNode;
1001 if (!(plibNode = xmlNewNode(NULL, // namespace
1002 (const xmlChar*)pcszElementName)))
1003 throw std::bad_alloc();
1004 xmlAddChild(m_plibNode, plibNode);
1005
1006 // now wrap this in C++
1007 ElementNode *p = new ElementNode(m_pelmRoot, this, plibNode);
1008 boost::shared_ptr<ElementNode> pNew(p);
1009 m->children.push_back(pNew);
1010
1011 return p;
1012}
1013
1014
1015/**
1016 * Creates a content node and appends it to the list of children
1017 * in "this".
1018 *
1019 * @param pcszContent
1020 * @return
1021 */
1022ContentNode* ElementNode::addContent(const char *pcszContent)
1023{
1024 // libxml side: create new node
1025 xmlNode *plibNode;
1026 if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
1027 throw std::bad_alloc();
1028 xmlAddChild(m_plibNode, plibNode);
1029
1030 // now wrap this in C++
1031 ContentNode *p = new ContentNode(this, plibNode);
1032 boost::shared_ptr<ContentNode> pNew(p);
1033 m->children.push_back(pNew);
1034
1035 return p;
1036}
1037
1038/**
1039 * Sets the given attribute; overloaded version for const char *.
1040 *
1041 * If an attribute with the given name exists, it is overwritten,
1042 * otherwise a new attribute is created. Returns the attribute node
1043 * that was either created or changed.
1044 *
1045 * @param pcszName
1046 * @param pcszValue
1047 * @return
1048 */
1049AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
1050{
1051 AttributeNode *pattrReturn;
1052 Data::AttributesMap::const_iterator it;
1053
1054 it = m->attribs.find(pcszName);
1055 if (it == m->attribs.end())
1056 {
1057 // libxml side: xmlNewProp creates an attribute
1058 xmlAttr *plibAttr = xmlNewProp(m_plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
1059
1060 // C++ side: create an attribute node around it
1061 const char *pcszKey;
1062 boost::shared_ptr<AttributeNode> pNew(new AttributeNode(*m_pelmRoot, this, plibAttr, &pcszKey));
1063 // store
1064 m->attribs[pcszKey] = pNew;
1065 pattrReturn = pNew.get();
1066 }
1067 else
1068 {
1069 // overwrite existing libxml attribute node
1070 xmlAttrPtr plibAttr = xmlSetProp(m_plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
1071
1072 // and fix our existing C++ side around it
1073 boost::shared_ptr<AttributeNode> pattr = it->second;
1074 pattr->m_plibAttr = plibAttr; // in case the xmlAttrPtr is different, I'm not sure
1075
1076 pattrReturn = pattr.get();
1077 }
1078
1079 return pattrReturn;
1080
1081}
1082
1083/**
1084 * Like setAttribute (ministring variant), but replaces all backslashes with forward slashes
1085 * before calling that one.
1086 * @param pcszName
1087 * @param strValue
1088 * @return
1089 */
1090AttributeNode* ElementNode::setAttributePath(const char *pcszName, const RTCString &strValue)
1091{
1092 RTCString strTemp(strValue);
1093 strTemp.findReplace('\\', '/');
1094 return setAttribute(pcszName, strTemp.c_str());
1095}
1096
1097/**
1098 * Sets the given attribute; overloaded version for int32_t.
1099 *
1100 * If an attribute with the given name exists, it is overwritten,
1101 * otherwise a new attribute is created. Returns the attribute node
1102 * that was either created or changed.
1103 *
1104 * @param pcszName
1105 * @param i
1106 * @return
1107 */
1108AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
1109{
1110 char szValue[12]; // negative sign + 10 digits + \0
1111 RTStrPrintf(szValue, sizeof(szValue), "%RI32", i);
1112 AttributeNode *p = setAttribute(pcszName, szValue);
1113 return p;
1114}
1115
1116/**
1117 * Sets the given attribute; overloaded version for uint32_t.
1118 *
1119 * If an attribute with the given name exists, it is overwritten,
1120 * otherwise a new attribute is created. Returns the attribute node
1121 * that was either created or changed.
1122 *
1123 * @param pcszName
1124 * @param u
1125 * @return
1126 */
1127AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t u)
1128{
1129 char szValue[11]; // 10 digits + \0
1130 RTStrPrintf(szValue, sizeof(szValue), "%RU32", u);
1131 AttributeNode *p = setAttribute(pcszName, szValue);
1132 return p;
1133}
1134
1135/**
1136 * Sets the given attribute; overloaded version for int64_t.
1137 *
1138 * If an attribute with the given name exists, it is overwritten,
1139 * otherwise a new attribute is created. Returns the attribute node
1140 * that was either created or changed.
1141 *
1142 * @param pcszName
1143 * @param i
1144 * @return
1145 */
1146AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
1147{
1148 char szValue[21]; // negative sign + 19 digits + \0
1149 RTStrPrintf(szValue, sizeof(szValue), "%RI64", i);
1150 AttributeNode *p = setAttribute(pcszName, szValue);
1151 return p;
1152}
1153
1154/**
1155 * Sets the given attribute; overloaded version for uint64_t.
1156 *
1157 * If an attribute with the given name exists, it is overwritten,
1158 * otherwise a new attribute is created. Returns the attribute node
1159 * that was either created or changed.
1160 *
1161 * @param pcszName
1162 * @param u
1163 * @return
1164 */
1165AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t u)
1166{
1167 char szValue[21]; // 20 digits + \0
1168 RTStrPrintf(szValue, sizeof(szValue), "%RU64", u);
1169 AttributeNode *p = setAttribute(pcszName, szValue);
1170 return p;
1171}
1172
1173/**
1174 * Sets the given attribute to the given uint32_t, outputs a hexadecimal string.
1175 *
1176 * If an attribute with the given name exists, it is overwritten,
1177 * otherwise a new attribute is created. Returns the attribute node
1178 * that was either created or changed.
1179 *
1180 * @param pcszName
1181 * @param u
1182 * @return
1183 */
1184AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t u)
1185{
1186 char szValue[11]; // "0x" + 8 digits + \0
1187 RTStrPrintf(szValue, sizeof(szValue), "0x%RX32", u);
1188 AttributeNode *p = setAttribute(pcszName, szValue);
1189 return p;
1190}
1191
1192/**
1193 * Sets the given attribute; overloaded version for bool.
1194 *
1195 * If an attribute with the given name exists, it is overwritten,
1196 * otherwise a new attribute is created. Returns the attribute node
1197 * that was either created or changed.
1198 *
1199 * @param pcszName
1200 * @param i
1201 * @return
1202 */
1203AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
1204{
1205 return setAttribute(pcszName, (f) ? "true" : "false");
1206}
1207
1208/**
1209 * Private constructor for a new attribute node. This one is special:
1210 * in ppcszKey, it returns a pointer to a string buffer that should be
1211 * used to index the attribute correctly with namespaces.
1212 *
1213 * @param pParent
1214 * @param elmRoot
1215 * @param plibAttr
1216 * @param ppcszKey
1217 */
1218AttributeNode::AttributeNode(const ElementNode &elmRoot,
1219 Node *pParent,
1220 xmlAttr *plibAttr,
1221 const char **ppcszKey)
1222 : Node(IsAttribute,
1223 pParent,
1224 NULL,
1225 plibAttr)
1226{
1227 m_pcszName = (const char*)plibAttr->name;
1228
1229 *ppcszKey = m_pcszName;
1230
1231 if ( plibAttr->ns
1232 && plibAttr->ns->prefix
1233 )
1234 {
1235 m_pcszNamespacePrefix = (const char*)plibAttr->ns->prefix;
1236 m_pcszNamespaceHref = (const char*)plibAttr->ns->href;
1237
1238 if ( !elmRoot.m_pcszNamespaceHref
1239 || (strcmp(m_pcszNamespaceHref, elmRoot.m_pcszNamespaceHref))
1240 )
1241 {
1242 // not default namespace:
1243 m_strKey = m_pcszNamespacePrefix;
1244 m_strKey.append(':');
1245 m_strKey.append(m_pcszName);
1246
1247 *ppcszKey = m_strKey.c_str();
1248 }
1249 }
1250}
1251
1252ContentNode::ContentNode(Node *pParent, xmlNode *plibNode)
1253 : Node(IsContent,
1254 pParent,
1255 plibNode,
1256 NULL)
1257{
1258}
1259
1260/*
1261 * NodesLoop
1262 *
1263 */
1264
1265struct NodesLoop::Data
1266{
1267 ElementNodesList listElements;
1268 ElementNodesList::const_iterator it;
1269};
1270
1271NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
1272{
1273 m = new Data;
1274 node.getChildElements(m->listElements, pcszMatch);
1275 m->it = m->listElements.begin();
1276}
1277
1278NodesLoop::~NodesLoop()
1279{
1280 delete m;
1281}
1282
1283
1284/**
1285 * Handy convenience helper for looping over all child elements. Create an
1286 * instance of NodesLoop on the stack and call this method until it returns
1287 * NULL, like this:
1288 * <code>
1289 * xml::ElementNode node; // should point to an element
1290 * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
1291 * const xml::ElementNode *pChild = NULL;
1292 * while (pChild = loop.forAllNodes())
1293 * ...;
1294 * </code>
1295 * @return
1296 */
1297const ElementNode* NodesLoop::forAllNodes() const
1298{
1299 const ElementNode *pNode = NULL;
1300
1301 if (m->it != m->listElements.end())
1302 {
1303 pNode = *(m->it);
1304 ++(m->it);
1305 }
1306
1307 return pNode;
1308}
1309
1310////////////////////////////////////////////////////////////////////////////////
1311//
1312// Document class
1313//
1314////////////////////////////////////////////////////////////////////////////////
1315
1316struct Document::Data
1317{
1318 xmlDocPtr plibDocument;
1319 ElementNode *pRootElement;
1320 ElementNode *pComment;
1321
1322 Data()
1323 {
1324 plibDocument = NULL;
1325 pRootElement = NULL;
1326 pComment = NULL;
1327 }
1328
1329 ~Data()
1330 {
1331 reset();
1332 }
1333
1334 void reset()
1335 {
1336 if (plibDocument)
1337 {
1338 xmlFreeDoc(plibDocument);
1339 plibDocument = NULL;
1340 }
1341 if (pRootElement)
1342 {
1343 delete pRootElement;
1344 pRootElement = NULL;
1345 }
1346 if (pComment)
1347 {
1348 delete pComment;
1349 pComment = NULL;
1350 }
1351 }
1352
1353 void copyFrom(const Document::Data *p)
1354 {
1355 if (p->plibDocument)
1356 {
1357 plibDocument = xmlCopyDoc(p->plibDocument,
1358 1); // recursive == copy all
1359 }
1360 }
1361};
1362
1363Document::Document()
1364 : m(new Data)
1365{
1366}
1367
1368Document::Document(const Document &x)
1369 : m(new Data)
1370{
1371 m->copyFrom(x.m);
1372}
1373
1374Document& Document::operator=(const Document &x)
1375{
1376 m->reset();
1377 m->copyFrom(x.m);
1378 return *this;
1379}
1380
1381Document::~Document()
1382{
1383 delete m;
1384}
1385
1386/**
1387 * private method to refresh all internal structures after the internal pDocument
1388 * has changed. Called from XmlFileParser::read(). m->reset() must have been
1389 * called before to make sure all members except the internal pDocument are clean.
1390 */
1391void Document::refreshInternals() // private
1392{
1393 m->pRootElement = new ElementNode(NULL, NULL, xmlDocGetRootElement(m->plibDocument));
1394
1395 m->pRootElement->buildChildren(*m->pRootElement);
1396}
1397
1398/**
1399 * Returns the root element of the document, or NULL if the document is empty.
1400 * Const variant.
1401 * @return
1402 */
1403const ElementNode* Document::getRootElement() const
1404{
1405 return m->pRootElement;
1406}
1407
1408/**
1409 * Returns the root element of the document, or NULL if the document is empty.
1410 * Non-const variant.
1411 * @return
1412 */
1413ElementNode* Document::getRootElement()
1414{
1415 return m->pRootElement;
1416}
1417
1418/**
1419 * Creates a new element node and sets it as the root element. This will
1420 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
1421 */
1422ElementNode* Document::createRootElement(const char *pcszRootElementName,
1423 const char *pcszComment /* = NULL */)
1424{
1425 if (m->pRootElement || m->plibDocument)
1426 throw EDocumentNotEmpty(RT_SRC_POS);
1427
1428 // libxml side: create document, create root node
1429 m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
1430 xmlNode *plibRootNode;
1431 if (!(plibRootNode = xmlNewNode(NULL, // namespace
1432 (const xmlChar*)pcszRootElementName)))
1433 throw std::bad_alloc();
1434 xmlDocSetRootElement(m->plibDocument, plibRootNode);
1435 // now wrap this in C++
1436 m->pRootElement = new ElementNode(NULL, NULL, plibRootNode);
1437
1438 // add document global comment if specified
1439 if (pcszComment != NULL)
1440 {
1441 xmlNode *pComment;
1442 if (!(pComment = xmlNewDocComment(m->plibDocument,
1443 (const xmlChar *)pcszComment)))
1444 throw std::bad_alloc();
1445 xmlAddPrevSibling(plibRootNode, pComment);
1446 // now wrap this in C++
1447 m->pComment = new ElementNode(NULL, NULL, pComment);
1448 }
1449
1450 return m->pRootElement;
1451}
1452
1453////////////////////////////////////////////////////////////////////////////////
1454//
1455// XmlParserBase class
1456//
1457////////////////////////////////////////////////////////////////////////////////
1458
1459XmlParserBase::XmlParserBase()
1460{
1461 m_ctxt = xmlNewParserCtxt();
1462 if (m_ctxt == NULL)
1463 throw std::bad_alloc();
1464}
1465
1466XmlParserBase::~XmlParserBase()
1467{
1468 xmlFreeParserCtxt (m_ctxt);
1469 m_ctxt = NULL;
1470}
1471
1472////////////////////////////////////////////////////////////////////////////////
1473//
1474// XmlMemParser class
1475//
1476////////////////////////////////////////////////////////////////////////////////
1477
1478XmlMemParser::XmlMemParser()
1479 : XmlParserBase()
1480{
1481}
1482
1483XmlMemParser::~XmlMemParser()
1484{
1485}
1486
1487/**
1488 * Parse the given buffer and fills the given Document object with its contents.
1489 * Throws XmlError on parsing errors.
1490 *
1491 * The document that is passed in will be reset before being filled if not empty.
1492 *
1493 * @param pvBuf in: memory buffer to parse.
1494 * @param cbSize in: size of the memory buffer.
1495 * @param strFilename in: name fo file to parse.
1496 * @param doc out: document to be reset and filled with data according to file contents.
1497 */
1498void XmlMemParser::read(const void* pvBuf, size_t cbSize,
1499 const RTCString &strFilename,
1500 Document &doc)
1501{
1502 GlobalLock lock;
1503// global.setExternalEntityLoader(ExternalEntityLoader);
1504
1505 const char *pcszFilename = strFilename.c_str();
1506
1507 doc.m->reset();
1508 if (!(doc.m->plibDocument = xmlCtxtReadMemory(m_ctxt,
1509 (const char*)pvBuf,
1510 (int)cbSize,
1511 pcszFilename,
1512 NULL, // encoding = auto
1513 XML_PARSE_NOBLANKS | XML_PARSE_NONET)))
1514 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1515
1516 doc.refreshInternals();
1517}
1518
1519////////////////////////////////////////////////////////////////////////////////
1520//
1521// XmlMemWriter class
1522//
1523////////////////////////////////////////////////////////////////////////////////
1524
1525XmlMemWriter::XmlMemWriter()
1526 : m_pBuf(0)
1527{
1528}
1529
1530XmlMemWriter::~XmlMemWriter()
1531{
1532 if (m_pBuf)
1533 xmlFree(m_pBuf);
1534}
1535
1536void XmlMemWriter::write(const Document &doc, void **ppvBuf, size_t *pcbSize)
1537{
1538 if (m_pBuf)
1539 {
1540 xmlFree(m_pBuf);
1541 m_pBuf = 0;
1542 }
1543 int size;
1544 xmlDocDumpFormatMemory(doc.m->plibDocument, (xmlChar**)&m_pBuf, &size, 1);
1545 *ppvBuf = m_pBuf;
1546 *pcbSize = size;
1547}
1548
1549////////////////////////////////////////////////////////////////////////////////
1550//
1551// XmlFileParser class
1552//
1553////////////////////////////////////////////////////////////////////////////////
1554
1555struct XmlFileParser::Data
1556{
1557 RTCString strXmlFilename;
1558
1559 Data()
1560 {
1561 }
1562
1563 ~Data()
1564 {
1565 }
1566};
1567
1568XmlFileParser::XmlFileParser()
1569 : XmlParserBase(),
1570 m(new Data())
1571{
1572}
1573
1574XmlFileParser::~XmlFileParser()
1575{
1576 delete m;
1577 m = NULL;
1578}
1579
1580struct IOContext
1581{
1582 File file;
1583 RTCString error;
1584
1585 IOContext(const char *pcszFilename, File::Mode mode, bool fFlush = false)
1586 : file(mode, pcszFilename, fFlush)
1587 {
1588 }
1589
1590 void setError(const RTCError &x)
1591 {
1592 error = x.what();
1593 }
1594
1595 void setError(const std::exception &x)
1596 {
1597 error = x.what();
1598 }
1599};
1600
1601struct ReadContext : IOContext
1602{
1603 ReadContext(const char *pcszFilename)
1604 : IOContext(pcszFilename, File::Mode_Read)
1605 {
1606 }
1607};
1608
1609struct WriteContext : IOContext
1610{
1611 WriteContext(const char *pcszFilename, bool fFlush)
1612 : IOContext(pcszFilename, File::Mode_Overwrite, fFlush)
1613 {
1614 }
1615};
1616
1617/**
1618 * Reads the given file and fills the given Document object with its contents.
1619 * Throws XmlError on parsing errors.
1620 *
1621 * The document that is passed in will be reset before being filled if not empty.
1622 *
1623 * @param strFilename in: name fo file to parse.
1624 * @param doc out: document to be reset and filled with data according to file contents.
1625 */
1626void XmlFileParser::read(const RTCString &strFilename,
1627 Document &doc)
1628{
1629 GlobalLock lock;
1630// global.setExternalEntityLoader(ExternalEntityLoader);
1631
1632 m->strXmlFilename = strFilename;
1633 const char *pcszFilename = strFilename.c_str();
1634
1635 ReadContext context(pcszFilename);
1636 doc.m->reset();
1637 if (!(doc.m->plibDocument = xmlCtxtReadIO(m_ctxt,
1638 ReadCallback,
1639 CloseCallback,
1640 &context,
1641 pcszFilename,
1642 NULL, // encoding = auto
1643 XML_PARSE_NOBLANKS | XML_PARSE_NONET)))
1644 throw XmlError(xmlCtxtGetLastError(m_ctxt));
1645
1646 doc.refreshInternals();
1647}
1648
1649// static
1650int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
1651{
1652 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
1653
1654 /* To prevent throwing exceptions while inside libxml2 code, we catch
1655 * them and forward to our level using a couple of variables. */
1656
1657 try
1658 {
1659 return pContext->file.read(aBuf, aLen);
1660 }
1661 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1662 catch (const RTCError &err) { pContext->setError(err); }
1663 catch (const std::exception &err) { pContext->setError(err); }
1664 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1665
1666 return -1 /* failure */;
1667}
1668
1669int XmlFileParser::CloseCallback(void *aCtxt)
1670{
1671 /// @todo to be written
1672 NOREF(aCtxt);
1673
1674 return -1;
1675}
1676
1677////////////////////////////////////////////////////////////////////////////////
1678//
1679// XmlFileWriter class
1680//
1681////////////////////////////////////////////////////////////////////////////////
1682
1683struct XmlFileWriter::Data
1684{
1685 Document *pDoc;
1686};
1687
1688XmlFileWriter::XmlFileWriter(Document &doc)
1689{
1690 m = new Data();
1691 m->pDoc = &doc;
1692}
1693
1694XmlFileWriter::~XmlFileWriter()
1695{
1696 delete m;
1697}
1698
1699void XmlFileWriter::writeInternal(const char *pcszFilename, bool fSafe)
1700{
1701 WriteContext context(pcszFilename, fSafe);
1702
1703 GlobalLock lock;
1704
1705 /* serialize to the stream */
1706 xmlIndentTreeOutput = 1;
1707 xmlTreeIndentString = " ";
1708 xmlSaveNoEmptyTags = 0;
1709
1710 xmlSaveCtxtPtr saveCtxt;
1711 if (!(saveCtxt = xmlSaveToIO(WriteCallback,
1712 CloseCallback,
1713 &context,
1714 NULL,
1715 XML_SAVE_FORMAT)))
1716 throw xml::LogicError(RT_SRC_POS);
1717
1718 long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
1719 if (rc == -1)
1720 {
1721 /* look if there was a forwarded exception from the lower level */
1722// if (m->trappedErr.get() != NULL)
1723// m->trappedErr->rethrow();
1724
1725 /* there must be an exception from the Output implementation,
1726 * otherwise the save operation must always succeed. */
1727 throw xml::LogicError(RT_SRC_POS);
1728 }
1729
1730 xmlSaveClose(saveCtxt);
1731}
1732
1733void XmlFileWriter::write(const char *pcszFilename, bool fSafe)
1734{
1735 if (!fSafe)
1736 writeInternal(pcszFilename, fSafe);
1737 else
1738 {
1739 /* Empty string and directory spec must be avoid. */
1740 if (RTPathFilename(pcszFilename) == NULL)
1741 throw xml::LogicError(RT_SRC_POS);
1742
1743 /* Construct both filenames first to ease error handling. */
1744 char szTmpFilename[RTPATH_MAX];
1745 int rc = RTStrCopy(szTmpFilename, sizeof(szTmpFilename) - strlen(s_pszTmpSuff), pcszFilename);
1746 if (RT_FAILURE(rc))
1747 throw EIPRTFailure(rc, "RTStrCopy");
1748 strcat(szTmpFilename, s_pszTmpSuff);
1749
1750 char szPrevFilename[RTPATH_MAX];
1751 rc = RTStrCopy(szPrevFilename, sizeof(szPrevFilename) - strlen(s_pszPrevSuff), pcszFilename);
1752 if (RT_FAILURE(rc))
1753 throw EIPRTFailure(rc, "RTStrCopy");
1754 strcat(szPrevFilename, s_pszPrevSuff);
1755
1756 /* Write the XML document to the temporary file. */
1757 writeInternal(szTmpFilename, fSafe);
1758
1759 /* Make a backup of any existing file (ignore failure). */
1760 uint64_t cbPrevFile;
1761 rc = RTFileQuerySize(pcszFilename, &cbPrevFile);
1762 if (RT_SUCCESS(rc) && cbPrevFile >= 16)
1763 RTFileRename(pcszFilename, szPrevFilename, RTPATHRENAME_FLAGS_REPLACE);
1764
1765 /* Commit the temporary file. Just leave the tmp file behind on failure. */
1766 rc = RTFileRename(szTmpFilename, pcszFilename, RTPATHRENAME_FLAGS_REPLACE);
1767 if (RT_FAILURE(rc))
1768 throw EIPRTFailure(rc, "Failed to replace '%s' with '%s'", pcszFilename, szTmpFilename);
1769
1770 /* Flush the directory changes (required on linux at least). */
1771 RTPathStripFilename(szTmpFilename);
1772 rc = RTDirFlush(szTmpFilename);
1773 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
1774 }
1775}
1776
1777int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
1778{
1779 WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
1780
1781 /* To prevent throwing exceptions while inside libxml2 code, we catch
1782 * them and forward to our level using a couple of variables. */
1783 try
1784 {
1785 return pContext->file.write(aBuf, aLen);
1786 }
1787 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
1788 catch (const RTCError &err) { pContext->setError(err); }
1789 catch (const std::exception &err) { pContext->setError(err); }
1790 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
1791
1792 return -1 /* failure */;
1793}
1794
1795int XmlFileWriter::CloseCallback(void *aCtxt)
1796{
1797 /// @todo to be written
1798 NOREF(aCtxt);
1799
1800 return -1;
1801}
1802
1803/*static*/ const char * const XmlFileWriter::s_pszTmpSuff = "-tmp";
1804/*static*/ const char * const XmlFileWriter::s_pszPrevSuff = "-prev";
1805
1806
1807} // end namespace xml
1808
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