VirtualBox

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

Last change on this file since 33464 was 33464, checked in by vboxsync, 15 years ago

*: Fixes for incorrect RTStrAPrintf usage (it does NOT return an IPRT status code) and the odd bugs nearby.

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

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